on master help sign in
Move to aeson types, most tests pass
e151a82Michael Snoyman
Data/Yaml.hs+73−253
@@ -3,232 +3,55 @@
3{-# LANGUAGE CPP #-}
3{-# LANGUAGE CPP #-}
4{-# LANGUAGE DeriveDataTypeable #-}
4{-# LANGUAGE DeriveDataTypeable #-}
5{-# LANGUAGE MultiParamTypeClasses #-}
5{-# LANGUAGE MultiParamTypeClasses #-}
6−{-# LANGUAGE PackageImports #-}
7{-# LANGUAGE PatternGuards #-}
6{-# LANGUAGE PatternGuards #-}
8−{-|
7+{-# LANGUAGE ScopedTypeVariables #-}
9−As a bit of background, this package is built on a few other packages I wrote.
10−yaml is a low-level wrapper around the C libyaml library, with an enumerator
11−interface. data-object is a package defining a data type:
12−
13−@
14− data Object k v = Scalar v
15− | Sequence [Object k v]
16− | Mapping [(k, Object k v)]
17−@
18−
19−In other words, it can represent JSON data fully, and YAML data almost fully.
20−In particular, it doesn't handle cyclical aliases, which I hope doesn't really
21−occur too much in real life.
22−
23−Another package to deal with is failure: it basically replaces using an Either
24−for error-handling into a typeclass. It has instances for Maybe, IO and lists
25− by default.
26−
27−The last package is convertible-text, which is a fork of John Goerzen's
28−convertible package. The difference is it supports both conversions that are
29−guaranteed to succeed (Int -> String) and ones which may fail (String -> Int),
30−and also supports various textual datatypes (String, lazy\/strict ByteString,
31−lazy\/string Text).
32−
33−/YamlScalar and YamlObject/
34−
35−We have a @type YamlObject = Object YamlScalar YamlScalar@, where a YamlScalar
36−is just a ByteString value with a tag and a style. A \"style\" is how the data
37−was represented in the underlying YAML file: single quoted, double quoted, etc.
38−
39−Then there is an IsYamlScalar typeclass, which provides fromYamlScalar and
40−toYamlScalar conversion functions. There are instances for all the
41−\"text-like\" datatypes: String, ByteString and Text. The built-in instances
42−all assume a UTF-8 data encoding. And around this we have toYamlObject and
43−fromYamlObject functions, which do exactly what they sound like.
44−
45−/Encoding and decoding/
46−
47−There are two encoding files: encode and encodeFile. You can guess the
48−different: the former produces a ByteString (strict) and the latter writes to a
49−file. They both take an Object, whose keys and values must be an instance of
50−IsYamlScalar. So, for example:
51−
52−@
53− encodeFile "myfile.yaml" $ Mapping
54− [ ("Michael", Mapping
55− [ ("age", Scalar "26")
56− , ("color", Scalar "blue")
57− ])
58− , ("Eliezer", Mapping
59− [ ("age", Scalar "2")
60− , ("color", Scalar "green")
61− ])
62− ]
63−@
64−
65−decoding is only slightly more complicated, since the decoding can fail. In
66−particular, the return type is an IO wrapped around a Failure. For example, you
67−could use:
68−
69−@
70− maybeObject <- decodeFile "myfile.yaml"
71− case maybeObject of
72− Nothing -> putStrLn "Error parsing YAML file."
73− Just object -> putStrLn "Successfully parsed."
74−@
75−
76−If you just want to throw any parse errors as IO exception, you can use join:
77−
78−@
79− import Control.Monad (join)
80− object <- join $ decodeFile "myfile.yaml"
81−@
82−
83−This takes advantage of the IO instance of Failure.
84−
85−/Parsing an Object/
86−
87−In order to pull the data out of an Object, you can use the helper functions
88−from Data.Object. For example:
89−
90−@
91− import Data.Object
92− import Data.Object.Yaml
93− import Control.Monad
94−
95− main = do
96− object <- join $ decodeFile "myfile.yaml"
97− people <- fromMapping object
98− michael <- lookupMapping "Michael" people
99− age <- lookupScalar "age" michael
100− putStrLn $ "Michael is " ++ age ++ " years old."
101−@
102−
103−lookupScalar and friends implement Maybe, so you can test for optional
104−attributes by switching on Nothing/Just a:
105−
106−@
107− name <- lookupScalar "middleName" michael :: Maybe String
108−@
109−
110−
111−/And that's it/
112−
113−There's really not more to know about this library. Enjoy!
114−-}
115module Data.Yaml
8module Data.Yaml
116− ( -- * Definition of 'YamlObject'
9+ ( -- * Types
117− YamlScalar (..)
10+ Value (..)
118− , YamlObject
11+ , object
119− -- * Automatic scalar conversions
12+ , array
120− , IsYamlScalar (..)
13+ -- * Classes
121− , toYamlObject
14+ , ToJSON (..)
122− , fromYamlObject
15+ , FromJSON (..)
123 -- * Encoding/decoding
16 -- * Encoding/decoding
124 , encode
17 , encode
125 , encodeFile
18 , encodeFile
126 , decode
19 , decode
127 , decodeFile
20 , decodeFile
128− -- * Exceptions
129− , ParseException (..)
130 ) where
21 ) where
131
22
132import qualified Text.Libyaml as Y
23import qualified Text.Libyaml as Y
24+import Data.Aeson (Value (..), ToJSON (..), FromJSON (..), object)
25+import Data.Aeson.Types (Pair, parseMaybe)
133import Text.Libyaml hiding (encode, decode, encodeFile, decodeFile)
26import Text.Libyaml hiding (encode, decode, encodeFile, decodeFile)
134−import Data.Object
135import Data.ByteString (ByteString)
27import Data.ByteString (ByteString)
136import qualified Data.Map as Map
28import qualified Data.Map as Map
137import System.IO.Unsafe (unsafePerformIO)
29import System.IO.Unsafe (unsafePerformIO)
138−import Control.Failure (Failure (failure))
30+import Control.Exception (try, throwIO, fromException, Exception)
139−
140−import qualified Data.Text
141−import qualified Data.Text.Lazy
142−import qualified Data.ByteString
143−import qualified Data.ByteString.Lazy
144−
145−import Data.Convertible.Text (cs)
146−import Data.Data
147−
148−import Control.Monad.Trans.Class
149−import Control.Monad.IO.Class
150import Control.Monad.Trans.State
31import Control.Monad.Trans.State
151−import Control.Monad
152import qualified Data.Conduit as C
32import qualified Data.Conduit as C
153import qualified Data.Conduit.List as CL
33import qualified Data.Conduit.List as CL
154−import Prelude hiding (catch)
34+import Control.Monad.Trans.Class (MonadTrans, lift)
155−import Control.Exception (throwIO, Exception, fromException, try)
35+import Control.Monad.IO.Class (MonadIO, liftIO)
156−import Data.String (IsString (fromString))
36+import Control.Monad (liftM)
157−
37+import qualified Data.Vector as V
158−-- | Equality depends on 'value' and 'tag', not 'style'.
38+import Data.Text (Text)
159−data YamlScalar = YamlScalar
39+import Data.Text.Encoding (encodeUtf8, decodeUtf8With)
160− { value :: ByteString
40+import Data.Text.Encoding.Error (lenientDecode)
161− , tag :: Tag
41+import qualified Data.HashMap.Strict as M
162− , style :: Style
163− }
164− deriving (Show, Read, Data, Typeable)
42+import Data.Typeable
165−instance Eq YamlScalar where
43+
166− (YamlScalar v t _) == (YamlScalar v' t' _) = v == v' && t == t'
167−instance IsString YamlScalar where
168− fromString = toYamlScalar
169−
170−type YamlObject = Object YamlScalar YamlScalar
171−
172−class (Eq a) => IsYamlScalar a where
173− fromYamlScalar :: YamlScalar -> a
174− toYamlScalar :: a -> YamlScalar
175−instance IsYamlScalar YamlScalar where
176− fromYamlScalar = id
177− toYamlScalar = id
178−instance IsYamlScalar Data.Text.Text where
179− fromYamlScalar = cs . value
180− toYamlScalar t = YamlScalar (cs t) NoTag Any
181−instance IsYamlScalar Data.Text.Lazy.Text where
182− fromYamlScalar = cs . value
183− toYamlScalar t = YamlScalar (cs t) NoTag Any
184−instance IsYamlScalar [Char] where
185− fromYamlScalar = cs . value
186− toYamlScalar s = YamlScalar (cs s) NoTag Any
187−instance IsYamlScalar Data.ByteString.ByteString where
188− fromYamlScalar = value
189− toYamlScalar b = YamlScalar b NoTag Any
190−instance IsYamlScalar Data.ByteString.Lazy.ByteString where
191− fromYamlScalar = cs . value
192− toYamlScalar b = YamlScalar (cs b) NoTag Any
193−
194−-- | Merge assoc-lists by keys.
195−-- First list overrides second:
196−-- [(k1, x), (k2, y)] `mergeAssocLists` [(k3, z)] == [(k1, x), (k2, y), (k3, z)]
197−-- [(k1, x), (k2, y)] `mergeAssocLists` [(k2, z)] == [(k1, x), (k2, y)]
198−mergeAssocLists :: (Eq k) => [(k, v)] -> [(k, v)] -> [(k, v)]
199−mergeAssocLists a [] = a
200−mergeAssocLists [] b = b
201−mergeAssocLists a ((bk, bv):bs) =
202− case lookup bk a of
203− Nothing -> (bk, bv) : mergeAssocLists a bs
204− Just av -> (bk, av) : mergeAssocLists (filter (\(x, _) -> x /= bk) a) bs
205−
206−toYamlObject :: IsYamlScalar k
207− => IsYamlScalar v
208− => Object k v
209− -> YamlObject
210−toYamlObject = mapKeysValues toYamlScalar toYamlScalar
211−
212−fromYamlObject :: IsYamlScalar k
213− => IsYamlScalar v
214− => YamlObject
215− -> Object k v
216−fromYamlObject = mapKeysValues fromYamlScalar fromYamlScalar
217−
218−encode :: (IsYamlScalar k, IsYamlScalar v) => Object k v -> ByteString
44+encode :: ToJSON a => a -> ByteString
219encode obj = unsafePerformIO $
45encode obj = unsafePerformIO $
220− C.runResourceT $ CL.sourceList (objToEvents $ toYamlObject obj)
46+ C.runResourceT $ CL.sourceList (objToEvents $ toJSON obj)
221 C.$$ Y.encode
47 C.$$ Y.encode
222
48
223−encodeFile :: (IsYamlScalar k, IsYamlScalar v)
224− => FilePath
49+encodeFile :: ToJSON a => FilePath -> a -> IO ()
225− -> Object k v
226− -> IO ()
227encodeFile fp obj = C.runResourceT
50encodeFile fp obj = C.runResourceT
228− $ CL.sourceList (objToEvents $ toYamlObject obj)
51+ $ CL.sourceList (objToEvents $ toJSON obj)
229 C.$$ Y.encodeFile fp
52 C.$$ Y.encodeFile fp
230
53
231−objToEvents :: YamlObject -> [Y.Event]
54+objToEvents :: Value -> [Y.Event]
232objToEvents o = (:) EventStreamStart
55objToEvents o = (:) EventStreamStart
233 . (:) EventDocumentStart
56 . (:) EventDocumentStart
234 $ objToEvents' o
57 $ objToEvents' o
@@ -236,21 +59,24 @@ objToEvents o = (:) EventStreamStart
236 , EventStreamEnd
59 , EventStreamEnd
237 ]
60 ]
238
61
62+{- FIXME
239scalarToEvent :: YamlScalar -> Event
63scalarToEvent :: YamlScalar -> Event
240scalarToEvent (YamlScalar v t s) = EventScalar v t s Nothing
64scalarToEvent (YamlScalar v t s) = EventScalar v t s Nothing
65+-}
241
66
242−objToEvents' :: YamlObject -> [Y.Event] -> [Y.Event]
67+objToEvents' :: Value -> [Y.Event] -> [Y.Event]
243−objToEvents' (Scalar s) rest = scalarToEvent s : rest
68+--objToEvents' (Scalar s) rest = scalarToEvent s : rest
244−objToEvents' (Sequence list) rest =
69+objToEvents' (Array list) rest =
245 EventSequenceStart Nothing
70 EventSequenceStart Nothing
246− : foldr ($) (EventSequenceEnd : rest) (map objToEvents' list)
71+ : foldr ($) (EventSequenceEnd : rest) (map objToEvents' $ V.toList list)
247−objToEvents' (Mapping pairs) rest =
72+objToEvents' (Object pairs) rest =
248 EventMappingStart Nothing
73 EventMappingStart Nothing
249− : foldr ($) (EventMappingEnd : rest) (map pairToEvents pairs)
74+ : foldr ($) (EventMappingEnd : rest) (map pairToEvents $ M.toList pairs)
75+objToEvents' (String s) rest = EventScalar (encodeUtf8 s) StrTag Any Nothing : rest
250
76
251−pairToEvents :: (YamlScalar, YamlObject) -> [Y.Event] -> [Y.Event]
77+pairToEvents :: Pair -> [Y.Event] -> [Y.Event]
252pairToEvents (k, v) rest =
78pairToEvents (k, v) rest =
253− scalarToEvent k
79+ EventScalar (encodeUtf8 k) StrTag Any Nothing
254 : objToEvents' v rest
80 : objToEvents' v rest
255
81
256-- Parsing
82-- Parsing
@@ -277,7 +103,7 @@ instance MonadTrans PErrorT where
277instance MonadIO m => MonadIO (PErrorT m) where
103instance MonadIO m => MonadIO (PErrorT m) where
278 liftIO = lift . liftIO
104 liftIO = lift . liftIO
279
105
280−type Parser = StateT (Map.Map String YamlObject) IO
106+type Parser = StateT (Map.Map String Value) IO
281
107
282requireEvent :: Event -> C.Sink Event Parser ()
108requireEvent :: Event -> C.Sink Event Parser ()
283requireEvent e = do
109requireEvent e = do
@@ -286,7 +112,7 @@ requireEvent e = do
286 then return ()
112 then return ()
287 else liftIO $ throwIO $ UnexpectedEvent f $ Just e
113 else liftIO $ throwIO $ UnexpectedEvent f $ Just e
288
114
289−parse :: C.Sink Event Parser YamlObject
115+parse :: C.Sink Event Parser Value
290parse = do
116parse = do
291 requireEvent EventStreamStart
117 requireEvent EventStreamStart
292 requireEvent EventDocumentStart
118 requireEvent EventDocumentStart
@@ -295,23 +121,23 @@ parse = do
295 requireEvent EventStreamEnd
121 requireEvent EventStreamEnd
296 return res
122 return res
297
123
298−parseScalar :: ByteString -> Tag -> Style -> Anchor
124+parseScalar :: ByteString -> Anchor
299− -> C.Sink Event Parser YamlScalar
125+ -> C.Sink Event Parser Text
300−parseScalar v t s a = do
126+parseScalar v a = do
301− let res = YamlScalar v t s
127+ let res = decodeUtf8With lenientDecode v
302 case a of
128 case a of
303 Nothing -> return res
129 Nothing -> return res
304 Just an -> do
130 Just an -> do
305− lift $ modify (Map.insert an $ Scalar res)
131+ lift $ modify (Map.insert an $ String res)
306 return res
132 return res
307
133
308−parseO :: C.Sink Event Parser YamlObject
134+parseO :: C.Sink Event Parser Value
309parseO = do
135parseO = do
310 me <- CL.head
136 me <- CL.head
311 case me of
137 case me of
312− Just (EventScalar v t s a) -> Scalar `liftM` parseScalar v t s a
138+ Just (EventScalar v _t _s a) -> fmap String $ parseScalar v a
313 Just (EventSequenceStart a) -> parseS a id
139 Just (EventSequenceStart a) -> parseS a id
314− Just (EventMappingStart a) -> parseM a id
140+ Just (EventMappingStart a) -> parseM a M.empty
315 Just (EventAlias an) -> do
141 Just (EventAlias an) -> do
316 m <- lift get
142 m <- lift get
317 case Map.lookup an m of
143 case Map.lookup an m of
@@ -320,14 +146,14 @@ parseO = do
320 _ -> liftIO $ throwIO $ UnexpectedEvent me Nothing
146 _ -> liftIO $ throwIO $ UnexpectedEvent me Nothing
321
147
322parseS :: Y.Anchor
148parseS :: Y.Anchor
323− -> ([YamlObject] -> [YamlObject])
149+ -> ([Value] -> [Value])
324− -> C.Sink Event Parser YamlObject
150+ -> C.Sink Event Parser Value
325parseS a front = do
151parseS a front = do
326 me <- CL.peek
152 me <- CL.peek
327 case me of
153 case me of
328 Just EventSequenceEnd -> do
154 Just EventSequenceEnd -> do
329 CL.drop 1
155 CL.drop 1
330− let res = Sequence $ front []
156+ let res = Array $ V.fromList $ front []
331 case a of
157 case a of
332 Nothing -> return res
158 Nothing -> return res
333 Just an -> do
159 Just an -> do
@@ -338,54 +164,48 @@ parseS a front = do
338 parseS a $ front . (:) o
164 parseS a $ front . (:) o
339
165
340parseM :: Y.Anchor
166parseM :: Y.Anchor
341− -> ([(YamlScalar, YamlObject)] -> [(YamlScalar, YamlObject)])
167+ -> M.HashMap Text Value
342− -> C.Sink Event Parser YamlObject
168+ -> C.Sink Event Parser Value
343parseM a front = do
169parseM a front = do
344 me <- CL.peek
170 me <- CL.peek
345 case me of
171 case me of
346 Just EventMappingEnd -> do
172 Just EventMappingEnd -> do
347 CL.drop 1
173 CL.drop 1
348− let res = Mapping $ front []
174+ let res = Object front
349 case a of
175 case a of
350 Nothing -> return res
176 Nothing -> return res
351 Just an -> do
177 Just an -> do
352 lift $ modify $ Map.insert an res
178 lift $ modify $ Map.insert an res
353 return res
179 return res
354 _ -> do
180 _ -> do
355− me' <- CL.head
181+ CL.drop 1
356− s <- case me' of
182+ s <- case me of
357− Just (EventScalar v t s a') -> parseScalar v t s a'
183+ Just (EventScalar v _ _ a') -> parseScalar v a'
358− _ -> liftIO $ throwIO $ UnexpectedEvent me' Nothing
184+ _ -> liftIO $ throwIO $ UnexpectedEvent me Nothing
359 o <- parseO
185 o <- parseO
360− let al = mergeAssocLists [(s, o)] $ front []
186+ parseM a $ M.insert s o front
361− al' = if fromYamlScalar s == "<<"
362− then case o of
363− Scalar _ -> al
364− Mapping l -> mergeAssocLists al l
365− Sequence l -> mergeAssocLists al $ foldl merge' [] l
366− else al
367− parseM a (`mergeAssocLists` al')
368− where merge' :: (Eq k) => [(k, Object k v)] -> Object k v -> [(k, Object k v)]
369− merge' al (Mapping om) = mergeAssocLists al om
370− merge' al _ = al
371
187
372−decode :: (Failure ParseException m, IsYamlScalar k, IsYamlScalar v)
188+decode :: FromJSON a
373 => ByteString
189 => ByteString
374− -> m (Object k v)
190+ -> Maybe a
375−decode bs = unsafePerformIO $ decodeHelper (Y.decode bs)
191+decode bs = unsafePerformIO $ fmap (either (const Nothing) id) $ decodeHelper (Y.decode bs)
376
192
377−decodeFile :: (Failure ParseException m, IsYamlScalar k, IsYamlScalar v)
193+decodeFile :: FromJSON a
378 => FilePath
194 => FilePath
379− -> IO (m (Object k v))
195+ -> IO (Maybe a)
380−decodeFile fp = decodeHelper (Y.decodeFile fp)
196+decodeFile fp = decodeHelper (Y.decodeFile fp) >>= either throwIO return
381
197
382−decodeHelper :: (Failure ParseException m, IsYamlScalar k, IsYamlScalar v)
198+decodeHelper :: FromJSON a
383 => C.Source Parser Y.Event
199 => C.Source Parser Y.Event
384− -> IO (m (Object k v))
200+ -> IO (Either ParseException (Maybe a))
385decodeHelper src = do
201decodeHelper src = do
386 x <- try $ flip evalStateT Map.empty $ C.runResourceT $ src C.$$ parse
202 x <- try $ flip evalStateT Map.empty $ C.runResourceT $ src C.$$ parse
387 case x of
203 case x of
388 Left e
204 Left e
389− | Just pe <- fromException e -> return $ failure (pe :: ParseException)
205+ | Just pe <- fromException e -> return $ Left pe
390− | otherwise -> return $ failure $ InvalidYaml $ Just $ show e
206+ | Just ye <- fromException e -> return $ Left $ InvalidYaml $ Just $ show (ye :: YamlException)
391− Right y -> return $ return $ fromYamlObject y
207+ | otherwise -> throwIO e
208+ Right y -> return $ Right $ parseMaybe parseJSON y
209+
210+array :: [Value] -> Value
211+array = Array . V.fromList
test/main.hs+35−55
@@ -1,6 +1,7 @@
1{-# LANGUAGE CPP #-}
1{-# LANGUAGE CPP #-}
2{-# LANGUAGE PackageImports #-}
2{-# LANGUAGE PackageImports #-}
3{-# LANGUAGE ScopedTypeVariables #-}
3{-# LANGUAGE ScopedTypeVariables #-}
4+{-# LANGUAGE OverloadedStrings #-}
4
5
5import qualified Text.Libyaml as Y
6import qualified Text.Libyaml as Y
6import qualified Data.ByteString.Char8 as B8
7import qualified Data.ByteString.Char8 as B8
@@ -16,10 +17,11 @@ import Control.Exception (try, SomeException)
16import Test.Hspec.Monadic
17import Test.Hspec.Monadic
17import Test.Hspec.HUnit ()
18import Test.Hspec.HUnit ()
18
19
19−import Data.Object
20import qualified Data.Yaml as D
20import qualified Data.Yaml as D
21+import Data.Yaml (object, array)
21import Data.Maybe
22import Data.Maybe
22−import Data.Convertible.Text (cs)
23+import qualified Data.HashMap.Strict as M
24+import qualified Data.Text as T
23
25
24main :: IO ()
26main :: IO ()
25main = hspecX $ do
27main = hspecX $ do
@@ -39,7 +41,6 @@ main = hspecX $ do
39 it "encode/decode file" caseEncodeDecodeFileData
41 it "encode/decode file" caseEncodeDecodeFileData
40 it "encode/decode strings" caseEncodeDecodeStrings
42 it "encode/decode strings" caseEncodeDecodeStrings
41 it "decode invalid file" caseDecodeInvalid
43 it "decode invalid file" caseDecodeInvalid
42− it "encode/decode in order" caseInOrder
43 describe "Data.Yaml aliases" $ do
44 describe "Data.Yaml aliases" $ do
44 it "simple scalar alias" caseSimpleScalarAlias
45 it "simple scalar alias" caseSimpleScalarAlias
45 it "simple sequence alias" caseSimpleSequenceAlias
46 it "simple sequence alias" caseSimpleSequenceAlias
@@ -181,75 +182,69 @@ caseDecodeInvalidDocument = do
181 yamlString = " - foo\n - baz\nbuz"
182 yamlString = " - foo\n - baz\nbuz"
182 yamlBS = B8.pack yamlString
183 yamlBS = B8.pack yamlString
183
184
184−mkFoldedScalar :: String -> D.YamlScalar
185+mkScalar :: String -> D.Value
185−mkFoldedScalar s = D.YamlScalar (cs s) Y.StrTag Y.Folded
186+mkScalar = mkStrScalar
186
187
187−mkScalar :: String -> D.YamlScalar
188+mkStrScalar :: String -> D.Value
188−mkScalar s = D.YamlScalar (cs s) Y.NoTag Y.Plain
189+mkStrScalar = D.String . T.pack
189
190
190−mkStrScalar :: String -> D.YamlScalar
191+mappingKey :: D.Value-> String -> D.Value
191−mkStrScalar s = D.YamlScalar (cs s) Y.StrTag Y.Plain
192+mappingKey (D.Object m) k = (fromJust . M.lookup (T.pack k) $ m)
193+mappingKey _ _ = error "expected Object"
192
194
193−mappingKey :: D.YamlObject -> String -> D.YamlObject
194−mappingKey (Mapping m) k = (fromJust . lookup (mkScalar k) $ m)
195−mappingKey _ _ = error "expected Mapping"
196−
197−decodeYaml :: String -> Maybe D.YamlObject
195+decodeYaml :: String -> Maybe D.Value
198decodeYaml s = D.decode $ B8.pack s
196decodeYaml s = D.decode $ B8.pack s
199
197
200−sample :: D.YamlObject
198+sample :: D.Value
201−sample = Sequence
199+sample = array
202− [ Scalar $ mkFoldedScalar "foo"
200+ [ D.String "foo"
203− , Mapping
201+ , object
204− [ (mkFoldedScalar "bar1", Scalar $ mkFoldedScalar "bar2")
202+ [ ("bar1", D.String "bar2")
205 ]
203 ]
206 ]
204 ]
207
205
208−sampleStr :: Object String String
209−sampleStr = mapKeysValues D.fromYamlScalar D.fromYamlScalar sample
210−
211caseEncodeDecodeData :: Assertion
206caseEncodeDecodeData :: Assertion
212caseEncodeDecodeData = do
207caseEncodeDecodeData = do
213− out <- D.decode $ D.encode sample
208+ let out = D.decode $ D.encode sample
214− out @?= sample
209+ out @?= Just sample
215
210
216caseEncodeDecodeFileData :: Assertion
211caseEncodeDecodeFileData :: Assertion
217caseEncodeDecodeFileData = do
212caseEncodeDecodeFileData = do
218 let fp = "tmp.yaml"
213 let fp = "tmp.yaml"
219 D.encodeFile fp sample
214 D.encodeFile fp sample
220− out <- join $ D.decodeFile fp
215+ out <- D.decodeFile fp
221− out @?= sample
216+ out @?= Just sample
222
217
223caseEncodeDecodeStrings :: Assertion
218caseEncodeDecodeStrings :: Assertion
224caseEncodeDecodeStrings = do
219caseEncodeDecodeStrings = do
225− out <- D.decode $ D.encode $ D.toYamlObject sampleStr
220+ let out = D.decode $ D.encode sample
226− D.fromYamlObject out @?= sampleStr
221+ out @?= Just sample
227
222
228caseDecodeInvalid :: Assertion
223caseDecodeInvalid :: Assertion
229caseDecodeInvalid = do
224caseDecodeInvalid = do
230 let invalid = B8.pack "\tthis is 'not' valid :-)"
225 let invalid = B8.pack "\tthis is 'not' valid :-)"
231− Nothing @=? (D.decode invalid :: Maybe D.YamlObject)
226+ Nothing @=? (D.decode invalid :: Maybe D.Value)
232
227
233caseSimpleScalarAlias :: Assertion
228caseSimpleScalarAlias :: Assertion
234caseSimpleScalarAlias = do
229caseSimpleScalarAlias = do
235 let maybeRes = decodeYaml "- &anch foo\n- baz\n- *anch"
230 let maybeRes = decodeYaml "- &anch foo\n- baz\n- *anch"
236 isJust maybeRes @? "decoder should return Just YamlObject but returned Nothing"
231 isJust maybeRes @? "decoder should return Just YamlObject but returned Nothing"
237 let res = fromJust maybeRes
232 let res = fromJust maybeRes
238− res @?= Sequence [Scalar (mkScalar "foo"), Scalar (mkScalar "baz"), Scalar (mkScalar "foo")]
233+ res @?= array [(mkScalar "foo"), (mkScalar "baz"), (mkScalar "foo")]
239
234
240caseSimpleSequenceAlias :: Assertion
235caseSimpleSequenceAlias :: Assertion
241caseSimpleSequenceAlias = do
236caseSimpleSequenceAlias = do
242 let maybeRes = decodeYaml "seq: &anch\n - foo\n - baz\nseq2: *anch"
237 let maybeRes = decodeYaml "seq: &anch\n - foo\n - baz\nseq2: *anch"
243 isJust maybeRes @? "decoder should return Just YamlObject but returned Nothing"
238 isJust maybeRes @? "decoder should return Just YamlObject but returned Nothing"
244 let res = fromJust maybeRes
239 let res = fromJust maybeRes
245− res @?= Mapping [(mkScalar "seq", Sequence [Scalar (mkScalar "foo"), Scalar (mkScalar "baz")]), (mkScalar "seq2", Sequence [Scalar (mkScalar "foo"), Scalar (mkScalar "baz")])]
240+ res @?= object [("seq", array [(mkScalar "foo"), (mkScalar "baz")]), ("seq2", array [(mkScalar "foo"), (mkScalar "baz")])]
246
241
247caseSimpleMappingAlias :: Assertion
242caseSimpleMappingAlias :: Assertion
248caseSimpleMappingAlias = do
243caseSimpleMappingAlias = do
249 let maybeRes = decodeYaml "map: &anch\n key1: foo\n key2: baz\nmap2: *anch"
244 let maybeRes = decodeYaml "map: &anch\n key1: foo\n key2: baz\nmap2: *anch"
250 isJust maybeRes @? "decoder should return Just YamlObject but returned Nothing"
245 isJust maybeRes @? "decoder should return Just YamlObject but returned Nothing"
251 let res = fromJust maybeRes
246 let res = fromJust maybeRes
252− 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"))])]
247+ res @?= object [(T.pack "map", object [("key1", mkScalar "foo"), ("key2", (mkScalar "baz"))]), (T.pack "map2", object [("key1", (mkScalar "foo")), ("key2", mkScalar "baz")])]
253
248
254caseMappingAliasBeforeAnchor :: Assertion
249caseMappingAliasBeforeAnchor :: Assertion
255caseMappingAliasBeforeAnchor = do
250caseMappingAliasBeforeAnchor = do
@@ -266,44 +261,29 @@ caseScalarAliasOverriding = do
266 let maybeRes = decodeYaml "- &anch foo\n- baz\n- *anch\n- &anch boo\n- buz\n- *anch"
261 let maybeRes = decodeYaml "- &anch foo\n- baz\n- *anch\n- &anch boo\n- buz\n- *anch"
267 isJust maybeRes @? "decoder should return Just YamlObject but returned Nothing"
262 isJust maybeRes @? "decoder should return Just YamlObject but returned Nothing"
268 let res = fromJust maybeRes
263 let res = fromJust maybeRes
269− res @?= Sequence [Scalar (mkScalar "foo"), Scalar (mkScalar "baz"), Scalar (mkScalar "foo"), Scalar (mkScalar "boo"), Scalar (mkScalar "buz"), Scalar (mkScalar "boo")]
264+ res @?= array [(mkScalar "foo"), (mkScalar "baz"), (mkScalar "foo"), (mkScalar "boo"), (mkScalar "buz"), (mkScalar "boo")]
270
265
271caseAllKeysShouldBeUnique :: Assertion
266caseAllKeysShouldBeUnique :: Assertion
272caseAllKeysShouldBeUnique = do
267caseAllKeysShouldBeUnique = do
273 let maybeRes = decodeYaml "foo1: foo\nfoo2: baz\nfoo1: buz"
268 let maybeRes = decodeYaml "foo1: foo\nfoo2: baz\nfoo1: buz"
274 isJust maybeRes @? "decoder should return Just YamlObject but returned Nothing"
269 isJust maybeRes @? "decoder should return Just YamlObject but returned Nothing"
275 let res = fromJust maybeRes
270 let res = fromJust maybeRes
276− mappingKey res "foo1" @?= Scalar (mkScalar "buz")
271+ mappingKey res "foo1" @?= (mkScalar "buz")
277
272
278caseSimpleMappingMerge :: Assertion
273caseSimpleMappingMerge :: Assertion
279caseSimpleMappingMerge = do
274caseSimpleMappingMerge = do
280 let maybeRes = decodeYaml "foo1: foo\nfoo2: baz\n<<:\n foo1: buz\n foo3: fuz"
275 let maybeRes = decodeYaml "foo1: foo\nfoo2: baz\n<<:\n foo1: buz\n foo3: fuz"
281 isJust maybeRes @? "decoder should return Just YamlObject but returned Nothing"
276 isJust maybeRes @? "decoder should return Just YamlObject but returned Nothing"
282 let res = fromJust maybeRes
277 let res = fromJust maybeRes
283− mappingKey res "foo1" @?= Scalar (mkScalar "foo")
278+ mappingKey res "foo1" @?= (mkScalar "foo")
284− mappingKey res "foo3" @?= Scalar (mkScalar "fuz")
279+ mappingKey res "foo3" @?= (mkScalar "fuz")
285
280
286caseMergeSequence :: Assertion
281caseMergeSequence :: Assertion
287caseMergeSequence = do
282caseMergeSequence = do
288 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 ]"
283 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 ]"
289 isJust maybeRes @? "decoder should return Just YamlObject but returned Nothing"
284 isJust maybeRes @? "decoder should return Just YamlObject but returned Nothing"
290 let res = fromJust maybeRes
285 let res = fromJust maybeRes
291− mappingKey res "foo1" @?= Scalar (mkScalar "foo")
286+ mappingKey res "foo1" @?= (mkScalar "foo")
292− mappingKey res "k1" @?= Scalar (mkStrScalar "1")
287+ mappingKey res "k1" @?= (mkStrScalar "1")
293− mappingKey res "k2" @?= Scalar (mkStrScalar "2")
288+ mappingKey res "k2" @?= (mkStrScalar "2")
294− mappingKey res "k3" @?= Scalar (mkStrScalar "4")
289+ mappingKey res "k3" @?= (mkStrScalar "4")
295−
296−inOrderData :: String
297−inOrderData = "'Fatal': 'Unknown variable \"bar\"'\n'Date': '2001-11-23 15:03:17 -5'\n'User': 'ed'\n'Stack':\n- 'line': '23'\n 'file': 'TopClass.py'\n 'code': 'x = MoreObject(\"345\\n\")\n\n'\n- 'line': '58'\n 'file': 'MoreClass.py'\n 'code': 'foo = bar'\n"
298−
299−inOrderData2 :: String
300−inOrderData2 =
301− "'a': '1'\n'b': '2'\n'd': '4'\n'c': '3'\n"
302− ++ "'g': '1'\n'n': '2'\n'q': '4'\n'f': '3'\n"
303− ++ "'z': '1'\n'y': '2'\n'x': '4'\n'w': '3'\n"
304−
305−caseInOrder :: Assertion
306−caseInOrder = do
307− Just (Mapping ((x, _):_)) <- return $ decodeYaml inOrderData
308− x @?= mkScalar "Fatal"
309− fmap (B8.unpack . D.encode) (decodeYaml inOrderData2) @?= Just inOrderData2
yaml.cabal+6−6
@@ -29,11 +29,11 @@ library
29 , transformers >= 0.1 && < 0.3
29 , transformers >= 0.1 && < 0.3
30 , bytestring >= 0.9.1.4 && < 0.10
30 , bytestring >= 0.9.1.4 && < 0.10
31 , conduit >= 0.0 && < 0.1
31 , conduit >= 0.0 && < 0.1
32− , convertible-text
32+ , aeson >= 0.5
33− , text
34− , failure
35 , containers
33 , containers
36− , data-object
34+ , unordered-containers
35+ , vector
36+ , text
37 exposed-modules: Text.Libyaml
37 exposed-modules: Text.Libyaml
38 Data.Yaml
38 Data.Yaml
39 ghc-options: -Wall
39 ghc-options: -Wall
@@ -65,8 +65,8 @@ test-suite test
65 , bytestring >= 0.9.1.4 && < 0.10
65 , bytestring >= 0.9.1.4 && < 0.10
66 , conduit
66 , conduit
67 , yaml
67 , yaml
68− , data-object
68+ , text
69− , convertible-text
69+ , unordered-containers
70 ghc-options: -Wall
70 ghc-options: -Wall
71 c-sources: c/helper.c
71 c-sources: c/helper.c
72 include-dirs: c
72 include-dirs: c