config-parser 1.1.0.1 → 1.2.0.0
raw patch · 5 files changed
+101/−14 lines, 5 filesPVP ok
version bump matches the API change (PVP)
API changes (from Hackage documentation)
+ Text.ConfigParser: [keyIdentifier] :: ConfigParser c -> Parser Key
- Text.ConfigParser: ConfigParser :: (forall a. Parser Key -> Parser a -> Parser a) -> [String] -> c -> [ConfigOption c] -> ConfigParser c
+ Text.ConfigParser: ConfigParser :: (forall a. Parser Key -> Parser a -> Parser a) -> [String] -> Parser Key -> c -> [ConfigOption c] -> ConfigParser c
Files
- ChangeLog.md +6/−0
- Text/ConfigParser/Parser.hs +3/−4
- Text/ConfigParser/Types.hs +17/−3
- config-parser.cabal +1/−1
- tests/Parsing.hs +74/−6
ChangeLog.md view
@@ -1,5 +1,11 @@ # Revision history for config-parser +## 1.2.0.0 -- 2018-1-20++* Fixed incorrect parsing error when invalid key appeared at beginning of file.+* Added `keyIdentifier` field to `ConfigParser` to facilitate more accurate+ invalid-key messages.+ ## 1.1.0.1 -- 2018-1-20 * Fixed some unexpected parsing behavior w/r/t line-ends.
Text/ConfigParser/Parser.hs view
@@ -101,12 +101,11 @@ where whitespace = void . many $ oneOf " \t\v\r" :: Parser () line = many $ noneOf "\n\r" :: Parser String- identifier = many1 $ noneOf "\n\r\t\v =" :: Parser String requiredKeys = fmap key . filter required $ options p optionKeysUniq = length (nub $ key <$> options p) == length (options p)- maybeP parser = Just <$> try parser <|> Nothing <$ try identifier+ maybeP parser = Just <$> try parser <|> Nothing <$ try line optionParsers = choice $ try . keyActionP <$> options p- dummyActionP = (Nothing,Nothing) <$ keyValue p identifier (try line)+ dummyActionP = (Nothing,Nothing) <$ keyValue p (keyIdentifier p) (try line) configLineP = (optionParsers <|> try dummyActionP) <* whitespace <* (void newline <|> eof) keyActionP o = (Just o,) <$> mbActionParser o actionParser ConfigOption {..} = keyValue p (P.string key) $ action <$> parser@@ -119,7 +118,7 @@ case mbo of -- Key is bad Nothing -> do - k <- lookAhead identifier+ k <- keyIdentifier p unexpected $ "unknown key " ++ show k -- Key is good Just o@(ConfigOption {..}) -> do
Text/ConfigParser/Types.hs view
@@ -3,7 +3,8 @@ {-# OPTIONS_HADDOCK hide #-} module Text.ConfigParser.Types where -import Text.Parsec (spaces, char)+import Text.Parsec (spaces, char, many1)+import Text.Parsec.Char (noneOf) import Text.Parsec.Text (Parser) type Key = String@@ -27,12 +28,20 @@ data ConfigParser c = ConfigParser { keyValue :: forall a. Parser Key -> Parser a -> Parser a -- ^ Specifies how a key and a value parser should be represented in the- -- config file, e.g., @key = value@, or @key: value@.+ -- config file, e.g., @key = value@, or @key: value@. The first parameter+ -- will be either be @Text.Parsec.string (key (co::ConfigOption c))@ or+ -- @keyIdentifier@. The second parameter will be+ -- @parser (co::ConfigOption c)@. , lineCommentInit :: [String] -- ^ Strings to start a line comment, such as @#@, @--@, or @//@. All -- characters following this string up to the following newline or EOF -- will be removed. You can use the string without starting a comment by -- escaping it with a backslash, e.g. @\\#@ or @\\--@.+ , keyIdentifier :: Parser Key+ -- ^ This is the general form of an identifier to be passed as the first+ -- parameter of @keyValue@. Having a general form allows us to correctly+ -- detect invalid (e.g., typoed) keys. For example, the @keyIdentifier@+ -- corresponding to @defaultKeyValue@ is @many1 (noneOf "\\r\\v\\n\\t =")@. , defaults :: c -- ^ Initial @c@ to fold each 'ConfigOption' action over. , options :: [ConfigOption c]@@ -43,7 +52,12 @@ -- | Smart constructor for a 'ConfigParser' that uses a default syntax like -- @key = value@ and line comments starting with @#@. configParser :: c -> [ConfigOption c] -> ConfigParser c-configParser = ConfigParser defaultKeyValue defaultLineCommentInit+configParser =+ ConfigParser defaultKeyValue defaultLineCommentInit defaultKeyIdentifier++-- | The @keyIdentifier@ corresponding to @defaultKeyValue@.+defaultKeyIdentifier :: Parser Key+defaultKeyIdentifier = many1 $ noneOf "\n\t\r\v =" -- | Default syntax like @key = value@. defaultKeyValue :: Parser Key -> Parser a -> Parser a
config-parser.cabal view
@@ -1,5 +1,5 @@ name: config-parser-version: 1.1.0.1+version: 1.2.0.0 synopsis: Parse config files using parsec and generate parse errors on unhandled keys description: This is yet another entry in Haskell's enourmous collection
tests/Parsing.hs view
@@ -5,14 +5,15 @@ import Prelude hiding (unlines) -import Control.Lens+import Control.Lens hiding (noneOf) import Data.Int (Int8) import Data.List.Extra (isInfixOf) import Data.Text (unlines) import Data.Word (Word8, Word16) import Test.Hspec import Text.Parsec (ParseError, errorPos, sourceLine, sourceColumn)-import Text.Parsec (parse, eof, getInput, spaces)+import Text.Parsec (parse, eof, getInput, spaces, many1)+import Text.Parsec.Char (noneOf) import Text.Parsec.Text (Parser) import qualified Text.Parsec as P (string) @@ -98,10 +99,11 @@ reqcp = configParser defC [co1,co2,co3,co4,co5,co6,co7,co8] badcp = configParser defC [co1,co2,co2,co4,co5,co6,co7] cp = configParser defC [co1,co2,co3,co4,co5,co6,co7]-cp' = ConfigParser lp lcs defC [co1,co2,co3,co4,co5,co6,co7]+cp' = ConfigParser lp lcs kid defC [co1,co2,co3,co4,co5,co6,co7] where lp :: Parser Key -> Parser a -> Parser a lp k q = q <* spaces <* P.string "->" <* spaces <* k+ kid = many1 (noneOf "\n\r") lcs = ["--","//"] shouldFailOnLine :: Show a => Either ParseError a -> Int -> Expectation@@ -638,7 +640,7 @@ , _f4 = ["foo","bar"] , _f5 = [1,2,3,4,5] }- it "parses an multiple comment syntaxes" $+ it "parses multiple comment syntaxes" $ parseFromText cp' "test" (unlines [ "\"foo\" -> f1" , "9001 -> f2"@@ -659,6 +661,26 @@ Right _ -> False Left e -> sourceLine (errorPos e) == 1 && "non-unique keys in ConfigParser" `isInfixOf` show e+ it "errors appropriately when delimiter is missing in middle of file" $+ parseFromText cp "test" "f1 \nf2 = 9001" `shouldSatisfy` \res -> case res of+ Right _ -> False+ Left _ -> True+ it "errors appropriately when delimiter is missing at end of file" $+ parseFromText cp "test" "f1 " `shouldSatisfy` \res -> case res of+ Right _ -> False+ Left e -> sourceLine (errorPos e) == 1+ && sourceColumn (errorPos e) == 4+ && "\"=\"" `isInfixOf` show e+ it "errors appropriately when value is missing in middle of file" $+ parseFromText cp "test" "f1 = \nf2 = 9001" `shouldSatisfy` \res -> case res of+ Right _ -> False+ Left e -> "string in quotes" `isInfixOf` show e+ it "errors appropriately when value is missing at end of file" $+ parseFromText cp "test" "f1 = " `shouldSatisfy` \res -> case res of+ Right _ -> False+ Left e -> sourceLine (errorPos e) == 1+ && sourceColumn (errorPos e) == 6+ && "string in quotes" `isInfixOf` show e it "errors appropriately when full value can't be parsed in middle of file" $ parseFromText cp "test" (unlines [ "f2 = 9001foo"@@ -694,18 +716,64 @@ Right _ -> False Left e -> sourceLine (errorPos e) == 1 && "missing required keys: \"f8\"" `isInfixOf` show e- it "errors on non-existent keys with a pertinent message" $+ it "errors on non-existent key alone with a pertinent message" $+ parseFromText cp "test" "badkey = 9999"+ `shouldSatisfy` \res -> case res of+ Right _ -> False+ Left e -> sourceLine (errorPos e) == 1+ && sourceColumn (errorPos e) == 7+ && "unknown key \"badkey\"" `isInfixOf` show e+ it "errors on non-existent key with initial spaces with a pertinent message" $+ parseFromText cp "test" " \r\t\vbadkey = 9999"+ `shouldSatisfy` \res -> case res of+ Right _ -> False+ Left e -> sourceLine (errorPos e) == 1+ && "unknown key \"badkey\"" `isInfixOf` show e+ it "errors on non-existent key with final spaces with a pertinent message" $+ parseFromText cp "test" "badkey = 9999 \r\t\v"+ `shouldSatisfy` \res -> case res of+ Right _ -> False+ Left e -> sourceLine (errorPos e) == 1+ && sourceColumn (errorPos e) == 7+ && "unknown key \"badkey\"" `isInfixOf` show e+ it "errors on non-existent keys at start of input with a pertinent message" $ parseFromText cp "test" (unlines+ [ "badkey = 9999"+ , "f1 = \"foo\""+ , "f2 = 9001"+ , "f3 = True "+ , "f4 = [\"foo\",\"bar\"]"+ , "f5 = [1,2,3,4,5]"+ ]) `shouldSatisfy` \res -> case res of+ Right _ -> False+ Left e -> sourceLine (errorPos e) == 1+ && sourceColumn (errorPos e) == 7+ && "unknown key \"badkey\"" `isInfixOf` show e+ it "errors on non-existent keys in middle of input with a pertinent message" $+ parseFromText cp "test" (unlines [ "f1 = \"foo\"" , "f2 = 9001" , "f3 = True "+ , "badkey = 9999" , "f4 = [\"foo\",\"bar\"]" , "f5 = [1,2,3,4,5]"+ ]) `shouldSatisfy` \res -> case res of+ Right _ -> False+ Left e -> sourceLine (errorPos e) == 4+ && sourceColumn (errorPos e) == 7+ && "unknown key \"badkey\"" `isInfixOf` show e+ it "errors on non-existent keys at end of input with a pertinent message" $+ parseFromText cp "test" (unlines+ [ "f1 = \"foo\""+ , "f2 = 9001"+ , "f3 = True "+ , "f4 = [\"foo\",\"bar\"]"+ , "f5 = [1,2,3,4,5]" , "badkey = 9999" ]) `shouldSatisfy` \res -> case res of Right _ -> False Left e -> sourceLine (errorPos e) == 6- && sourceColumn (errorPos e) == 1+ && sourceColumn (errorPos e) == 7 && "unknown key \"badkey\"" `isInfixOf` show e it "errors on duplicate keys in config file with a pertinent message" $ parseFromText cp "test" (unlines