salak-toml (empty) → 0.2.7
raw patch · 7 files changed
+474/−0 lines, 7 filesdep +QuickCheckdep +basedep +hspecsetup-changed
Dependencies added: QuickCheck, base, hspec, mtl, salak, text, time, tomland, unordered-containers
Files
- LICENSE +30/−0
- README.md +1/−0
- Setup.hs +2/−0
- salak-toml.cabal +50/−0
- src/Salak/Load/Toml.hs +61/−0
- test/Spec.hs +69/−0
- test/salak.toml +261/−0
+ LICENSE view
@@ -0,0 +1,30 @@+Copyright Daniel YU (c) 2019++All rights reserved.++Redistribution and use in source and binary forms, with or without+modification, are permitted provided that the following conditions are met:++ * Redistributions of source code must retain the above copyright+ notice, this list of conditions and the following disclaimer.++ * Redistributions in binary form must reproduce the above+ copyright notice, this list of conditions and the following+ disclaimer in the documentation and/or other materials provided+ with the distribution.++ * Neither the name of Daniel YU nor the names of other+ contributors may be used to endorse or promote products derived+ from this software without specific prior written permission.++THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ README.md view
@@ -0,0 +1,1 @@+# salak-yaml
+ Setup.hs view
@@ -0,0 +1,2 @@+import Distribution.Simple+main = defaultMain
+ salak-toml.cabal view
@@ -0,0 +1,50 @@+cabal-version: 1.12+name: salak-toml+version: 0.2.7+license: BSD3+license-file: LICENSE+copyright: (c) 2018 Daniel YU+maintainer: Daniel YU <leptonyu@gmail.com>+author: Daniel YU+homepage: https://github.com/leptonyu/salak#readme+synopsis: Configuration Loader for toml+category: Library, Configuration+build-type: Simple+extra-source-files:+ README.md+ test/salak.toml++library+ exposed-modules:+ Salak.Load.Toml+ hs-source-dirs: src+ other-modules:+ Paths_salak_toml+ default-language: Haskell2010+ build-depends:+ base >=4.10 && <5,+ mtl >=2.2.2 && <2.3,+ salak ==0.2.7,+ text >=1.2.3.1 && <1.3,+ time >=1.8.0.2 && <1.9,+ tomland ==1.0.*,+ unordered-containers >=0.2.9.0 && <0.3++test-suite spec+ type: exitcode-stdio-1.0+ main-is: Spec.hs+ hs-source-dirs: test src+ other-modules:+ Salak.Load.Toml+ Paths_salak_toml+ default-language: Haskell2010+ build-depends:+ QuickCheck >=2.12.6.1 && <2.14,+ base >=4.10 && <5,+ hspec ==2.*,+ mtl >=2.2.2 && <2.3,+ salak ==0.2.7,+ text >=1.2.3.1 && <1.3,+ time >=1.8.0.2 && <1.9,+ tomland ==1.0.*,+ unordered-containers >=0.2.9.0 && <0.3
+ src/Salak/Load/Toml.hs view
@@ -0,0 +1,61 @@+{-# LANGUAGE GADTs #-}+{-# LANGUAGE RecordWildCards #-}+{-# LANGUAGE TupleSections #-}+module Salak.Load.Toml where++import Control.Monad (foldM, (>=>))+import Control.Monad.IO.Class (MonadIO, liftIO)+import Control.Monad.State+import qualified Data.HashMap.Strict as HM+import qualified Data.List.NonEmpty as N+import qualified Data.Text.IO as IO+import Data.Time+import Salak+import Salak.Load+import Toml hiding (TOML)+import qualified Toml as T++data TOML = TOML++instance HasLoad TOML where+ loaders _ = (, loadToml) <$> ["toml", "tml"]++toSs :: Key -> [Selector]+toSs (Key ps) = toS <$> N.toList ps++toS :: Piece -> Selector+toS = SStr . unPiece++loadTOML :: Reload -> T.TOML -> SourcePack -> SourcePack+loadTOML name v sp = loadFile name sp $ go v+ where+ go T.TOML{..} i s = HM.foldlWithKey' (g1 i) (return s) tomlPairs+ >>= h2 i tomlTables+ g1 i ms k v' = ms >>= updateSources (toSs k) (f1 i v')+ g2 i ms _ v' = ms >>= f2 i v'+ f1 :: Monad m => Priority -> AnyValue -> Source -> m Source+ f1 i (AnyValue (Array b)) = \s -> foldM (\s' (ix,x) -> updateSource (SNum ix) (f1 i $ AnyValue x) s') s $ zip [0..] b+ f1 i (AnyValue (Bool b)) = return . insertSource (VBool i b)+ f1 i (AnyValue (Integer b)) = return . insertSource (VNum i $ fromIntegral b)+ f1 i (AnyValue (Double b)) = return . insertSource (VNum i $ realToFrac b)+ f1 i (AnyValue (Text b)) = return . insertSource (VStr i b)+ f1 i (AnyValue (Local b)) = return . insertSource (VLTime i b)+ f1 i (AnyValue (Day b)) = return . insertSource (VDay i b)+ f1 i (AnyValue (Hours b)) = return . insertSource (VHour i b)+ f1 i (AnyValue (Zoned (ZonedTime a b))) = return . insertSource (VZTime i b a)+ -- f1 i _ = return+ f2 :: Monad m => Priority -> PrefixTree T.TOML -> Source -> m Source+ f2 i (Leaf k toml) = updateSources (toSs k) (go toml i)+ f2 i (Branch k v' tomap) = updateSources (toSs k) (h1 i v' >=> h2 i tomap)+ h1 :: Monad m => Priority -> Maybe T.TOML -> Source -> m Source+ h1 i (Just t) = go t i+ h1 _ _ = return+ h2 :: Monad m => Priority -> PrefixMap T.TOML -> Source -> m Source+ h2 i m s = HM.foldlWithKey' (g2 i) (return s) m++loadToml :: MonadIO m => FilePath -> SourcePackT m ()+loadToml file = do+ re <- liftIO $ parse <$> IO.readFile file+ modify $ \sp ->case re of+ Left e -> addErr' (show e) sp+ Right a -> loadTOML (defReload file $ loadToml file) a sp
+ test/Spec.hs view
@@ -0,0 +1,69 @@+{-# LANGUAGE DataKinds #-}+{-# LANGUAGE DeriveGeneric #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE ScopedTypeVariables #-}++module Main where++import Control.Monad.Reader+import Control.Monad.Writer+import Data.Either+import Data.List (intercalate)+import Data.Text (Text, pack, unpack)+import GHC.Generics+import Salak+import Salak.Load.Toml+import Test.Hspec+import Test.QuickCheck++main = hspec spec++spec :: Spec+spec = do+ describe "Salak.Load.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 SubConf where+ fromProp = SubConf <$> "hello" .?= "yyy"++instance FromProp Conf++tomlProperty = do+ context "load toml" $ do+ it "salak.toml" $ do+ loadAndRunSalak (loadToml "test/salak.toml") $ do+ 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 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"