lens-toml-parser (empty) → 0.1.0.0
raw patch · 10 files changed
+728/−0 lines, 10 filesdep +basedep +containersdep +dwergazsetup-changed
Dependencies added: base, containers, dwergaz, hlint, lens-simple, lens-toml-parser, profunctors, text, time, toml-parser
Files
- .gitignore +4/−0
- .travis.yml +92/−0
- ChangeLog.md +5/−0
- LICENSE +13/−0
- Setup.hs +2/−0
- example/example-v0.4.0.toml +246/−0
- lens-toml-parser.cabal +62/−0
- src/TOML/Lens.hs +150/−0
- tests/HLint.hs +12/−0
- tests/Main.hs +142/−0
+ .gitignore view
@@ -0,0 +1,4 @@+dist/+dist-newstyle/+shell.nix+cabal.project.local
+ .travis.yml view
@@ -0,0 +1,92 @@+# This Travis job script has been generated by a script via+#+# make_travis_yml_2.hs 'lens-toml-parser.cabal'+#+# For more information, see https://github.com/hvr/multi-ghc-travis+#+language: c+sudo: false++git:+ submodules: false # whether to recursively clone submodules++cache:+ directories:+ - $HOME/.cabal/packages+ - $HOME/.cabal/store++before_cache:+ - rm -fv $HOME/.cabal/packages/hackage.haskell.org/build-reports.log+ # remove files that are regenerated by 'cabal update'+ - rm -fv $HOME/.cabal/packages/hackage.haskell.org/00-index.*+ - rm -fv $HOME/.cabal/packages/hackage.haskell.org/*.json+ - rm -fv $HOME/.cabal/packages/hackage.haskell.org/01-index.cache+ - rm -fv $HOME/.cabal/packages/hackage.haskell.org/01-index.tar+ - rm -fv $HOME/.cabal/packages/hackage.haskell.org/01-index.tar.idx++matrix:+ include:+ - compiler: "ghc-8.0.2"+ # env: TEST=--disable-tests BENCH=--disable-benchmarks+ addons: {apt: {packages: [ghc-ppa-tools,cabal-install-head,ghc-8.0.2], sources: [hvr-ghc]}}+ - compiler: "ghc-8.2.1"+ # env: TEST=--disable-tests BENCH=--disable-benchmarks+ addons: {apt: {packages: [ghc-ppa-tools,cabal-install-head,ghc-8.2.1], sources: [hvr-ghc]}}++before_install:+ - HC=${CC}+ - HCPKG=${HC/ghc/ghc-pkg}+ - unset CC+ - PATH=/opt/ghc/bin:/opt/ghc-ppa-tools/bin:$PATH+ - PKGNAME='lens-toml-parser'++install:+ - cabal --version+ - echo "$(${HC} --version) [$(${HC} --print-project-git-commit-id 2> /dev/null || echo '?')]"+ - BENCH=${BENCH---enable-benchmarks}+ - TEST=${TEST---enable-tests}+ - HADDOCK=${HADDOCK-true}+ - INSTALLED=${INSTALLED-true}+ - travis_retry cabal update -v+ - sed -i.bak 's/^jobs:/-- jobs:/' ${HOME}/.cabal/config+ - rm -fv cabal.project.local+ - "echo 'packages: .' > cabal.project"+ - rm -f cabal.project.freeze+ - cabal new-build -w ${HC} ${TEST} ${BENCH} --dep -j2 all+ - cabal new-build -w ${HC} --disable-tests --disable-benchmarks --dep -j2 all++# Here starts the actual work to be performed for the package under test;+# any command which exits with a non-zero exit code causes the build to fail.+script:+ - if [ -f configure.ac ]; then autoreconf -i; fi+ - rm -rf .ghc.environment.* dist/+ - cabal sdist # test that a source-distribution can be generated+ - cd dist/+ - SRCTAR=(${PKGNAME}-*.tar.gz)+ - SRC_BASENAME="${SRCTAR/%.tar.gz}"+ - tar -xvf "./$SRC_BASENAME.tar.gz"+ - cd "$SRC_BASENAME/"+## from here on, CWD is inside the extracted source-tarball+ - rm -fv cabal.project.local+ - "echo 'packages: .' > cabal.project"+ # this builds all libraries and executables (without tests/benchmarks)+ - rm -f cabal.project.freeze+ - cabal new-build -w ${HC} --disable-tests --disable-benchmarks all+ # this builds all libraries and executables (including tests/benchmarks)+ # - rm -rf ./dist-newstyle++ # Build with installed constraints for packages in global-db+ - if $INSTALLED; then+ echo cabal new-build -w ${HC} --disable-tests --disable-benchmarks $(${HCPKG} list --global --simple-output --names-only | sed 's/\([a-zA-Z0-9-]\{1,\}\) */--constraint="\1 installed" /g') all | sh;+ else echo "Not building with installed constraints"; fi++ # build & run tests, build benchmarks+ - cabal new-build -w ${HC} ${TEST} ${BENCH} all+ - if [ "x$TEST" = "x--enable-tests" ]; then cabal new-test -w ${HC} ${TEST} all; fi++ # haddock+ - rm -rf ./dist-newstyle+ - if $HADDOCK; then cabal new-haddock -w ${HC} --disable-tests --disable-benchmarks all; else echo "Skipping haddock generation";fi++# REGENDATA ["lens-toml-parser.cabal"]+# EOF
+ ChangeLog.md view
@@ -0,0 +1,5 @@+# Revision history for lens-toml-parser++## 0.1.0.0 -- 2017-08-14++* First version. Released on an unsuspecting world.
+ LICENSE view
@@ -0,0 +1,13 @@+Copyright (c) 2017 Henry Till++Permission to use, copy, modify, and/or distribute this software for any purpose+with or without fee is hereby granted, provided that the above copyright notice+and this permission notice appear in all copies.++THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH+REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND+FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,+INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS+OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER+TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF+THIS SOFTWARE.
+ Setup.hs view
@@ -0,0 +1,2 @@+import Distribution.Simple+main = defaultMain
+ example/example-v0.4.0.toml view
@@ -0,0 +1,246 @@+################################################################################+## 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"++## toml-parser does handle the following:+#+# [fruit.physical]+# color = "red"+# shape = "round"+#+# [[fruit.variety]]+# name = "red delicious"+#+# [[fruit.variety]]+# name = "granny smith"+#+# [[fruit]]+# name = "banana"+#+# [[fruit.variety]]+# name = "plantain"
+ lens-toml-parser.cabal view
@@ -0,0 +1,62 @@+-- Initial lens-toml-parser.cabal generated by cabal init. For further +-- documentation, see http://haskell.org/cabal/users-guide/++name: lens-toml-parser+version: 0.1.0.0+synopsis: Lenses for toml-parser+description: Lenses for toml-parser+license: ISC+license-file: LICENSE+author: Henry Till+maintainer: henrytill@gmail.com+homepage: https://github.com/xngns/lens-toml-parser+copyright: Copyright (c) 2017, Henry Till+category: Language, Lenses+build-type: Simple+cabal-version: >=1.10+tested-with: GHC == 8.0.2, GHC == 8.2.1++extra-source-files:+ .gitignore+ .travis.yml+ ChangeLog.md+ example/example-v0.4.0.toml++source-repository head+ type: git+ location: https://github.com/xngns/lens-toml-parser.git++library+ exposed-modules: TOML.Lens+ -- other-modules: + -- other-extensions: + build-depends: base >=4.8 && <4.11+ , text >=1.2 && <1.3+ , time >=1.5 && <1.9+ , profunctors >=5.2 && <5.3+ , toml-parser >=0.1 && <0.2+ hs-source-dirs: src+ default-language: Haskell2010+ ghc-options: -Wall++test-suite tests+ type: exitcode-stdio-1.0+ main-is: Main.hs+ build-depends: base >=4.8 && <4.11+ , containers >=0.5 && <0.6+ , text >=1.2 && <1.3+ , dwergaz >=0.2 && <0.3+ , toml-parser >=0.1 && <0.2+ , lens-simple >=0.1 && <0.2+ , lens-toml-parser+ hs-source-dirs: tests+ default-language: Haskell2010+ ghc-options: -Wall++test-suite hlint+ type: exitcode-stdio-1.0+ main-is: HLint.hs+ build-depends: base+ , hlint >= 1.9+ hs-source-dirs: tests+ default-language: Haskell2010
+ src/TOML/Lens.hs view
@@ -0,0 +1,150 @@+-- |+-- Module : TOML.Lens+-- Description : Lenses for toml-parser+-- Copyright : (c) 2017, Henry Till+-- License : ISC+-- Maintainer : henrytill@gmail.com+-- Stability : experimental+--+-- Lenses for <https://hackage.haskell.org/package/toml-parser toml-parser>.+--+module TOML.Lens+ ( _Table+ , _List+ , _Double+ , _Integer+ , _String+ , _Bool+ , _ZonedTimeV+ , _LocalTimeV+ , _DayV+ , _TimeOfDayV+ ) where++import Data.Profunctor+import qualified Data.Text as T+import Data.Time++import TOML++--+-- With help from:+-- <https://github.com/ekmett/lens/wiki/How-can-I-write-lenses-without-depending-on-lens%3F>+--+-- @+-- type Prism s t a b = (Choice p, Applicative f) => p a (f b) -> p s (f t)+-- @+--+-- @+-- type Prism' s a = Prism s s a a+-- @+--++prism+ :: (Choice p, Applicative f)+ => (b -> t)+ -> (s -> Either t a)+ -> p a (f b)+ -> p s (f t)+prism bt seta = dimap seta (either pure (fmap bt)) . right'+{-# INLINE prism #-}++-- | @_Table :: Prism' Value [(Text, Value)]@+_Table+ :: (Choice p, Applicative f)+ => p [(T.Text, Value)] (f [(T.Text, Value)])+ -> p Value (f Value)+_Table =+ prism Table $ \ n -> case n of+ Table v -> pure v+ _ -> Left n++-- | @_List :: Prism' Value [Value]@+_List+ :: (Choice p, Applicative f)+ => p [Value] (f [Value])+ -> p Value (f Value)+_List =+ prism List $ \ n -> case n of+ List v -> pure v+ _ -> Left n++-- | @_Double :: Prism' Value Double@+_Double+ :: (Choice p, Applicative f)+ => p Double (f Double)+ -> p Value (f Value)+_Double =+ prism Double $ \ n -> case n of+ Double v -> pure v+ _ -> Left n++-- | @_Integer :: Prism' Value Integer@+_Integer+ :: (Choice p, Applicative f)+ => p Integer (f Integer)+ -> p Value (f Value)+_Integer =+ prism Integer $ \ n -> case n of+ Integer v -> pure v+ _ -> Left n++-- | @_String :: Prism' Value T.Text@+_String+ :: (Choice p, Applicative f)+ => p T.Text (f T.Text)+ -> p Value (f Value)+_String =+ prism String $ \ n -> case n of+ String v -> pure v+ _ -> Left n++-- | @_Bool :: Prism' Value Bool@+_Bool+ :: (Choice p, Applicative f)+ => p Bool (f Bool)+ -> p Value (f Value)+_Bool =+ prism Bool $ \ n -> case n of+ Bool v -> pure v+ _ -> Left n++-- | @_ZonedTimeV :: Prism' Value ZonedTime@+_ZonedTimeV+ :: (Choice p, Applicative f)+ => p ZonedTime (f ZonedTime)+ -> p Value (f Value)+_ZonedTimeV =+ prism ZonedTimeV $ \ n -> case n of+ ZonedTimeV v -> pure v+ _ -> Left n++-- | @_LocalTimeV :: Prism' Value LocalTime@+_LocalTimeV+ :: (Choice p, Applicative f)+ => p LocalTime (f LocalTime)+ -> p Value (f Value)+_LocalTimeV =+ prism LocalTimeV $ \ n -> case n of+ LocalTimeV v -> pure v+ _ -> Left n++-- | @_DayV :: Prism' Value Day@+_DayV+ :: (Choice p, Applicative f)+ => p Day (f Day)+ -> p Value (f Value)+_DayV =+ prism DayV $ \ n -> case n of+ DayV v -> pure v+ _ -> Left n++-- | @_TimeOfDayV :: Prism' Value TimeOfDay@+_TimeOfDayV+ :: (Choice p, Applicative f)+ => p TimeOfDay (f TimeOfDay)+ -> p Value (f Value)+_TimeOfDayV =+ prism TimeOfDayV $ \ n -> case n of+ TimeOfDayV v -> pure v+ _ -> Left n
+ tests/HLint.hs view
@@ -0,0 +1,12 @@+module Main (main) where++import Control.Monad+import Language.Haskell.HLint3+import System.Environment+import System.Exit++main :: IO ()+main = do+ args <- getArgs+ hints <- hlint $ ["src", "tests"] ++ args+ unless (null hints) exitFailure
+ tests/Main.hs view
@@ -0,0 +1,142 @@+{-# LANGUAGE OverloadedStrings #-}++module Main (main) where++import Control.Monad (unless)+import qualified Data.Map.Lazy as Map+import qualified Data.Text as T+import Data.Text.IO (readFile)+import Lens.Simple+import Prelude hiding (readFile)+import System.Exit (exitFailure)+import Test.Dwergaz+import qualified TOML+import TOML.Lens+++alist :: Ord k => [(k, v)] -> Map.Map k v+alist = Map.fromList++alistLens+ :: (Ord k1, Ord k2, Functor f)+ => LensLike f [(k1, v1)] [(k2, v2)] (Map.Map k1 v1) (Map.Map k2 v2)+alistLens = iso Map.fromList Map.toList++mapAt+ :: Applicative f+ => T.Text+ -> (Map.Map T.Text TOML.Value -> f (Map.Map T.Text TOML.Value))+ -> Map.Map T.Text TOML.Value+ -> f (Map.Map T.Text TOML.Value)+mapAt k = at k . _Just . _Table . alistLens++listAt+ :: Applicative f+ => T.Text+ -> ([TOML.Value] -> f [TOML.Value])+ -> Map.Map T.Text TOML.Value+ -> f (Map.Map T.Text TOML.Value)+listAt k = at k . _Just . _List++testTableKey :: [(T.Text, TOML.Value)] -> Test+testTableKey kv+ = Expect "'key' from 'table' == Just \"value\""+ (==) expected actual+ where+ expected = Just "value"+ actual = alist kv ^? mapAt "table" . at "key" . _Just . _String++testTableZoo :: [(T.Text, TOML.Value)] -> Test+testTableZoo kv+ = Expect "'zoo' from 'table' == Nothing"+ (==) expected actual+ where+ expected = Nothing+ actual = alist kv ^? mapAt "table" . at "zoo" . _Just . _String++testTableSubtableKey :: [(T.Text, TOML.Value)] -> Test+testTableSubtableKey kv+ = Expect "'key' from 'subtable' from 'table' == Just \"another value\""+ (==) expected actual+ where+ expected = Just "another value"+ actual = alist kv ^? mapAt "table" . mapAt "subtable" . at "key" . _Just . _String++testTableInlineNameFirst :: [(T.Text, TOML.Value)] -> Test+testTableInlineNameFirst kv+ = Expect "'first' from 'name' from 'inline' from 'table' == \"Tom\""+ (==) expected actual+ where+ expected = Just "Tom"+ actual = alist kv ^? mapAt "table" . mapAt "inline" . mapAt "name" . at "first" . _Just . _String++testTableInlinePointY :: [(T.Text, TOML.Value)] -> Test+testTableInlinePointY kv+ = Expect "'y' from 'point' from 'inline' from 'table' == Just 2"+ (==) expected actual+ where+ expected = Just 2+ actual = alist kv ^? mapAt "table" . mapAt "inline" . mapAt "point" . at "y" . _Just . _Integer++testStringBasicBasic :: [(T.Text, TOML.Value)] -> Test+testStringBasicBasic kv+ = Expect "'basic' from 'basic' from 'string' == <some escaped nonsense>"+ (==) expected actual+ where+ expected = Just "I'm a string. \"You can quote me\". Name\tJos\233\nLocation\tSF."+ actual = alist kv ^? mapAt "string" . mapAt "basic" . at "basic" . _Just . _String++testStringMultiline :: [(T.Text, TOML.Value)] -> Test+testStringMultiline kv+ = Predicate "'key1', 'key2', and 'key3' from 'multiline' from 'string' are all the same"+ (\ xs -> all (== head xs) (tail xs))+ [actual1, actual2, actual3]+ where+ actual1 = alist kv ^? mapAt "string" . mapAt "multiline" . at "key1" . _Just . _String+ actual2 = alist kv ^? mapAt "string" . mapAt "multiline" . at "key2" . _Just . _String+ actual3 = alist kv ^? mapAt "string" . mapAt "multiline" . at "key3" . _Just . _String++testStringMultilineContinued :: [(T.Text, TOML.Value)] -> Test+testStringMultilineContinued kv+ = Predicate "'key1', 'key2', and 'key3' from 'continued' from 'multiline' from 'string' are all the same"+ (\ xs -> all (== head xs) (tail xs))+ [actual1, actual2, actual3]+ where+ actual1 = alist kv ^? mapAt "string" . mapAt "multiline" . mapAt "continued" . at "key1" . _Just . _String+ actual2 = alist kv ^? mapAt "string" . mapAt "multiline" . mapAt "continued" . at "key2" . _Just . _String+ actual3 = alist kv ^? mapAt "string" . mapAt "multiline" . mapAt "continued" . at "key3" . _Just . _String++testArrayKey1 :: [(T.Text, TOML.Value)] -> Test+testArrayKey1 kv+ = Expect "'key1' from 'array' == [1, 2, 3]"+ (==) expected actual+ where+ expected = [1, 2, 3]+ actual = alist kv ^.. mapAt "array" . listAt "key1" . traverse . _Integer++runTests :: [(T.Text, TOML.Value)] -> [Result]+runTests kv = runTest . ($ kv) <$> tests+ where+ tests = [ testTableKey+ , testTableZoo+ , testTableSubtableKey+ , testTableInlineNameFirst+ , testTableInlinePointY+ , testStringBasicBasic+ , testStringMultiline+ , testStringMultilineContinued+ , testArrayKey1+ ]++readTOMLFile :: String -> IO [(T.Text, TOML.Value)]+readTOMLFile file = readFile file >>= parse >>= handleError+ where+ parse = pure . TOML.parseTOML+ handleError = either (error . show) pure++main :: IO ()+main = do+ ex <- readTOMLFile "./example/example-v0.4.0.toml"+ rs <- pure (runTests ex)+ _ <- mapM_ print rs+ unless (all isPassed rs) exitFailure