packages feed

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 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