salak-toml 0.3.4 → 0.3.4.1
raw patch · 7 files changed
+409/−367 lines, 7 filesdep +criteriondep ~basedep ~salak
Dependencies added: criterion
Dependency ranges changed: base, salak
Files
- bench/SalakTomlBench.hs +14/−0
- salak-toml.cabal +32/−10
- salak.toml +261/−0
- src/Salak/Toml.hs +28/−22
- test/SalakTomlSpec.hs +74/−0
- test/Spec.hs +0/−74
- test/salak.toml +0/−261
+ bench/SalakTomlBench.hs view
@@ -0,0 +1,14 @@+module SalakTomlBench where++import Criterion.Main+import Salak+import Salak.Toml+++main = defaultMain+ [ bgroup "load"+ [ bench "loadYaml" $ whnfIO $ loadAndRunSalak (loadToml "salak.toml") action+ ]+ ]++action = return ()
salak-toml.cabal view
@@ -1,6 +1,6 @@ cabal-version: 1.12 name: salak-toml-version: 0.3.4+version: 0.3.4.1 license: MIT license-file: LICENSE copyright: 2019 Daniel YU@@ -12,7 +12,7 @@ build-type: Simple extra-source-files: README.md- test/salak.toml+ salak.toml library exposed-modules:@@ -22,36 +22,58 @@ Paths_salak_toml default-language: Haskell2010 default-extensions: RecordWildCards TupleSections OverloadedStrings- ScopedTypeVariables FlexibleInstances MultiParamTypeClasses- DeriveGeneric+ NoOverloadedLists ScopedTypeVariables FlexibleInstances+ MultiParamTypeClasses DeriveGeneric FlexibleContexts RankNTypes ghc-options: -Wall -fno-warn-orphans -fno-warn-missing-signatures build-depends: base >=4.10 && <5,- salak >=0.3.4 && <0.4,+ salak >=0.3.4.1 && <0.4, text >=1.2.3.1 && <1.3, time >=1.8.0.2 && <1.9, tomland >=1.0 && <1.2, unordered-containers >=0.2.10.0 && <0.3 -test-suite spec+test-suite salak-toml-spec type: exitcode-stdio-1.0- main-is: Spec.hs+ main-is: SalakTomlSpec.hs hs-source-dirs: test src other-modules: Salak.Toml Paths_salak_toml default-language: Haskell2010 default-extensions: RecordWildCards TupleSections OverloadedStrings- ScopedTypeVariables FlexibleInstances MultiParamTypeClasses- DeriveGeneric+ NoOverloadedLists ScopedTypeVariables FlexibleInstances+ MultiParamTypeClasses DeriveGeneric FlexibleContexts RankNTypes ghc-options: -Wall -fno-warn-orphans -fno-warn-missing-signatures+ -main-is SalakTomlSpec.main build-depends: QuickCheck >=2.13.2 && <2.14, base >=4.10 && <5, exceptions >=0.10.2 && <0.11, hspec ==2.*, mtl >=2.2.2 && <2.3,- salak >=0.3.4 && <0.4,+ salak >=0.3.4.1 && <0.4,+ text >=1.2.3.1 && <1.3,+ time >=1.8.0.2 && <1.9,+ tomland >=1.0 && <1.2,+ unordered-containers >=0.2.10.0 && <0.3++benchmark salak-toml-bench+ type: exitcode-stdio-1.0+ main-is: SalakTomlBench.hs+ hs-source-dirs: bench src+ other-modules:+ Salak.Toml+ default-language: Haskell2010+ default-extensions: RecordWildCards TupleSections OverloadedStrings+ NoOverloadedLists ScopedTypeVariables FlexibleInstances+ MultiParamTypeClasses DeriveGeneric FlexibleContexts RankNTypes+ ghc-options: -Wall -fno-warn-orphans -fno-warn-missing-signatures+ -main-is SalakTomlBench.main -rtsopts -threaded -with-rtsopts=-K1K+ build-depends:+ base >=4.10 && <5,+ criterion >=1.5.5.0 && <1.6,+ salak >=0.3.4.1 && <0.4, text >=1.2.3.1 && <1.3, time >=1.8.0.2 && <1.9, tomland >=1.0 && <1.2,
+ salak.toml view
@@ -0,0 +1,261 @@+server.port = 8080+server.codes = [ 5, 10, 42 ]+server.description = """+This is production server.+Don't touch it!+"""++[mail]+ host = "smtp.gmail.com"+ send-if-inactive = false++[me.icymint.conf]+name="shelly"+age=16+male="no"+det.hello="def"++################################################################################+## Comment++# Speak your mind with the hash symbol. They go from the symbol to the end of+# the line.+++################################################################################+## Table++# Tables (also known as hash tables or dictionaries) are collections of+# key/value pairs. They appear in square brackets on a line by themselves.++[table]++key = "value" # Yeah, you can do this.++# Nested tables are denoted by table names with dots in them. Name your tables+# whatever crap you please, just don't use #, ., [ or ].++[table.subtable]++key = "another value"++# You don't need to specify all the super-tables if you don't want to. TOML+# knows how to do it for you.++# [x] you+# [x.y] don't+# [x.y.z] need these+[x.y.z.w] # for this to work+++################################################################################+## Inline Table++# Inline tables provide a more compact syntax for expressing tables. They are+# especially useful for grouped data that can otherwise quickly become verbose.+# Inline tables are enclosed in curly braces `{` and `}`. No newlines are+# allowed between the curly braces unless they are valid within a value.++[table.inline]++name = { first = "Tom", last = "Preston-Werner" }+point = { x = 1, y = 2 }+++################################################################################+## String++# There are four ways to express strings: basic, multi-line basic, literal, and+# multi-line literal. All strings must contain only valid UTF-8 characters.++[string.basic]++basic = "I'm a string. \"You can quote me\". Name\tJos\u00E9\nLocation\tSF."++[string.multiline]++# The following strings are byte-for-byte equivalent:+key1 = "One\nTwo"+key2 = """One\nTwo"""+key3 = """+One+Two"""++[string.multiline.continued]++# The following strings are byte-for-byte equivalent:+key1 = "The quick brown fox jumps over the lazy dog."++key2 = """+The quick brown \+++ fox jumps over \+ the lazy dog."""++key3 = """\+ The quick brown \+ fox jumps over \+ the lazy dog.\+ """++[string.literal]++# What you see is what you get.+winpath = 'C:\\Users\\nodejs\\templates'+winpath2 = '\\\\ServerX\\admin\$\\system32\\'+quoted = 'Tom "Dubs" Preston-Werner'+regex = '<\\i\\c*\\s*>'+++[string.literal.multiline]++regex2 = '''I [dw]on't need \\d\{2\} apples'''+lines = '''+The first newline is+trimmed in raw strings.+ All other whitespace+ is preserved.+'''+++################################################################################+## Integer++# Integers are whole numbers. Positive numbers may be prefixed with a plus sign.+# Negative numbers are prefixed with a minus sign.++[integer]++key1 = +99+key2 = 42+key3 = 0+key4 = -17++[integer.underscores]++# For large numbers, you may use underscores to enhance readability. Each+# underscore must be surrounded by at least one digit.+key1 = 1_000+key2 = 5_349_221+key3 = 1_2_3_4_5 # valid but inadvisable+++################################################################################+## Float++# A float consists of an integer part (which may be prefixed with a plus or+# minus sign) followed by a fractional part and/or an exponent part.++[float.fractional]++key1 = +1.0+key2 = 3.1415+key3 = -0.01++[float.exponent]++key1 = 5e+22+key2 = 1e6+key3 = -2E-2++[float.both]++key = 6.626e-34++[float.underscores]++key1 = 9_224_617.445_991_228_313+key2 = 1e1_000+++################################################################################+## Boolean++# Booleans are just the tokens you're used to. Always lowercase.++[boolean]++True = true+False = false+++################################################################################+## Datetime++# Datetimes are RFC 3339 dates.++[datetime]++key1 = 1979-05-27T07:32:00Z+key2 = 1979-05-27T00:32:00-07:00+key3 = 1979-05-27T00:32:00.999999-07:00+++################################################################################+## Array++# Arrays are square brackets with other primitives inside. Whitespace is+# ignored. Elements are separated by commas. Data types may not be mixed.++[array]++key1 = [ 1, 2, 3 ]+key2 = [ "red", "yellow", "green" ]+key3 = [ [ 1, 2 ], [3, 4, 5] ]+key4 = [ [ 1, 2 ], ["a", "b", "c"] ] # this is ok++# Arrays can also be multiline. So in addition to ignoring whitespace, arrays+# also ignore newlines between the brackets. Terminating commas are ok before+# the closing bracket.++key5 = [+ 1, 2, 3+]+key6 = [+ 1,+ 2, # this is ok+]+++################################################################################+## Array of Tables++# These can be expressed by using a table name in double brackets. Each table+# with the same double bracketed name will be an element in the array. The+# tables are inserted in the order encountered.++[[products]]++name = "Hammer"+sku = 738594937++[[products]]++[[products]]++name = "Nail"+sku = 284758393+color = "gray"+++# You can create nested arrays of tables as well.++[[fruit]]+ name = "apple"++ [fruit.physical]+ color = "red"+ shape = "round"++ [[fruit.variety]]+ name = "red delicious"++ [[fruit.variety]]+ name = "granny smith"++[[fruit]]+ name = "banana"++ [[fruit.variety]]+ name = "plantain"
src/Salak/Toml.hs view
@@ -6,7 +6,7 @@ -- | -- Module: Salak.Toml -- Copyright: (c) 2019 Daniel YU--- License: BSD3+-- License: MIT -- Maintainer: leptonyu@gmail.com -- Stability: experimental -- Portability: portable@@ -21,6 +21,7 @@ import Control.Exception (Exception, throwIO) import qualified Data.HashMap.Strict as HM+import Data.List (foldl') import qualified Data.List.NonEmpty as N import Data.Text (Text) import qualified Data.Text.IO as IO@@ -41,9 +42,11 @@ instance HasLoad TOML where loaders _ = (, loadToml) <$> ["toml", "tml"] +{-# INLINE toSs #-} toSs :: T.Key -> Keys toSs (T.Key ps) = fromKeys $ toS <$> N.toList ps +{-# INLINE toS #-} toS :: Piece -> Key toS = KT . unPiece @@ -53,28 +56,31 @@ . foldTables tomlTables . foldTableArrays tomlTableArrays where- foldToml go p t = HM.foldlWithKey' go t p- foldPairs = foldToml (\s k v -> TR.modify' (toSs k) (insertAnyValue i v) s)- foldTableArrays = foldToml (\s _ v -> foldArray (N.toList v) (loadTOML i) s)+ {-# INLINE foldToml #-}+ foldToml = flip . HM.foldlWithKey'+ {-# INLINE foldPairs #-}+ foldPairs = foldToml (\s k v -> TR.modify' (insertAnyValue v) (toSs k) s)+ {-# INLINE foldTableArrays #-}+ foldTableArrays = foldToml (\s _ v -> foldArray (loadTOML i) (N.toList v) s)+ {-# INLINE foldTables #-} foldTables = foldToml (\s _ v -> go v s)- where- go (Leaf k toml) = TR.modify' (toSs k) (loadTOML i toml)- go (Branch k v tomap) = TR.modify' (toSs k) (foldTables tomap) . maybe id (loadTOML i) v-insertAnyValue :: Int -> AnyValue -> TraceSource -> TraceSource-insertAnyValue i (AnyValue (Array b)) ts = foldArray b (insertAnyValue i . AnyValue) ts-insertAnyValue i (AnyValue (Bool b)) ts = setVal i (VB b) ts-insertAnyValue i (AnyValue (Integer b)) ts = setVal i (VI $ fromIntegral b) ts-insertAnyValue i (AnyValue (Double b)) ts = setVal i (VI $ realToFrac b) ts-insertAnyValue i (AnyValue (Text b)) ts = setVal i (VT b) ts-insertAnyValue i (AnyValue (Local b)) ts = setVal i (VLT b) ts-insertAnyValue i (AnyValue (Day b)) ts = setVal i (VD b) ts-insertAnyValue i (AnyValue (Hours b)) ts = setVal i (VH b) ts-insertAnyValue i (AnyValue (Zoned (ZonedTime a b))) ts = setVal i (VZT b a) ts--foldArray :: [a] -> (a -> TraceSource -> TraceSource) -> TraceSource -> TraceSource-foldArray as f ts = foldl go ts $ zip [0..] as- where- go t (i, a) = TR.modify (KI i) (f a) t+ {-# INLINE go #-}+ go (Leaf k toml) = TR.modify' (loadTOML i toml) (toSs k)+ go (Branch k v tomap) = TR.modify' (foldTables tomap) (toSs k) . maybe id (loadTOML i) v+ {-# INLINE insertAnyValue #-}+ insertAnyValue :: AnyValue -> TraceSource -> TraceSource+ insertAnyValue (AnyValue (Array b)) = foldArray (insertAnyValue . AnyValue) b+ insertAnyValue (AnyValue (Bool b)) = setVal i (VB b)+ insertAnyValue (AnyValue (Integer b)) = setVal i (VI $ fromIntegral b)+ insertAnyValue (AnyValue (Double b)) = setVal i (VI $ realToFrac b)+ insertAnyValue (AnyValue (Text b)) = setVal i (VT b)+ insertAnyValue (AnyValue (Local b)) = setVal i (VLT b)+ insertAnyValue (AnyValue (Day b)) = setVal i (VD b)+ insertAnyValue (AnyValue (Hours b)) = setVal i (VH b)+ insertAnyValue (AnyValue (Zoned b)) = setVal i (VU $ zonedTimeToUTC b)+ {-# INLINE foldArray #-}+ foldArray :: (a -> TraceSource -> TraceSource) -> [a] -> TraceSource -> TraceSource+ foldArray f = flip (foldl' (\t (j,a) -> TR.modify (KI j) (f a) t)) . zip [0..] newtype TomlException = TomlException Text deriving Show
+ test/SalakTomlSpec.hs view
@@ -0,0 +1,74 @@+{-# LANGUAGE DataKinds #-}+{-# LANGUAGE DeriveGeneric #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE MultiParamTypeClasses #-}+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE RecordWildCards #-}+{-# LANGUAGE ScopedTypeVariables #-}++module SalakTomlSpec where++import Control.Monad.Reader+import Data.List (intercalate)+import Data.Text (Text, pack)+import GHC.Generics+import Salak+import Salak.Internal+import Salak.Toml+import Test.Hspec+import Test.QuickCheck++main :: IO ()+main = hspec spec++spec :: Spec+spec = do+ describe "Salak.Toml" tomlProperty++newtype SKey = SKey { unKey :: Text } deriving Show++instance Arbitrary SKey where+ arbitrary = do+ key <- choose (1,20)+ vs <- vectorOf key $ do+ k <- choose (1,20)+ v <- vectorOf k $ choose ('a','z')+ b <- choose (0,10) :: Gen Int+ if b > 0 then return v else do+ x <- choose (0,10) :: Gen Int+ return (v ++ "[" ++ show x ++ "]")+ return (SKey $ pack $ intercalate "." vs)++data Conf = Conf+ { name :: String+ , age :: Int+ , male :: Bool+ , det :: SubConf+ } deriving (Eq, Show, Generic)++data SubConf = SubConf+ { hello :: String } deriving (Eq, Show, Generic)++instance FromProp m SubConf where+ fromProp = SubConf <$> "hello" .?= "yyy"++instance FromProp m Conf++tomlProperty :: SpecWith ()+tomlProperty = do+ context "load toml" $ do+ it "salak.toml" $ do+ loadAndRunSalak (loadToml "salak.toml") $ do+ SourcePack{..} <- ask+ cf <- require "me.icymint.conf"+ lift $ do+ name cf `shouldBe` "shelly"+ age cf `shouldBe` 16+ male cf `shouldBe` False+ det cf `shouldBe` SubConf "def"+++++
− test/Spec.hs
@@ -1,74 +0,0 @@-{-# LANGUAGE DataKinds #-}-{-# LANGUAGE DeriveGeneric #-}-{-# LANGUAGE FlexibleContexts #-}-{-# LANGUAGE FlexibleInstances #-}-{-# LANGUAGE MultiParamTypeClasses #-}-{-# LANGUAGE OverloadedStrings #-}-{-# LANGUAGE RecordWildCards #-}-{-# LANGUAGE ScopedTypeVariables #-}--module Main where--import Control.Monad.Reader-import Data.List (intercalate)-import Data.Text (Text, pack)-import GHC.Generics-import Salak-import Salak.Internal-import Salak.Toml-import Test.Hspec-import Test.QuickCheck--main :: IO ()-main = hspec spec--spec :: Spec-spec = do- describe "Salak.Toml" tomlProperty--newtype SKey = SKey { unKey :: Text } deriving Show--instance Arbitrary SKey where- arbitrary = do- key <- choose (1,20)- vs <- vectorOf key $ do- k <- choose (1,20)- v <- vectorOf k $ choose ('a','z')- b <- choose (0,10) :: Gen Int- if b > 0 then return v else do- x <- choose (0,10) :: Gen Int- return (v ++ "[" ++ show x ++ "]")- return (SKey $ pack $ intercalate "." vs)--data Conf = Conf- { name :: String- , age :: Int- , male :: Bool- , det :: SubConf- } deriving (Eq, Show, Generic)--data SubConf = SubConf- { hello :: String } deriving (Eq, Show, Generic)--instance FromProp m SubConf where- fromProp = SubConf <$> "hello" .?= "yyy"--instance FromProp m Conf--tomlProperty :: SpecWith ()-tomlProperty = do- context "load toml" $ do- it "salak.toml" $ do- loadAndRunSalak (loadToml "test/salak.toml") $ do- SourcePack{..} <- ask- cf <- require "me.icymint.conf"- lift $ do- name cf `shouldBe` "shelly"- age cf `shouldBe` 16- male cf `shouldBe` False- det cf `shouldBe` SubConf "def"-----
− test/salak.toml
@@ -1,261 +0,0 @@-server.port = 8080-server.codes = [ 5, 10, 42 ]-server.description = """-This is production server.-Don't touch it!-"""--[mail]- host = "smtp.gmail.com"- send-if-inactive = false--[me.icymint.conf]-name="shelly"-age=16-male="no"-det.hello="def"--################################################################################-## Comment--# Speak your mind with the hash symbol. They go from the symbol to the end of-# the line.---################################################################################-## Table--# Tables (also known as hash tables or dictionaries) are collections of-# key/value pairs. They appear in square brackets on a line by themselves.--[table]--key = "value" # Yeah, you can do this.--# Nested tables are denoted by table names with dots in them. Name your tables-# whatever crap you please, just don't use #, ., [ or ].--[table.subtable]--key = "another value"--# You don't need to specify all the super-tables if you don't want to. TOML-# knows how to do it for you.--# [x] you-# [x.y] don't-# [x.y.z] need these-[x.y.z.w] # for this to work---################################################################################-## Inline Table--# Inline tables provide a more compact syntax for expressing tables. They are-# especially useful for grouped data that can otherwise quickly become verbose.-# Inline tables are enclosed in curly braces `{` and `}`. No newlines are-# allowed between the curly braces unless they are valid within a value.--[table.inline]--name = { first = "Tom", last = "Preston-Werner" }-point = { x = 1, y = 2 }---################################################################################-## String--# There are four ways to express strings: basic, multi-line basic, literal, and-# multi-line literal. All strings must contain only valid UTF-8 characters.--[string.basic]--basic = "I'm a string. \"You can quote me\". Name\tJos\u00E9\nLocation\tSF."--[string.multiline]--# The following strings are byte-for-byte equivalent:-key1 = "One\nTwo"-key2 = """One\nTwo"""-key3 = """-One-Two"""--[string.multiline.continued]--# The following strings are byte-for-byte equivalent:-key1 = "The quick brown fox jumps over the lazy dog."--key2 = """-The quick brown \--- fox jumps over \- the lazy dog."""--key3 = """\- The quick brown \- fox jumps over \- the lazy dog.\- """--[string.literal]--# What you see is what you get.-winpath = 'C:\\Users\\nodejs\\templates'-winpath2 = '\\\\ServerX\\admin\$\\system32\\'-quoted = 'Tom "Dubs" Preston-Werner'-regex = '<\\i\\c*\\s*>'---[string.literal.multiline]--regex2 = '''I [dw]on't need \\d\{2\} apples'''-lines = '''-The first newline is-trimmed in raw strings.- All other whitespace- is preserved.-'''---################################################################################-## Integer--# Integers are whole numbers. Positive numbers may be prefixed with a plus sign.-# Negative numbers are prefixed with a minus sign.--[integer]--key1 = +99-key2 = 42-key3 = 0-key4 = -17--[integer.underscores]--# For large numbers, you may use underscores to enhance readability. Each-# underscore must be surrounded by at least one digit.-key1 = 1_000-key2 = 5_349_221-key3 = 1_2_3_4_5 # valid but inadvisable---################################################################################-## Float--# A float consists of an integer part (which may be prefixed with a plus or-# minus sign) followed by a fractional part and/or an exponent part.--[float.fractional]--key1 = +1.0-key2 = 3.1415-key3 = -0.01--[float.exponent]--key1 = 5e+22-key2 = 1e6-key3 = -2E-2--[float.both]--key = 6.626e-34--[float.underscores]--key1 = 9_224_617.445_991_228_313-key2 = 1e1_000---################################################################################-## Boolean--# Booleans are just the tokens you're used to. Always lowercase.--[boolean]--True = true-False = false---################################################################################-## Datetime--# Datetimes are RFC 3339 dates.--[datetime]--key1 = 1979-05-27T07:32:00Z-key2 = 1979-05-27T00:32:00-07:00-key3 = 1979-05-27T00:32:00.999999-07:00---################################################################################-## Array--# Arrays are square brackets with other primitives inside. Whitespace is-# ignored. Elements are separated by commas. Data types may not be mixed.--[array]--key1 = [ 1, 2, 3 ]-key2 = [ "red", "yellow", "green" ]-key3 = [ [ 1, 2 ], [3, 4, 5] ]-key4 = [ [ 1, 2 ], ["a", "b", "c"] ] # this is ok--# Arrays can also be multiline. So in addition to ignoring whitespace, arrays-# also ignore newlines between the brackets. Terminating commas are ok before-# the closing bracket.--key5 = [- 1, 2, 3-]-key6 = [- 1,- 2, # this is ok-]---################################################################################-## Array of Tables--# These can be expressed by using a table name in double brackets. Each table-# with the same double bracketed name will be an element in the array. The-# tables are inserted in the order encountered.--[[products]]--name = "Hammer"-sku = 738594937--[[products]]--[[products]]--name = "Nail"-sku = 284758393-color = "gray"---# You can create nested arrays of tables as well.--[[fruit]]- name = "apple"-- [fruit.physical]- color = "red"- shape = "round"-- [[fruit.variety]]- name = "red delicious"-- [[fruit.variety]]- name = "granny smith"--[[fruit]]- name = "banana"-- [[fruit.variety]]- name = "plantain"