hsini 0.1 → 0.5.2.2
raw patch · 9 files changed
Files
- Setup.hs +0/−40
- hsini.cabal +48/−36
- src/Data/Ini.hs +34/−31
- src/Data/Ini/Reader.hs +21/−20
- src/Data/Ini/Reader/Internals.hs +79/−75
- src/Data/Ini/Types.hs +10/−6
- tst/Ini.hs +49/−34
- tst/Main.hs +7/−6
- tst/ReaderI.hs +206/−40
− Setup.hs
@@ -1,40 +0,0 @@-#! /usr/bin/env runhaskell-{-# OPTIONS_GHC -fno-warn-warnings-deprecations #-}--- Copyright : 2011 Magnus Therning--- License : BSD3-module Main where--import Distribution.Simple-import Distribution.PackageDescription-import Distribution.Simple.Utils-import Distribution.Simple.LocalBuildInfo-import System.Cmd-import Control.Monad-import System.FilePath-import System.Directory--main = defaultMainWithHooks $ simpleUserHooks- { cleanHook = profileClean- , runTests = runTestsBuild- }--profileClean pd v uh cf = let- _matchFileGlob g = catch (matchFileGlob g) (\ _ -> return [])- in do- (cleanHook simpleUserHooks) pd v uh cf- tixFiles <- _matchFileGlob "*.tix"- mapM_ removeFile tixFiles- doesDirectoryExist ".hpc" >>= \ d -> when d $ removeDirectoryRecursive ".hpc"--runTestsBuild a b pd lbi = let- doWithExe bldDir e _ = let- _eN = exeName e- _exe = bldDir </> _eN </> _eN- _runTest = do- putStrLn $ "** " ++ _eN ++ ":"- system _exe >> return ()- in- when (_eN `elem` ["tests"]) _runTest- in do- (runTests simpleUserHooks) a b pd lbi- withExeLBI pd lbi (doWithExe $ buildDir lbi)
hsini.cabal view
@@ -1,42 +1,54 @@-name : hsini-version : 0.1-license : BSD3-license-file : LICENSE-author : Magnus Therning-maintainer : magnus@therning.org-copyright : Magnus Therning, 2010-synopsis : Package for user configuration files (INI)-description : None yet-build-type : Custom-category : Network-cabal-version : >= 1.6+cabal-version: 3.0+name: hsini+version: 0.5.2.2+synopsis: ini configuration files+description:+ Library for reading and writing configuration files in INI format (see <https://en.wikipedia.org/wiki/INI_file>). +category: Configuration, Data+author: Magnus Therning <magnus@therning.org>+maintainer: Magnus Therning <magnus@therning.org>+license: BSD-3-Clause+license-file: LICENSE+build-type: Simple+ source-repository head- type : git- location : https : //github.com/magthe/hsini.git+ type: git+ location: https://github.com/magthe/hsini.git -source-repository this- type : git- location : https : //github.com/magthe/hsini.git- tag : REL_0.1+library+ exposed-modules:+ Data.Ini+ Data.Ini.Reader+ Data.Ini.Reader.Internals+ Data.Ini.Types -flag Test- description : Enable building of tests- default : False+ hs-source-dirs: src+ build-depends:+ , base <5+ , containers+ , mtl+ , parsec -library- hs-source-dirs : src- build-depends : base ==4.3.*, bytestring ==0.9.*, containers ==0.4.*, mtl ==2.0.*, parsec ==3.1.*- exposed-modules : Data.Ini Data.Ini.Types Data.Ini.Reader- other-modules : Data.Ini.Reader.Internals+ default-language: Haskell2010+ ghc-options: -Wall -Wunused-packages -executable tests- main-is : Main.hs- hs-source-dirs : tst, src- other-modules : Ini ReaderI- if flag(Test)- build-depends : HUnit, test-framework, test-framework-hunit, test-framework-quickcheck2, test-framework-th, QuickCheck- buildable : True- ghc-options : -fhpc- else- buildable : False+test-suite hsini-tests+ type: exitcode-stdio-1.0+ main-is: Main.hs+ other-modules:+ Ini+ ReaderI++ hs-source-dirs: tst+ build-depends:+ , base <5+ , hsini+ , parsec+ , tasty+ , tasty-hunit+ , tasty-quickcheck+ , tasty-th++ default-language: Haskell2010+ ghc-options: -Wall -Wunused-packages
src/Data/Ini.hs view
@@ -1,28 +1,33 @@--- |--- Module : Data.Ini--- Copyright : 2011 Magnus Therning--- License : BSD3------ A representation of configuration options. It consists of /sections/,--- each which can contain 0 or more /options/. Each options is a /key/,--- /value/ pair.------ This module contains the API for constructing, manipulating, and querying--- configurations.+{-# LANGUAGE ImportQualifiedPost #-}++{- |+Module : Data.Ini+Copyright : 2011-2014 Magnus Therning+License : BSD3++A representation of configuration options. It consists of /sections/,+each which can contain 0 or more /options/. Each options is a /key/,+/value/ pair.++This module contains the API for constructing, manipulating, and querying+configurations.+-} module Data.Ini where -- {{{1 imports-import qualified Data.Map as M-import Data.Maybe+import Data.Map qualified as M+import Data.Maybe (isJust) -import Data.Ini.Types+import Data.Ini.Types (Config, OptionName, OptionValue, Section, SectionName) -- {{{1 configurations+ -- | Constructs an empty configuration. emptyConfig :: Config emptyConfig = M.empty -- {{{1 sections+ -- | Returns @True@ iff the configuration has a section with that name. hasSection :: SectionName -> Config -> Bool hasSection = M.member@@ -40,37 +45,35 @@ delSection = M.delete -- {{{1 options+ -- | Returns @True@ if the names section has the option. hasOption :: SectionName -> OptionName -> Config -> Bool-hasOption sn on cfg = isJust $ do- s <- getSection sn cfg- M.lookup on s+hasOption sn on cfg = isJust $ getSection sn cfg >>= M.lookup on -- | Returns the value of the option, if it exists. getOption :: SectionName -> OptionName -> Config -> Maybe OptionValue-getOption sn on cfg = do- s <- getSection sn cfg- M.lookup on s+getOption sn on cfg = getSection sn cfg >>= M.lookup on -- | Returns a list of all options in the section.-options :: SectionName -> Config -> [OptionName]-options sn cfg = maybe [] (M.keys) (getSection sn cfg)+options :: SectionName -> Config -> [OptionName]+options sn cfg = maybe [] M.keys (getSection sn cfg) -- | Sets the value of the option, adding it if it doesn't exist. setOption :: SectionName -> OptionName -> OptionValue -> Config -> Config-setOption sn on ov cfg = let- s = getSection sn cfg- new_s = M.insert on ov M.empty- in maybe (M.insert sn new_s cfg) (\ sec -> M.insert sn (M.insert on ov sec) cfg) s+setOption sn on ov cfg = maybe (M.insert sn new_s cfg) (\sec -> M.insert sn (M.insert on ov sec) cfg) s+ where+ s = getSection sn cfg+ new_s = M.insert on ov M.empty -- | Removes the option if it exists. Empty sections are pruned. delOption :: SectionName -> OptionName -> Config -> Config-delOption sn on cfg = let- s = getSection sn cfg- sEmptyAfterDelete = maybe True (\ sec -> M.empty == M.delete on sec) s- in if sEmptyAfterDelete+delOption sn on cfg =+ if sEmptyAfterDelete then M.delete sn cfg- else maybe cfg (\ sec -> M.insert sn (M.delete on sec) cfg) s+ else maybe cfg (\sec -> M.insert sn (M.delete on sec) cfg) s+ where+ s = getSection sn cfg+ sEmptyAfterDelete = maybe True (\sec -> M.empty == M.delete on sec) s -- | Returns all options and their values of a section. allItems :: SectionName -> Config -> [(OptionName, OptionValue)]
src/Data/Ini/Reader.hs view
@@ -1,25 +1,26 @@--- |--- Module : Data.Ini.Reader--- Copyright : 2011 Magnus Therning--- License : BSD3------ Parser for configurations.-module Data.Ini.Reader- ( parse- , IniReaderError(..)- , IniParseResult- ) where+{-# LANGUAGE ImportQualifiedPost #-} -import Control.Monad.Error-import qualified Text.ParserCombinators.Parsec as P+{- |+Module : Data.Ini.Reader+Copyright : 2011-2014 Magnus Therning+License : BSD3 -import Data.Ini.Types-import Data.Ini.Reader.Internals+Parser for configurations.+-}+module Data.Ini.Reader (+ parse,+ IniReaderError (..),+ IniParseResult,+) where +import Control.Monad.Except (throwError)+import Text.ParserCombinators.Parsec qualified as P++import Data.Ini.Reader.Internals (IniParseResult, IniReaderError (..), buildConfig, iniParser)+import Data.Ini.Types (Config)+ -- | Parser for a configuration contained in a 'String'. parse :: String -> IniParseResult Config-parse s = let- pr = P.parse iniParser "ini" s- in case pr of- Left e -> throwError . IniParserError $ show e- Right is -> buildConfig is+parse s = case P.parse iniParser "ini" s of+ Left e -> throwError . IniParserError $ show e+ Right is -> buildConfig is
src/Data/Ini/Reader/Internals.hs view
@@ -1,20 +1,35 @@--- |--- Module : Data.Ini.Reader.Internals--- Copyright : 2011 Magnus Therning--- License : BSD3------ Internal functions used in 'Data.Ini.Reader'.+{-# LANGUAGE FlexibleContexts #-}++{- |+Module : Data.Ini.Reader.Internals+Copyright : 2011-2014 Magnus Therning+License : BSD3++Internal functions used in 'Data.Ini.Reader'.+-} module Data.Ini.Reader.Internals where -import Control.Monad.Error-import Control.Monad.State--- import Text.ParserCombinators.Parsec as P-import Text.Parsec as P-import Text.Parsec.String-import qualified Data.ByteString as BS+import Control.Monad.Except (MonadError (throwError))+import Control.Monad.State (evalState, get, put)+import Data.Functor (($>))+import Text.Parsec as P (+ anyChar,+ between,+ char,+ choice,+ many,+ many1,+ manyTill,+ newline,+ noneOf,+ oneOf,+ )+import Text.Parsec.String (Parser) -import Data.Ini-import Data.Ini.Types+import Data.Char (isSpace)+import Data.Ini (emptyConfig, setOption)+import Data.Ini.Types (Config)+import Data.List (dropWhileEnd) data IniReaderError = IniParserError String@@ -22,10 +37,6 @@ | IniOtherError String deriving (Eq, Show) -instance Error IniReaderError where- noMsg = IniOtherError "Unknown error"- strMsg s = IniOtherError s- type IniParseResult = Either IniReaderError -- | The type used to represent a line of a config file.@@ -38,7 +49,8 @@ -- | Build a configuration from a list of 'IniFile' items. buildConfig :: [IniFile] -> IniParseResult Config-buildConfig ifs = let+buildConfig ifs =+ let isComment CommentL = True isComment _ = False @@ -46,10 +58,10 @@ -- merge together OptionL and subsequent OptionContL items mergeOptions [] = return []- mergeOptions (s@(SectionL _) : ifs) = (s :) `liftM` mergeOptions ifs- mergeOptions (CommentL : ifs ) = (CommentL :) `liftM` mergeOptions ifs- mergeOptions (OptionL on ov : OptionContL ov2 : ifs) = mergeOptions $ (OptionL on (ov ++ ov2)) : ifs- mergeOptions (o@(OptionL on ov) : ifs) = (o :) `liftM` mergeOptions ifs+ mergeOptions (s@(SectionL _) : ifs') = (s :) `fmap` mergeOptions ifs'+ mergeOptions (CommentL : ifs') = (CommentL :) `fmap` mergeOptions ifs'+ mergeOptions (OptionL on ov : OptionContL ov2 : ifs') = mergeOptions $ OptionL on (ov ++ ov2) : ifs'+ mergeOptions (o@(OptionL _ _) : ifs') = (o :) `fmap` mergeOptions ifs' mergeOptions _ = throwError $ IniSyntaxError "Syntax error in INI file." -- build the configuration from a [IniFile]@@ -59,69 +71,61 @@ sn <- get let na = setOption sn on ov a buildit na is-- in mergeOptions fIfs >>= (\ is -> return . fst $ runState (buildit emptyConfig is) "default")+ buildit _ _ = undefined+ in+ mergeOptions fIfs >>= \is -> return $ evalState (buildit emptyConfig is) "default" -- | Consumer of whitespace \"@ \t@\". eatWhiteSpace :: Parser String eatWhiteSpace = many $ oneOf " \t" --- | Parser for the start-of-section line. It expects the line to start with a--- @[@ then find the section name, and finally a @]@. The section name may be--- surrounded by any number of white space characters (see 'eatWhiteSpace').+{- | Parser for the start-of-section line. It expects the line to start with a+@[@ then find the section name, and finally a @]@. The section name may be+surrounded by any number of white space characters (see 'eatWhiteSpace').+-} secParser :: Parser IniFile-secParser = let- validSecNameChrs = ['a'..'z'] ++ ['A'..'Z'] ++ "-/@"- in do- char '['- eatWhiteSpace- sn <- many1 $ oneOf validSecNameChrs- eatWhiteSpace- char ']'- manyTill anyChar newline- return $ SectionL sn+secParser = SectionL <$> between sectionNameOpen sectionNameClose sectionName+ where+ sectionNameOpen = char '[' *> eatWhiteSpace+ sectionNameClose = eatWhiteSpace *> char ']' *> manyTill anyChar newline+ sectionName = many1 $ oneOf validSecNameChrs+ validSecNameChrs = ['a' .. 'z'] ++ ['A' .. 'Z'] ++ ['0' .. '9'] ++ "._-/@\" " --- | Parser for a single line of an option. The line must start with an option--- name, then a @=@ must be found, and finally the rest of the line is taken as--- the option value. The equal sign may be surrounded by any number of white--- space characters (see 'eatWhiteSpace').+{- | Parser for a single line of an option. The line must start with an option+name, then a @=@ must be found, and finally the rest of the line is taken as+the option value. The equal sign may be surrounded by any number of white+space characters (see 'eatWhiteSpace').+-} optLineParser :: Parser IniFile-optLineParser = let- validOptNameChrs = ['a'..'z'] ++ ['A'..'Z'] ++ "-/@"- in do- on <- many1 $ oneOf validOptNameChrs- eatWhiteSpace- char '='- eatWhiteSpace- ov <- manyTill anyChar newline- return $ OptionL on ov+optLineParser = OptionL <$> optionName <*> (optionEqual *> optionValue)+ where+ optionName = dropWhileEnd isSpace <$> (eatWhiteSpace *> many1 (oneOf validOptNameChrs))+ optionEqual = eatWhiteSpace *> char '=' *> eatWhiteSpace+ optionValue = manyTill anyChar newline+ validOptNameChrs = ['a' .. 'z'] ++ ['A' .. 'Z'] ++ ['0' .. '9'] ++ "_-/@ " --- | Parser for an option-value continuation line. The line must start with--- either a space or a tab character (\"@ \t@\"). Everything else on the line,--- until the newline character, is taken as the continuation of an option--- value.+{- | Parser for an option-value continuation line. The line must start with+either a space or a tab character (\"@ \t@\"). Everything else on the line,+until the newline character, is taken as the continuation of an option+value.+-} optContParser :: Parser IniFile-optContParser = do- oneOf " \t"- eatWhiteSpace- oc <- noneOf " \t"- ov <- manyTill anyChar newline- return $ OptionContL $ oc:ov+optContParser = OptionContL <$> value+ where+ value = (:) <$> (oneOf " \t" *> eatWhiteSpace *> noneOf " \t") <*> manyTill anyChar newline --- | Parser for "noise" in the configuration file, such as comments and empty--- lines. (Note that lines containing only space characters will be--- successfully parsed by 'optContParser'.)+{- | Parser for "noise" in the configuration file, such as comments and empty+lines. (Note that lines containing only space characters will be+successfully parsed by 'optContParser'.)+-} noiseParser :: Parser IniFile-noiseParser = let- commentP = do- char '#'- manyTill anyChar newline- emptyL = newline >> return ""- in choice [commentP, emptyL] >> return CommentL+noiseParser =+ let+ commentP = oneOf "#;" *> manyTill anyChar newline+ emptyL = (newline $> "")+ in+ choice [commentP, emptyL] $> CommentL iniParser :: Parser [IniFile]-iniParser = do- many noiseParser- s1 <- secParser- r <- many $ choice [secParser, optLineParser, optContParser, noiseParser]- return (s1:r)+iniParser =+ many $ choice [secParser, optLineParser, optContParser, noiseParser]
src/Data/Ini/Types.hs view
@@ -1,10 +1,14 @@--- |--- Module : Data.Ini.Types--- Copyright : 2011 Magnus Therning--- License : BSD3+{-# LANGUAGE ImportQualifiedPost #-}++{- |+Module : Data.Ini.Types+Copyright : 2011-2014 Magnus Therning+License : BSD3+-} module Data.Ini.Types where -import qualified Data.Map as M+import Control.Arrow (second)+import Data.Map qualified as M type Config = M.Map SectionName Section @@ -16,7 +20,7 @@ -- useful since Map doesn't have any Serial instance cfgFromList :: [(SectionName, [(OptionName, OptionValue)])] -> Config-cfgFromList = M.fromList . map (\ (sn, ol) -> (sn, M.fromList ol))+cfgFromList = M.fromList . map (second M.fromList) cfgToList :: Config -> [(SectionName, [(OptionName, OptionValue)])] cfgToList = M.toList . M.map M.toList
tst/Ini.hs view
@@ -1,71 +1,86 @@-{-# OPTIONS_GHC -XTemplateHaskell #-}--- Copyright : 2011 Magnus Therning+{-# LANGUAGE TemplateHaskell #-}++-- Copyright : 2011-2014 Magnus Therning -- License : BSD3-module Ini where+module Ini (+ allTests,+) where -- {{{1 imports-import Data.List-import Data.Maybe-import Test.Framework-import Test.Framework.TH-import Test.Framework.Providers.QuickCheck2-import Test.QuickCheck+import Data.Maybe (fromJust, isJust, isNothing)+import Test.Tasty (TestTree)+import Test.Tasty.QuickCheck (Property, testProperty, (==>))+import Test.Tasty.TH (testGroupGenerator) -import Data.Ini-import Data.Ini.Types+import Data.Ini (allItems, delOption, delSection, getOption, getSection, hasOption, hasSection, setOption)+import Data.Ini.Types (OptionName, OptionValue, SectionName, cfgFromList, cfgToList) -- {{{1 section properties -- adding and then deleting a section is a no-op (if the section doesn't exist -- already)+prop_secAddDel :: [Char] -> [(SectionName, [(OptionName, OptionValue)])] -> Bool prop_secAddDel sn cfglst = delSection sn (setOption sn "foo" "bar" cfg2) == cfg2- where- cfg = cfgFromList cfglst- cfg2 = delSection sn cfg -- must make sure the section doesn't exist before adding+ where+ cfg = cfgFromList cfglst+ cfg2 = delSection sn cfg -- must make sure the section doesn't exist before adding -- after adding a section the config has such a section+prop_secAddHas :: SectionName -> [(SectionName, [(OptionName, OptionValue)])] -> Bool prop_secAddHas sn cfglst = hasSection sn (setOption sn "foo" "bar" cfg)- where cfg = cfgFromList cfglst+ where+ cfg = cfgFromList cfglst -- after adding a section it's possible to get it+prop_secAddGet :: SectionName -> [(SectionName, [(OptionName, OptionValue)])] -> Bool prop_secAddGet sn cfglst = isJust $ getSection sn (setOption sn "foo" "bar" cfg)- where cfg = cfgFromList cfglst+ where+ cfg = cfgFromList cfglst -- after deleting a section it's gone+prop_secDelGet :: [Char] -> [(SectionName, [(OptionName, OptionValue)])] -> Bool prop_secDelGet sn cfglst = isNothing $ getSection sn $ delSection sn cfg2- where- cfg = cfgFromList cfglst- cfg2 = setOption sn "foo" "bar" cfg+ where+ cfg = cfgFromList cfglst+ cfg2 = setOption sn "foo" "bar" cfg -- {{{1 option properties -- setting and then deleting an option is a no-op (if the option doesn't exist -- already)+prop_optSetDel :: [Char] -> [Char] -> OptionValue -> [(SectionName, [(OptionName, OptionValue)])] -> Bool prop_optSetDel sn on ov cfglst = delOption sn on (setOption sn on ov cfg) == cfg2- where- cfg = cfgFromList cfglst- cfg2 = delOption sn on cfg+ where+ cfg = cfgFromList cfglst+ cfg2 = delOption sn on cfg -- after setting an option it's there+prop_optSetHas :: SectionName -> OptionName -> OptionValue -> [(SectionName, [(OptionName, OptionValue)])] -> Bool prop_optSetHas sn on ov cfglst = hasOption sn on (setOption sn on ov cfg)- where cfg = cfgFromList cfglst+ where+ cfg = cfgFromList cfglst -- after setting an option it's possible to get it+prop_optSetGet :: SectionName -> OptionName -> OptionValue -> [(SectionName, [(OptionName, OptionValue)])] -> Bool prop_optSetGet sn on ov cfglst = isJust $ getOption sn on $ setOption sn on ov cfg- where cfg = cfgFromList cfglst+ where+ cfg = cfgFromList cfglst -- after deleting a section it's gone+prop_optDelGet :: [Char] -> [Char] -> [Char] -> [(SectionName, [(OptionName, OptionValue)])] -> Bool prop_optDelGet sn on ov cfglst = isNothing $ getOption sn on $ delOption sn on cfg2- where- cfg = cfgFromList cfglst- cfg2 = setOption sn on ov cfg+ where+ cfg = cfgFromList cfglst+ cfg2 = setOption sn on ov cfg -- getting all items-prop_optAllItems cfglst = (length _cfglst > 0) ==> lstItems == (allItems sn cfg)- where- cfg = cfgFromList cfglst- _cfglst = cfgToList cfg- -- sn = head . sort $ map fst _cfglst- sn = head $ map fst _cfglst- lstItems = fromJust $ lookup sn _cfglst+prop_optAllItems :: [(SectionName, [(OptionName, OptionValue)])] -> Property+prop_optAllItems cfglst = not (null _cfglst) ==> lstItems == allItems sn cfg+ where+ cfg = cfgFromList cfglst+ _cfglst = cfgToList cfg+ -- sn = head . sort $ map fst _cfglst+ sn = head $ map fst _cfglst+ lstItems = fromJust $ lookup sn _cfglst -- {{{1 allTests+allTests :: TestTree allTests = $(testGroupGenerator)
tst/Main.hs view
@@ -1,13 +1,14 @@--- Copyright : 2011 Magnus Therning+-- Copyright : 2011-2014 Magnus Therning -- License : BSD3 module Main where -import Test.Framework+import Test.Tasty import qualified Ini as I import qualified ReaderI as RI -main = defaultMain- [ I.allTests- , RI.allTests- ]+main :: IO ()+main = defaultMain allTests++allTests :: TestTree+allTests = testGroup "All tests" [I.allTests, RI.allTests]
tst/ReaderI.hs view
@@ -1,92 +1,258 @@-{-# OPTIONS_GHC -XTemplateHaskell #-}--- Copyright : 2011 Magnus Therning+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE TemplateHaskell #-}++-- Copyright : 2011-2014 Magnus Therning -- License : BSD3-module ReaderI where+module ReaderI (+ allTests,+) where -import Test.Framework-import Test.Framework.Providers.HUnit-import Test.Framework.TH-import Test.HUnit.Base-import Text.ParserCombinators.Parsec as P+import Test.Tasty (TestTree)+import Test.Tasty.HUnit (Assertion, testCase, (@=?))+import Test.Tasty.TH (testGroupGenerator)+import Text.ParserCombinators.Parsec as P (Parser, parse) -import Data.Ini.Reader.Internals+import Data.Ini.Reader.Internals (+ IniFile (CommentL, OptionContL, OptionL, SectionL),+ iniParser,+ noiseParser,+ optContParser,+ optLineParser,+ secParser,+ ) -- Convenience function that translates a parser result to something that's -- easier to check.-p2E p s t = let+p2E :: Parser a -> String -> String -> Either String a+p2E p s t =+ let res = P.parse p s t- in case res of- Left _ -> Left "bad"- Right e -> Right e+ in+ case res of+ Left _ -> Left "bad"+ Right e -> Right e -- {{{1 secParser-case_secParserBasic = let+case_secParserAllowedChars1 :: Assertion+case_secParserAllowedChars1 =+ let expected = Right $ SectionL "foo" actual = p2E secParser "sec" "[foo]\n"- in expected @=? actual+ in+ expected @=? actual -case_secParserDropSpace = let+case_secParserAllowedChars2 :: Assertion+case_secParserAllowedChars2 =+ let+ expected = Right $ SectionL "FooBar"+ actual = p2E secParser "sec" "[FooBar]\n"+ in+ expected @=? actual++case_secParserAllowedChars3 :: Assertion+case_secParserAllowedChars3 =+ let+ expected = Right $ SectionL "@Foo/Bar-"+ actual = p2E secParser "sec" "[@Foo/Bar-]\n"+ in+ expected @=? actual++case_secParserAllowedChars4 :: Assertion+case_secParserAllowedChars4 =+ let+ expected = Right $ SectionL "foo123"+ actual = p2E secParser "sec" "[foo123]\n"+ in+ expected @=? actual++case_secParserAllowedChars5 :: Assertion+case_secParserAllowedChars5 =+ let+ expected = Right $ SectionL "_foo"+ actual = p2E secParser "sec" "[_foo]\n"+ in+ expected @=? actual++case_secParserDropSpace :: Assertion+case_secParserDropSpace =+ let expected = Right $ SectionL "foo" actual = p2E secParser "sec" "[ \tfoo\t ]\n"- in expected @=? actual+ in+ expected @=? actual -case_secParserDropTrailing = let+case_secParserDropTrailing :: Assertion+case_secParserDropTrailing =+ let expected = Right $ SectionL "foo" actual = p2E secParser "sec" "[foo] \t foobar\n"- in expected @=? actual+ in+ expected @=? actual +case_secParserAllowGit1 :: Assertion+case_secParserAllowGit1 =+ let+ expected = Right $ SectionL "branch \"master\""+ actual = p2E secParser "sec" "[branch \"master\"]\n"+ in+ expected @=? actual++case_secParserAllowGit2 :: Assertion+case_secParserAllowGit2 =+ let+ expected = Right $ SectionL "foo \"bar.baz\""+ actual = p2E secParser "sec" "[foo \"bar.baz\"]\n"+ in+ expected @=? actual+ -- {{{1 optLineParser-case_optLineParserBasic = let+case_optLineParserAllowedChars1 :: Assertion+case_optLineParserAllowedChars1 =+ let expected = Right $ OptionL "foo" "bar" actual = p2E optLineParser "optLine" "foo=bar\n"- in expected @=? actual+ in+ expected @=? actual -case_optLineParserDropSpace = let+case_optLineParserAllowedChars2 :: Assertion+case_optLineParserAllowedChars2 =+ let+ expected = Right $ OptionL "Foo" "bAr"+ actual = p2E optLineParser "optLine" "Foo=bAr\n"+ in+ expected @=? actual++case_optLineParserAllowedChars3 :: Assertion+case_optLineParserAllowedChars3 =+ let+ expected = Right $ OptionL "foo@/foo-" "bar"+ actual = p2E optLineParser "optLine" "foo@/foo-=bar\n"+ in+ expected @=? actual++case_optLineParserAllowedChars4 :: Assertion+case_optLineParserAllowedChars4 =+ let+ expected = Right $ OptionL "foo123" "bar"+ actual = p2E optLineParser "optLine" "foo123=bar\n"+ in+ expected @=? actual++case_optLineParserAllowedChars5 :: Assertion+case_optLineParserAllowedChars5 =+ let+ expected = Right $ OptionL "_foo" "bar"+ actual = p2E optLineParser "optLine" "_foo=bar\n"+ in+ expected @=? actual++case_optLineParserAllowedChars6 :: Assertion+case_optLineParserAllowedChars6 =+ let+ expected = Right $ OptionL "foo bar" "baz"+ actual = p2E optLineParser "optLine" "foo bar=baz\n"+ in+ expected @=? actual++case_optLineParserDisallowedChars1 :: Assertion+case_optLineParserDisallowedChars1 =+ let+ expected = Left "bad"+ actual = p2E optLineParser "optLine" "foo.bar=baz\n"+ in+ expected @=? actual++case_optLineParserDropSpace1 :: Assertion+case_optLineParserDropSpace1 =+ let expected = Right $ OptionL "foo" "bar"- actual = p2E optLineParser "optLine" "foo\t \t=\t \t bar\n"- in expected @=? actual+ actual = p2E optLineParser "optLine" " foo = bar\n"+ in+ expected @=? actual -case_optLineParserKeepSpace = let+case_optLineParserDropSpace2 :: Assertion+case_optLineParserDropSpace2 =+ let+ expected = Right $ OptionL "foo" "bar"+ actual = p2E optLineParser "optLine" " \tfoo\t \t=\t \t bar\n"+ in+ expected @=? actual++case_optLineParserKeepSpace :: Assertion+case_optLineParserKeepSpace =+ let expected = Right $ OptionL "foo" "bar \t \t" actual = p2E optLineParser "optLine" "foo\t \t=\t \t bar \t \t\n"- in expected @=? actual+ in+ expected @=? actual -- {{{1 optContParser-case_optContParserSpace = let+case_optContParserSpace :: Assertion+case_optContParserSpace =+ let expected = Right $ OptionContL "foo" actual = p2E optContParser "optCont" " foo\n"- in expected @=? actual+ in+ expected @=? actual -case_optContParserTab = let- expected = Right $OptionContL "foo"+case_optContParserTab :: Assertion+case_optContParserTab =+ let+ expected = Right $ OptionContL "foo" actual = p2E optContParser "optCont" "\tfoo\n"- in expected @=? actual+ in+ expected @=? actual -case_optContParserKeepTrailing = let+case_optContParserKeepTrailing :: Assertion+case_optContParserKeepTrailing =+ let expected = Right $ OptionContL "foo \t\t" actual = p2E optContParser "optCont" "\tfoo \t\t\n"- in expected @=? actual+ in+ expected @=? actual -- {{{1 noiseParser-case_noiseParserEmptyLine = let+case_noiseParserEmptyLine :: Assertion+case_noiseParserEmptyLine =+ let expected = Right CommentL actual = p2E noiseParser "noise" "\n"- in expected @=? actual+ in+ expected @=? actual -case_noiseParserComment = let+case_noiseParserComment1 :: Assertion+case_noiseParserComment1 =+ let expected = Right CommentL actual = p2E noiseParser "noise" "# a comment\n"- in expected @=? actual+ in+ expected @=? actual -case_noiseParserNonEmpty = let+case_noiseParserComment2 :: Assertion+case_noiseParserComment2 =+ let+ expected = Right CommentL+ actual = p2E noiseParser "noise" "; another comment\n"+ in+ expected @=? actual++case_noiseParserNonEmpty :: Assertion+case_noiseParserNonEmpty =+ let expected = Left "bad" actual = p2E noiseParser "noise" " \n"- in expected @=? actual+ in+ expected @=? actual -- {{{1 iniParser--- TBD+case_iniParserEmpty :: Assertion+case_iniParserEmpty = expected @=? actual+ where+ expected = Right []+ actual = p2E iniParser "parsing empty file" "" -- {{{1 buildConfig -- TBD +allTests :: TestTree allTests = $(testGroupGenerator)