packages feed

data-object-yaml 0.2.0.1 → 0.3.0

raw patch · 3 files changed

+383/−198 lines, 3 filesdep +MonadCatchIO-transformersdep +containersdep +iterateedep −QuickCheckdep −attemptdep −test-framework-quickcheckdep ~convertible-textdep ~data-objectdep ~failurePVP ok

version bump matches the API change (PVP)

Dependencies added: MonadCatchIO-transformers, containers, iteratee

Dependencies removed: QuickCheck, attempt, test-framework-quickcheck

Dependency ranges changed: convertible-text, data-object, failure, transformers, yaml

API changes (from Hackage documentation)

- Data.Object.Yaml: instance Applicative YAttemptIO
- Data.Object.Yaml: instance Exception UnexpectedEvent
- Data.Object.Yaml: instance Failure YamlException YAttemptIO
- Data.Object.Yaml: instance Functor YAttemptIO
- Data.Object.Yaml: instance Monad YAttemptIO
- Data.Object.Yaml: instance MonadIO YAttemptIO
- Data.Object.Yaml: instance Show UnexpectedEvent
- Data.Object.Yaml: instance Typeable UnexpectedEvent
- Data.Object.Yaml: instance With YAttemptIO
+ Data.Object.Yaml: fromYamlObject :: (IsYamlScalar k) => (IsYamlScalar v) => YamlObject -> Object k v
+ Data.Object.Yaml: instance (Monad m) => Monad (PErrorT m)
+ Data.Object.Yaml: instance (MonadCatchIO m) => MonadCatchIO (PErrorT m)
+ Data.Object.Yaml: instance (MonadIO m) => MonadIO (PErrorT m)
+ Data.Object.Yaml: instance Data YamlScalar
+ Data.Object.Yaml: instance MonadTrans PErrorT
+ Data.Object.Yaml: instance Read YamlScalar
+ Data.Object.Yaml: instance Typeable YamlScalar
+ Data.Object.Yaml: toYamlObject :: (IsYamlScalar k) => (IsYamlScalar v) => Object k v -> YamlObject
- Data.Object.Yaml: class IsYamlScalar a
+ Data.Object.Yaml: class (Eq a) => IsYamlScalar a
- Data.Object.Yaml: decode :: (MonadFailure YamlException m, IsYamlScalar k, IsYamlScalar v) => ByteString -> m (Object k v)
+ Data.Object.Yaml: decode :: (Failure ParseException m, IsYamlScalar k, IsYamlScalar v) => ByteString -> m (Object k v)
- Data.Object.Yaml: decodeFile :: (MonadFailure YamlException m, IsYamlScalar k, IsYamlScalar v, With m) => FilePath -> m (Object k v)
+ Data.Object.Yaml: decodeFile :: (Failure ParseException m, IsYamlScalar k, IsYamlScalar v) => FilePath -> IO (m (Object k v))
- Data.Object.Yaml: encodeFile :: (IsYamlScalar k, IsYamlScalar v, MonadFailure YamlException m, With m) => FilePath -> Object k v -> m ()
+ Data.Object.Yaml: encodeFile :: (IsYamlScalar k, IsYamlScalar v) => FilePath -> Object k v -> IO ()

Files

