packages feed

yaml 0.11.4.0 → 0.11.5.0

raw patch · 5 files changed

+146/−39 lines, 5 files

Files

ChangeLog.md view
@@ -1,5 +1,14 @@ # ChangeLog for yaml +## 0.11.5.0++* New functions capable of parsing YAML streams containing multiple documents into a list of results:+  * `decodeAllEither'`+  * `decodeAllFileEither`+  * `decodeAllFileWithWarnings`+  * `decodeAllThrow`+  * `decodeAllFileThrow`+ ## 0.11.4.0  * add `ToYaml` instance for `String` [#186](https://github.com/snoyberg/yaml/pull/186)
src/Data/Yaml.hs view
@@ -37,6 +37,16 @@     , decodeFileWithWarnings     , decodeThrow     , decodeFileThrow+      -- ** Decoding multiple documents+      --+      -- | For situations where we need to be able to parse multiple documents+      -- separated by `---` in a YAML stream, these functions decode a list of+      -- values rather than a single value.+    , decodeAllEither'+    , decodeAllFileEither+    , decodeAllFileWithWarnings+    , decodeAllThrow+    , decodeAllFileThrow       -- ** More control over decoding     , decodeHelper       -- * Types@@ -193,6 +203,15 @@     -> IO (Either ParseException a) decodeFileEither = fmap (fmap snd) . decodeFileWithWarnings +-- | Like `decodeFileEither`, but decode multiple documents.+--+-- @since 0.11.5.0+decodeAllFileEither+    :: FromJSON a+    => FilePath+    -> IO (Either ParseException [a])+decodeAllFileEither = fmap (fmap snd) . decodeAllFileWithWarnings+ -- | A version of `decodeFileEither` that returns warnings along with the parse -- result. --@@ -203,6 +222,15 @@     -> IO (Either ParseException ([Warning], a)) decodeFileWithWarnings = decodeHelper_ . Y.decodeFile +-- | Like `decodeFileWithWarnings`, but decode multiple documents.+--+-- @since 0.11.5.0+decodeAllFileWithWarnings+    :: FromJSON a+    => FilePath+    -> IO (Either ParseException ([Warning], [a]))+decodeAllFileWithWarnings = decodeAllHelper_ . Y.decodeFile+ decodeEither :: FromJSON a => ByteString -> Either String a decodeEither bs = unsafePerformIO                 $ either (Left . prettyPrintParseException) id@@ -218,17 +246,38 @@               . fmap (fmap snd) . decodeHelper               . Y.decode +-- | Like 'decodeEither'', but decode multiple documents.+--+-- @since 0.11.5.0+decodeAllEither' :: FromJSON a => ByteString -> Either ParseException [a]+decodeAllEither' = either Left (either (Left . AesonException) Right)+                 . unsafePerformIO+                 . fmap (fmap snd) . decodeAllHelper+                 . Y.decode+ -- | A version of 'decodeEither'' lifted to MonadThrow -- -- @since 0.8.31 decodeThrow :: (MonadThrow m, FromJSON a) => ByteString -> m a decodeThrow = either throwM return . decodeEither' +-- | Like `decodeThrow`, but decode multiple documents.+--+-- @since 0.11.5.0+decodeAllThrow :: (MonadThrow m, FromJSON a) => ByteString -> m [a]+decodeAllThrow = either throwM return . decodeAllEither'+ -- | A version of 'decodeFileEither' lifted to MonadIO -- -- @since 0.8.31 decodeFileThrow :: (MonadIO m, FromJSON a) => FilePath -> m a decodeFileThrow f = liftIO $ decodeFileEither f >>= either throwIO return++-- | Like `decodeFileThrow`, but decode multiple documents.+--+-- @since 0.11.5.0+decodeAllFileThrow :: (MonadIO m, FromJSON a) => FilePath -> m [a]+decodeAllFileThrow f = liftIO $ decodeAllFileEither f >>= either throwIO return  -- | Construct a new 'Value' from a list of 'Value's. array :: [Value] -> Value
src/Data/Yaml/Internal.hs view
@@ -11,6 +11,8 @@     , parse     , decodeHelper     , decodeHelper_+    , decodeAllHelper+    , decodeAllHelper_     , textToScientific     , stringScalar     , defaultStringStyle@@ -56,6 +58,7 @@ import Data.Text.Encoding.Error (lenientDecode) import Data.Typeable import qualified Data.Vector as V+import Data.Void (Void)  import qualified Text.Libyaml as Y import Text.Libyaml hiding (encode, decode, encodeFile, decodeFile)@@ -66,6 +69,7 @@                                       , _expected :: Maybe Event                                       }                     | InvalidYaml (Maybe YamlException)+                    | MultipleDocuments                     | AesonException String                     | OtherParseException SomeException                     | NonStringKey JSONPath@@ -115,6 +119,7 @@             _  -> ",\n" ++ context ++ ":\n"         , problem         ]+  MultipleDocuments -> "Multiple YAML documents encountered"   AesonException s -> "Aeson exception:\n" ++ s   OtherParseException exc -> "Generic parse exception:\n" ++ show exc   NonStringKey path -> formatError path "Non-string keys are not supported"@@ -158,24 +163,34 @@  parse :: ReaderT JSONPath (ConduitM Event o Parse) Value parse = do+    docs <- parseAll+    case docs of+        [] -> return Null+        [doc] -> return doc+        _ -> liftIO $ throwIO MultipleDocuments++parseAll :: ReaderT JSONPath (ConduitM Event o Parse) [Value]+parseAll = do     streamStart <- lift CL.head     case streamStart of         Nothing ->             -- empty string input-            return Null-        Just EventStreamStart -> do-            documentStart <- lift CL.head-            case documentStart of-                Just EventStreamEnd ->-                    -- empty file input, comment only string/file input-                    return Null-                Just EventDocumentStart -> do-                    res <- parseO-                    requireEvent EventDocumentEnd-                    requireEvent EventStreamEnd-                    return res-                _ -> liftIO $ throwIO $ UnexpectedEvent documentStart Nothing-        _ -> liftIO $ throwIO $ UnexpectedEvent streamStart Nothing+            return []+        Just EventStreamStart ->+            -- empty file input, comment only string/file input+            parseDocs+        _ -> missed streamStart+  where+    parseDocs = do+        documentStart <- lift CL.head+        case documentStart of+            Just EventStreamEnd -> return []+            Just EventDocumentStart -> do+                res <- parseO+                requireEvent EventDocumentEnd+                (res :) <$> parseDocs+            _ -> missed documentStart+    missed event = liftIO $ throwIO $ UnexpectedEvent event Nothing  parseScalar :: ByteString -> Anchor -> Style -> Tag             -> ReaderT JSONPath (ConduitM Event o Parse) Text@@ -283,36 +298,56 @@            merge xs = (Set.fromList (M.keys xs \\ M.keys front), M.union front xs) +parseSrc :: ReaderT JSONPath (ConduitM Event Void Parse) val+         -> ConduitM () Event Parse ()+         -> IO (val, ParseState)+parseSrc eventParser src = runResourceT $ runStateT+    (runConduit $ src .| runReaderT eventParser [])+    (ParseState Map.empty [])++mkHelper :: ReaderT JSONPath (ConduitM Event Void Parse) val -- ^ parse libyaml events as Value or [Value]+         -> (SomeException -> IO (Either ParseException a))  -- ^ what to do with unhandled exceptions+         -> ((val, ParseState) -> Either ParseException a)   -- ^ further transform and parse results+         -> ConduitM () Event Parse ()                       -- ^ the libyaml event (string/file) source+         -> IO (Either ParseException a)+mkHelper eventParser onOtherExc extractResults src = catches+    (extractResults <$> parseSrc eventParser src)+    [ Handler $ \pe -> return $ Left (pe :: ParseException)+    , Handler $ \ye -> return $ Left $ InvalidYaml $ Just (ye :: YamlException)+    , Handler $ \sae -> throwIO (sae :: SomeAsyncException)+    , Handler onOtherExc+    ]+ decodeHelper :: FromJSON a              => ConduitM () Y.Event Parse ()              -> IO (Either ParseException ([Warning], Either String a))-decodeHelper src = do-    -- This used to be tryAny, but the fact is that catching async-    -- exceptions is fine here. We'll rethrow them immediately in the-    -- otherwise clause.-    x <- try $ runResourceT $ runStateT (runConduit $ src .| runReaderT parse []) (ParseState Map.empty [])-    case x of-        Left e-            | Just pe <- fromException e -> return $ Left pe-            | Just ye <- fromException e -> return $ Left $ InvalidYaml $ Just (ye :: YamlException)-            | otherwise -> throwIO e-        Right (y, st) -> return $ Right (parseStateWarnings st, parseEither parseJSON y)+decodeHelper = mkHelper parse throwIO $ \(v, st) ->+    Right (parseStateWarnings st, parseEither parseJSON v) +decodeAllHelper :: FromJSON a+                => ConduitM () Event Parse ()+                -> IO (Either ParseException ([Warning], Either String [a]))+decodeAllHelper = mkHelper parseAll throwIO $ \(vs, st) ->+    Right (parseStateWarnings st, mapM (parseEither parseJSON) vs)++catchLeft :: SomeException -> IO (Either ParseException a)+catchLeft = return . Left . OtherParseException+ decodeHelper_ :: FromJSON a               => ConduitM () Event Parse ()               -> IO (Either ParseException ([Warning], a))-decodeHelper_ src = do-    x <- try $ runResourceT $ runStateT (runConduit $ src .| runReaderT parse []) (ParseState Map.empty [])-    case x of-        Left e-            | Just pe <- fromException e -> return $ Left pe-            | Just ye <- fromException e -> return $ Left $ InvalidYaml $ Just (ye :: YamlException)-            | Just sae <- fromException e -> throwIO (sae :: SomeAsyncException)-            | otherwise -> return $ Left $ OtherParseException e-        Right (y, st) -> return $ either-            (Left . AesonException)-            Right-            ((,) (parseStateWarnings st) <$> parseEither parseJSON y)+decodeHelper_ = mkHelper parse catchLeft $ \(v, st) ->+    case parseEither parseJSON v of+        Left e -> Left $ AesonException e+        Right x -> Right (parseStateWarnings st, x)++decodeAllHelper_ :: FromJSON a+                 => ConduitM () Event Parse ()+                 -> IO (Either ParseException ([Warning], [a]))+decodeAllHelper_ = mkHelper parseAll catchLeft $ \(vs, st) ->+    case mapM (parseEither parseJSON) vs of+        Left e -> Left $ AesonException e+        Right xs -> Right (parseStateWarnings st, xs)  type StringStyle = Text -> ( Tag, Style ) 
test/Data/YamlSpec.hs view
@@ -69,6 +69,11 @@     actual <- D.decodeThrow bs     actual `shouldBe` expected +shouldDecodeAll :: (Show a, D.FromJSON a, Eq a) => B8.ByteString -> [a] -> IO ()+shouldDecodeAll bs expected = do+    actual <- D.decodeAllThrow bs+    actual `shouldBe` expected+ shouldDecodeEvents :: B8.ByteString -> [Y.Event] -> IO () shouldDecodeEvents bs expected = do     actual <- runConduitRes $ Y.decode bs .| CL.consume@@ -193,6 +198,15 @@             it "returns Left" $ do                 (D.decodeFileEither "./does_not_exist.yaml" :: IO (Either D.ParseException D.Value)) >>= (`shouldSatisfy` isLeft) +    describe "multiple document support" $ do+        it "decodes zero-length input" $+            "" `shouldDecodeAll` ([] :: [D.Value])+        it "decodes comment-only input" $+            "# foo\n# bar" `shouldDecodeAll` ([] :: [D.Value])+        it "decodes a single document stream" $+            "foo: true" `shouldDecodeAll` [object ["foo" .= True]]+        it "decodes multiple documents" $+            "--- 1\n--- 2" `shouldDecodeAll` [D.Number 1, D.Number 2]      describe "round-tripping of special scalars" $ do         let special = words "y Y On ON false 12345 12345.0 12345a 12e3"
yaml.cabal view
@@ -4,10 +4,10 @@ -- -- see: https://github.com/sol/hpack ----- hash: 1f75743c2fefefb33f6f3e8a51fce90cb3f286c975d205d7ded3789d5b15c488+-- hash: 55c85c8d4d3074a558a82e30a2592ecff9db2e6f1571547c73d26ba44bfc1c20  name:           yaml-version:        0.11.4.0+version:        0.11.5.0 synopsis:       Support for parsing and rendering YAML documents. description:    README and API documentation are available at <https://www.stackage.org/package/yaml> category:       Data