Data/Object/Yaml.hs view
@@ -10,39 +10,43 @@     , YamlObject       -- * Automatic scalar conversions     , IsYamlScalar (..)+    , toYamlObject+    , fromYamlObject       -- * Encoding/decoding     , encode     , encodeFile     , decode     , decodeFile-#if TEST-    , testSuite-#endif     ) where  import qualified Text.Libyaml as Y import Text.Libyaml hiding (encode, decode, encodeFile, decodeFile) import Data.Object import Data.ByteString (ByteString)+import qualified Data.Map as Map import System.IO.Unsafe (unsafePerformIO)-import Control.Exception (Exception, SomeException (..))-import Data.Typeable (Typeable)--- debugging purposes import Debug.Trace import Control.Failure-import Control.Applicative+ import qualified Data.Text import qualified Data.Text.Lazy-import "transformers" Control.Monad.Trans-import Control.Monad+import qualified Data.ByteString+import qualified Data.ByteString.Lazy -#if TEST-import Test.Framework (testGroup, Test)-import Test.Framework.Providers.HUnit---import Test.Framework.Providers.QuickCheck (testProperty)-import Test.HUnit hiding (Test, path)---import Test.QuickCheck-import qualified Data.ByteString.Char8 as B8+import Data.Convertible.Text (cs)+import Data.Data++#if MIN_VERSION_transformers(0,2,0)+import "transformers" Control.Monad.Trans.Class+import "transformers" Control.Monad.IO.Class+#else+import "transformers" Control.Monad.Trans #endif+import "transformers" Control.Monad.Trans.State+import Control.Monad+import Data.Iteratee hiding (foldl)+import qualified Data.Iteratee as I+import Control.Monad.CatchIO hiding (try)+import Prelude hiding (catch)  -- | Equality depends on 'value' and 'tag', not 'style'. data YamlScalar = YamlScalar@@ -50,13 +54,13 @@     , tag :: Tag     , style :: Style     }-    deriving (Show)+    deriving (Show, Read, Data, Typeable) instance Eq YamlScalar where     (YamlScalar v t _) == (YamlScalar v' t' _) = v == v' && t == t'  type YamlObject = Object YamlScalar YamlScalar -class IsYamlScalar a where+class (Eq a) => IsYamlScalar a where     fromYamlScalar :: YamlScalar -> a     toYamlScalar :: a -> YamlScalar instance IsYamlScalar YamlScalar where@@ -71,203 +75,231 @@ instance IsYamlScalar [Char] where     fromYamlScalar = cs . value     toYamlScalar s = YamlScalar (cs s) NoTag Any-instance IsYamlScalar ByteString where+instance IsYamlScalar Data.ByteString.ByteString where     fromYamlScalar = value     toYamlScalar b = YamlScalar b NoTag Any+instance IsYamlScalar Data.ByteString.Lazy.ByteString where+    fromYamlScalar = cs . value+    toYamlScalar b = YamlScalar (cs b) NoTag Any +-- | Merge assoc-lists by keys.+-- First list overrides second:+--     [(k1, x), (k2, y)] `mergeAssocLists` [(k3, z)] == [(k1, x), (k2, y), (k3, z)]+--     [(k1, x), (k2, y)] `mergeAssocLists` [(k2, z)] == [(k1, x), (k2, y)]+mergeAssocLists :: (Eq k) => [(k, v)] -> [(k, v)] -> [(k, v)]+mergeAssocLists a [] = a+mergeAssocLists [] b = b+mergeAssocLists a ((bk, bv):bs) =+    case lookup bk a of+      Nothing -> mergeAssocLists ((bk, bv) : a) bs+      Just _  -> mergeAssocLists a bs++toYamlObject :: IsYamlScalar k+             => IsYamlScalar v+             => Object k v+             -> YamlObject+toYamlObject = mapKeysValues toYamlScalar toYamlScalar++fromYamlObject :: IsYamlScalar k+               => IsYamlScalar v+               => YamlObject+               -> Object k v+fromYamlObject = mapKeysValues fromYamlScalar fromYamlScalar+ encode :: (IsYamlScalar k, IsYamlScalar v) => Object k v -> ByteString-encode = unsafePerformIO . Y.encode . ge+encode obj =+    unsafePerformIO+    (enumPure1Chunk+        (objToEvents $ toYamlObject obj)+        Y.encode >>= run) -encodeFile :: (IsYamlScalar k, IsYamlScalar v, MonadFailure YamlException m,-               With m)+encodeFile :: (IsYamlScalar k, IsYamlScalar v)            => FilePath            -> Object k v-           -> m ()-encodeFile fp = Y.encodeFile fp . ge+           -> IO ()+encodeFile fp obj =+    enumPure1Chunk (objToEvents $ toYamlObject obj) (Y.encodeFile fp)+    >>= run -emitEvents :: (MonadIO m, MonadFailure YamlException m)-           => Event -> Event -> YamlEncoder m () -> YamlEncoder m ()-emitEvents start stop body = emitEvent start >> body >> emitEvent stop+objToEvents :: YamlObject -> [Y.Event]+objToEvents o = (:) EventStreamStart+              . (:) EventDocumentStart+              $ objToEvents' o+              [ EventDocumentEnd+              , EventStreamEnd+              ] -ge :: (MonadIO m, MonadFailure YamlException m, IsYamlScalar k,-       IsYamlScalar v)-   => Object k v-   -> YamlEncoder m ()-ge yo = emitEvents EventStreamStart EventStreamEnd-      $ emitEvents EventDocumentStart EventDocumentEnd-      $ geO yo+scalarToEvent :: YamlScalar -> Event+scalarToEvent (YamlScalar v t s) = EventScalar v t s Nothing -geO :: (MonadIO m, MonadFailure YamlException m, IsYamlScalar k,-        IsYamlScalar v)-    => Object k v-    -> YamlEncoder m ()-geO (Scalar s) = geS s-geO (Sequence yos)  = emitEvents EventSequenceStart EventSequenceEnd-                    $ mapM_ geO yos-geO (Mapping pairs) = emitEvents EventMappingStart EventMappingEnd-                    $ mapM_ gePair pairs+objToEvents' :: YamlObject -> [Y.Event] -> [Y.Event]+objToEvents' (Scalar s) rest = scalarToEvent s : rest+objToEvents' (Sequence list) rest =+    EventSequenceStart Nothing+  : foldr ($) (EventSequenceEnd : rest) (map objToEvents' list)+objToEvents' (Mapping pairs) rest =+    EventMappingStart Nothing+  : foldr ($) (EventMappingEnd : rest) (map pairToEvents pairs) -gePair :: (MonadIO m, MonadFailure YamlException m, IsYamlScalar k,-           IsYamlScalar v)-       => (k, Object k v)-       -> YamlEncoder m ()-gePair (ys, yo) = geS ys >> geO yo+pairToEvents :: (YamlScalar, YamlObject) -> [Y.Event] -> [Y.Event]+pairToEvents (k, v) rest =+    scalarToEvent k+  : objToEvents' v rest -geS :: (MonadIO m, IsYamlScalar a, MonadFailure YamlException m)-    => a-    -> YamlEncoder m ()-geS = emitEvent . toEventScalar . toYamlScalar+-- Parsing -toEventScalar :: YamlScalar -> Event-toEventScalar (YamlScalar v t s) = EventScalar v t s+data ParseException = NonScalarKey+                    | UnknownAlias { _anchorName :: Y.AnchorName }+                    | UnexpectedEvent { _received :: Maybe Event+                                      , _expected :: Maybe Event+                                      }+                    | InvalidYaml (Maybe ErrMsg)+    deriving (Show, Typeable)+instance Exception ParseException -decode :: (MonadFailure YamlException m, IsYamlScalar k, IsYamlScalar v)-       => ByteString-       -> m (Object k v)-decode bs = try $ unsafePerformIO $ unYAttemptIO $ Y.decode bs parse+newtype PErrorT m a = PErrorT { runPErrorT :: m (Either ParseException a) }+instance Monad m => Monad (PErrorT m) where+    return = PErrorT . return . Right+    (PErrorT m) >>= f = PErrorT $ do+        e <- m+        case e of+            Left e' -> return $ Left e'+            Right a -> runPErrorT $ f a+instance MonadTrans PErrorT where+    lift = PErrorT . liftM Right+instance MonadIO m => MonadIO (PErrorT m) where+    liftIO = lift . liftIO+instance MonadCatchIO m => MonadCatchIO (PErrorT m) where+  m `catch` f = mapPErrorT (\m' -> m' `catch` \e -> runPErrorT $ f e) m+  block       = mapPErrorT block+  unblock     = mapPErrorT unblock -newtype YAttemptIO v = YAttemptIO-    { unYAttemptIO :: IO (Either YamlException v)-    }-instance Monad YAttemptIO where-    return = YAttemptIO . return . Right-    (YAttemptIO io) >>= f = YAttemptIO $ do-        x <- io-        case x of-            Left e -> return $ Left e-            Right y -> unYAttemptIO $ f y-instance Functor YAttemptIO where-    fmap = liftM-instance Applicative YAttemptIO where-    pure = return-    (<*>) = ap-instance Failure YamlException YAttemptIO where-    failure = YAttemptIO . return . Left-instance MonadIO YAttemptIO where-    liftIO = YAttemptIO . fmap Right-instance With YAttemptIO where-    with orig = YAttemptIO . orig . (unYAttemptIO .)+mapPErrorT :: (m (Either ParseException a) -> n (Either ParseException b))+           -> PErrorT m a+           -> PErrorT n b+mapPErrorT f m = PErrorT $ f (runPErrorT m) -decodeFile :: (MonadFailure YamlException m, IsYamlScalar k, IsYamlScalar v,-               With m)-           => FilePath-           -> m (Object k v)-decodeFile fp = Y.decodeFile fp parse+pfailure :: Monad m => ParseException -> PErrorT m a+pfailure = PErrorT . return . Left -requireEvent :: (With m, MonadFailure YamlException m)-             => Event-             -> YamlDecoder m ()-requireEvent e = do-    e' <- parseEvent-    unless (e == e')-        $ failure $ YamlOtherException $ SomeException-        $ UnexpectedEvent e' $ Just e+type Parser = PErrorT (StateT (Map.Map String YamlObject) IO) -data UnexpectedEvent = UnexpectedEvent-    { _received :: Event-    , _expected :: Maybe Event-    }-    deriving (Show, Typeable)-instance Exception UnexpectedEvent+requireEvent :: Event -> IterateeG [] Event Parser ()+requireEvent e = do+    f <- peek+    if f == Just e+        then I.drop 1+        else lift $ pfailure $ UnexpectedEvent f $ Just e -parse :: (With m, MonadFailure YamlException m, IsYamlScalar k,-          IsYamlScalar v)-      => YamlDecoder m (Object k v)+parse :: IterateeG [] Event Parser YamlObject parse = do     requireEvent EventStreamStart     requireEvent EventDocumentStart-    e <- parseEvent-    res <- parseO e+    res <- parseO     requireEvent EventDocumentEnd     requireEvent EventStreamEnd-    requireEvent EventNone     return res -parseO :: (IsYamlScalar k, IsYamlScalar v, With m,-           MonadFailure YamlException m)-       => Event-       -> YamlDecoder m (Object k v)-parseO (EventScalar v t s) =-    return $ Scalar $ fromYamlScalar $ YamlScalar v t s-parseO EventSequenceStart = parseS id-parseO EventMappingStart = parseM id-parseO e = failure $ YamlOtherException $ SomeException-                   $ UnexpectedEvent e Nothing--parseS :: (IsYamlScalar k, IsYamlScalar v, With m,-           MonadFailure YamlException m)-       => ([Object k v] -> [Object k v])-       -> YamlDecoder m (Object k v)-parseS front = do-    e <- parseEvent-    case e of-        EventSequenceEnd -> return $ Sequence $ front []-        _ -> do-            o <- parseO e-            parseS $ front . (:) o--parseM :: (IsYamlScalar k, IsYamlScalar v, With m,-           MonadFailure YamlException m)-       => ([(k, Object k v)] -> [(k, Object k v)])-       -> YamlDecoder m (Object k v)-parseM front = do-    e <- parseEvent-    case e of-        EventMappingEnd -> return $ Mapping $ front []-        EventScalar v' t s -> do-            let k = fromYamlScalar $ YamlScalar v' t s-            v <- parseEvent >>= parseO-            parseM $ front . (:) (k, v)-        _ -> failure $ YamlOtherException-                     $ SomeException NonScalarKey--data ParseException = NonScalarKey-    deriving (Show, Typeable)-instance Exception ParseException--#if TEST-mkScalar :: String -> YamlScalar-mkScalar s = YamlScalar (cs s) StrTag Folded+safeHead :: IterateeG [] Event Parser (Maybe Event)+safeHead = do+    x <- peek+    I.drop 1+    return x -sample :: YamlObject-sample = Sequence-    [ Scalar $ mkScalar "foo"-    , Mapping-        [ (mkScalar "bar1", Scalar $ mkScalar "bar2")-        ]-    ]+parseScalar :: ByteString -> Tag -> Style -> Anchor+            -> IterateeG [] Event Parser YamlScalar+parseScalar v t s a = do+    let res = YamlScalar v t s+    case a of+        Nothing -> return res+        Just an -> do+            lift $ lift $ modify (Map.insert an $ Scalar res)+            return res -sampleStr :: Object String String-sampleStr = mapKeysValues fromYamlScalar fromYamlScalar sample+parseO :: IterateeG [] Event Parser YamlObject+parseO = do+    me <- safeHead+    case me of+        Just (EventScalar v t s a) -> Scalar `liftM` parseScalar v t s a+        Just (EventSequenceStart a) -> parseS a id+        Just (EventMappingStart a) -> parseM a id+        Just (EventAlias an) -> do+            m <- lift $ lift get+            case Map.lookup an m of+                Nothing -> lift $ pfailure $ UnknownAlias an+                Just v -> return v+        _ -> lift $ pfailure $ UnexpectedEvent me Nothing -testSuite :: Test-testSuite = testGroup "Data.Object.Yaml"-    [ testCase "encode/decode" caseEncodeDecode-    , testCase "encode/decode file" caseEncodeDecodeFile-    , testCase "encode/decode strings" caseEncodeDecodeStrings-    , testCase "decode invalid file" caseDecodeInvalid-    ]+parseS :: Y.Anchor+       -> ([YamlObject] -> [YamlObject])+       -> IterateeG [] Event Parser YamlObject+parseS a front = do+    me <- peek+    case me of+        Just EventSequenceEnd -> do+            I.drop 1+            let res = Sequence $ front []+            case a of+                Nothing -> return res+                Just an -> do+                    lift $ lift $ modify $ Map.insert an res+                    return res+        _ -> do+            o <- parseO+            parseS a $ front . (:) o -caseEncodeDecode :: Assertion-caseEncodeDecode = do-    out <- decode $ encode sample-    out @?= sample+parseM :: Y.Anchor+       -> ([(YamlScalar, YamlObject)] -> [(YamlScalar, YamlObject)])+       -> IterateeG [] Event Parser YamlObject+parseM a front = do+    me <- peek+    case me of+        Just EventMappingEnd -> do+            I.drop 1+            let res = Mapping $ front []+            case a of+                Nothing -> return res+                Just an -> do+                    lift $ lift $ modify $ Map.insert an res+                    return res+        _ -> do+            me' <- safeHead+            s <- case me' of+                    Just (EventScalar v t s a') -> parseScalar v t s a'+                    _ -> lift $ pfailure $ UnexpectedEvent me' Nothing+            o <- parseO+            let al  = mergeAssocLists [(s, o)] $ front []+                al' = if fromYamlScalar s == "<<"+                         then case o of+                                  Scalar _    -> al+                                  Mapping l  -> mergeAssocLists al l+                                  Sequence l -> mergeAssocLists al $ foldl merge' [] l+                         else al+            parseM a (`mergeAssocLists` al')+    where merge' :: (Eq k) => [(k, Object k v)] -> Object k v -> [(k, Object k v)]+          merge' al (Mapping om) = mergeAssocLists al om+          merge' al _            = al -caseEncodeDecodeFile :: Assertion-caseEncodeDecodeFile = do-    let fp = "tmp.yaml"-    encodeFile fp sample-    out <- decodeFile fp-    out @?= sample+decode :: (Failure ParseException m, IsYamlScalar k, IsYamlScalar v)+       => ByteString+       -> m (Object k v)+decode bs = try $ unsafePerformIO $ run' $ joinIM $ Y.decode bs parse -caseEncodeDecodeStrings :: Assertion-caseEncodeDecodeStrings = do-    out <- decode $ encode sampleStr-    out @?= sampleStr+decodeFile :: (Failure ParseException m, IsYamlScalar k, IsYamlScalar v)+           => FilePath+           -> IO (m (Object k v))+decodeFile fp = fmap try $ run' $ joinIM $ Y.decodeFile fp parse -caseDecodeInvalid :: Assertion-caseDecodeInvalid = do-    let invalid = B8.pack "\tthis is 'not' valid :-)"-    Nothing @=? (decode invalid :: Maybe YamlObject)+run' :: (IsYamlScalar k, IsYamlScalar v)+     => IterateeG [] Event Parser YamlObject+     -> IO (Either ParseException (Object k v))+run' iter = do+    let mmmitergv = runIter iter $ EOF Nothing+        mmitergv = runPErrorT mmmitergv+        mitergv = evalStateT mmitergv Map.empty+    itergv <- mitergv+    case itergv of+        Left e -> return $ Left e+        Right (Done x _) -> return $ Right $ fromYamlObject x+        Right (Cont _ e) -> return $ Left $ InvalidYaml e -#endif
data-object-yaml.cabal view
@@ -1,8 +1,8 @@ name:            data-object-yaml-version:         0.2.0.1+version:         0.3.0 license:         BSD3 license-file:    LICENSE-author:          Michael Snoyman <michael@snoyman.com>+author:          Michael Snoyman <michael@snoyman.com>, Anton Ageev <antage@gmail.com> maintainer:      Michael Snoyman <michael@snoyman.com> synopsis:        Serialize data to and from Yaml files description:     Provides high level conversions based on the data-object package. Parsing and emitting is handled by the yaml package, which in turn uses the libyaml C library.@@ -18,14 +18,16 @@  library     build-depends:   base >= 4 && < 5,-                     data-object >= 0.2.0 && < 0.3,+                     data-object >= 0.3.0 && < 0.4,                      bytestring >= 0.9.1.4 && < 0.10,                      text >= 0.7 && < 0.8,-                     convertible-text >= 0.2.0 && < 0.3,-                     attempt >= 0.2.0 && < 0.3,-                     failure >= 0.0.0 && < 0.1,-                     transformers >= 0.1.4.0 && < 0.2,-                     yaml >= 0.2.0 && < 0.3+                     failure >= 0.1.0 && < 0.2,+                     transformers >= 0.1.4.0 && < 0.3,+                     yaml >= 0.3.0 && < 0.4,+                     containers >= 0.2.0.0 && < 0.4,+                     iteratee >= 0.3.5 && < 0.4,+                     MonadCatchIO-transformers >= 0.2.2 && < 0.3,+                     convertible-text >= 0.3.0 && < 0.4     exposed-modules: Data.Object.Yaml     ghc-options:     -Wall @@ -34,10 +36,8 @@         Buildable: True         cpp-options:     -DTEST         build-depends:   test-framework,-                         test-framework-quickcheck,                          test-framework-hunit,-                         HUnit,-                         QuickCheck >= 1 && < 2+                         HUnit     else         Buildable: False     ghc-options:     -Wall
runtests.hs view
@@ -1,8 +1,161 @@-import Test.Framework (defaultMain)+{-# LANGUAGE NamedFieldPuns #-}+import Test.Framework (Test, defaultMain, testGroup)+import Test.Framework.Providers.HUnit+import Test.HUnit hiding (Test) -import qualified Data.Object.Yaml+import Data.Maybe+import qualified Data.ByteString.Char8 as B8+import Data.Convertible.Text (cs) +import qualified Text.Libyaml as LY++import Data.Object+import Data.Object.Yaml++import Control.Monad++mkFoldedScalar :: String -> YamlScalar+mkFoldedScalar s = YamlScalar (cs s) LY.StrTag LY.Folded++mkScalar :: String -> YamlScalar+mkScalar s = YamlScalar (cs s) LY.NoTag LY.Plain++mkStrScalar :: String -> YamlScalar+mkStrScalar s = YamlScalar (cs s) LY.StrTag LY.Plain++mappingKey :: YamlObject -> String -> YamlObject+mappingKey (Mapping m) k = (fromJust . lookup (mkScalar k) $ m)+mappingKey _ _ = error "expected Mapping"++decodeYaml :: String -> Maybe YamlObject+decodeYaml s = decode $ B8.pack s++sample :: YamlObject+sample = Sequence+    [ Scalar $ mkFoldedScalar "foo"+    , Mapping+        [ (mkFoldedScalar "bar1", Scalar $ mkFoldedScalar "bar2")+        ]+    ]++sampleStr :: Object String String+sampleStr = mapKeysValues fromYamlScalar fromYamlScalar sample+ main :: IO () main = defaultMain-    [ Data.Object.Yaml.testSuite+    [ testSuite+    , testSuiteOfAliases+    , testSuiteOfMergeKeys     ]++testSuite :: Test+testSuite = testGroup "Data.Object.Yaml"+    [ testCase "encode/decode" caseEncodeDecode+    , testCase "encode/decode file" caseEncodeDecodeFile+    , testCase "encode/decode strings" caseEncodeDecodeStrings+    , testCase "decode invalid file" caseDecodeInvalid+    ]++caseEncodeDecode :: Assertion+caseEncodeDecode = do+    out <- decode $ encode sample+    out @?= sample++caseEncodeDecodeFile :: Assertion+caseEncodeDecodeFile = do+    let fp = "tmp.yaml"+    encodeFile fp sample+    out <- join $ decodeFile fp+    out @?= sample++caseEncodeDecodeStrings :: Assertion+caseEncodeDecodeStrings = do+    out <- decode $ encode $ toYamlObject sampleStr+    fromYamlObject out @?= sampleStr++caseDecodeInvalid :: Assertion+caseDecodeInvalid = do+    let invalid = B8.pack "\tthis is 'not' valid :-)"+    Nothing @=? (decode invalid :: Maybe YamlObject)+++testSuiteOfAliases :: Test+testSuiteOfAliases = testGroup "Tests of aliases"+    [ testCase "simple scalar alias" caseSimpleScalarAlias+    , testCase "simple sequence alias" caseSimpleSequenceAlias+    , testCase "simple mapping alias" caseSimpleMappingAlias+    , testCase "mapping alias before anchor" caseMappingAliasBeforeAnchor+    , testCase "mapping alias inside anchor" caseMappingAliasInsideAnchor+    , testCase "scalar alias overriding" caseScalarAliasOverriding+    ]++caseSimpleScalarAlias :: Assertion+caseSimpleScalarAlias = do+    let maybeRes = decodeYaml "- &anch foo\n- baz\n- *anch"+    isJust maybeRes @? "decoder should return Just YamlObject but returned Nothing"+    let res = fromJust maybeRes+    res @?= Sequence [Scalar (mkScalar "foo"), Scalar (mkScalar "baz"), Scalar (mkScalar "foo")]++caseSimpleSequenceAlias :: Assertion+caseSimpleSequenceAlias = do+    let maybeRes = decodeYaml "seq: &anch\n  - foo\n  - baz\nseq2: *anch"+    isJust maybeRes @? "decoder should return Just YamlObject but returned Nothing"+    let res = fromJust maybeRes+    res @?= Mapping [(mkScalar "seq", Sequence [Scalar (mkScalar "foo"), Scalar (mkScalar "baz")]), (mkScalar "seq2", Sequence [Scalar (mkScalar "foo"), Scalar (mkScalar "baz")])]++caseSimpleMappingAlias :: Assertion+caseSimpleMappingAlias = do+    let maybeRes = decodeYaml "map: &anch\n  key1: foo\n  key2: baz\nmap2: *anch"+    isJust maybeRes @? "decoder should return Just YamlObject but returned Nothing"+    let res = fromJust maybeRes+    res @?= Mapping [(mkScalar "map", Mapping [(mkScalar "key1", Scalar (mkScalar "foo")), (mkScalar "key2", Scalar (mkScalar "baz"))]), (mkScalar "map2", Mapping [(mkScalar "key1", Scalar (mkScalar "foo")), (mkScalar "key2", Scalar (mkScalar "baz"))])]++caseMappingAliasBeforeAnchor :: Assertion+caseMappingAliasBeforeAnchor = do+    let res = decodeYaml "map: *anch\nmap2: &anch\n  key1: foo\n  key2: baz"+    isNothing res @? "decode should return Nothing due to unknown alias"++caseMappingAliasInsideAnchor :: Assertion+caseMappingAliasInsideAnchor = do+    let res = decodeYaml "map: &anch\n  key1: foo\n  key2: *anch"+    isNothing res @? "decode should return Nothing due to unknown alias"++caseScalarAliasOverriding :: Assertion+caseScalarAliasOverriding = do+    let maybeRes = decodeYaml "- &anch foo\n- baz\n- *anch\n- &anch boo\n- buz\n- *anch"+    isJust maybeRes @? "decoder should return Just YamlObject but returned Nothing"+    let res = fromJust maybeRes+    res @?= Sequence [Scalar (mkScalar "foo"), Scalar (mkScalar "baz"), Scalar (mkScalar "foo"), Scalar (mkScalar "boo"), Scalar (mkScalar "buz"), Scalar (mkScalar "boo")]+++testSuiteOfMergeKeys :: Test+testSuiteOfMergeKeys = testGroup "Tests of 'merge keys' feature"+        [ testCase "test uniqueness of keys" caseAllKeysShouldBeUnique+        , testCase "test mapping merge" caseSimpleMappingMerge+        , testCase "test sequence of mappings merging" caseMergeSequence+        ]++caseAllKeysShouldBeUnique :: Assertion+caseAllKeysShouldBeUnique = do+    let maybeRes = decodeYaml "foo1: foo\nfoo2: baz\nfoo1: buz"+    isJust maybeRes @? "decoder should return Just YamlObject but returned Nothing"+    let res = fromJust maybeRes+    mappingKey res "foo1" @?= Scalar (mkScalar "buz")++caseSimpleMappingMerge :: Assertion+caseSimpleMappingMerge = do+    let maybeRes = decodeYaml "foo1: foo\nfoo2: baz\n<<:\n  foo1: buz\n  foo3: fuz"+    isJust maybeRes @? "decoder should return Just YamlObject but returned Nothing"+    let res = fromJust maybeRes+    mappingKey res "foo1" @?= Scalar (mkScalar "foo")+    mappingKey res "foo3" @?= Scalar (mkScalar "fuz")++caseMergeSequence :: Assertion+caseMergeSequence = do+    let maybeRes = decodeYaml "m1: &m1\n  k1: !!str 1\n  k2: !!str 2\nm2: &m2\n  k1: !!str 3\n  k3: !!str 4\nfoo1: foo\n<<: [ *m1, *m2 ]"+    isJust maybeRes @? "decoder should return Just YamlObject but returned Nothing"+    let res = fromJust maybeRes+    mappingKey res "foo1" @?= Scalar (mkScalar "foo")+    mappingKey res "k1" @?= Scalar (mkStrScalar "1")+    mappingKey res "k2" @?= Scalar (mkStrScalar "2")+    mappingKey res "k3" @?= Scalar (mkStrScalar "4")