@@ -2,6 +2,7 @@
2{-# LANGUAGE FlexibleContexts #-}
2{-# LANGUAGE FlexibleContexts #-}
3{-# LANGUAGE MultiParamTypeClasses #-}
3{-# LANGUAGE MultiParamTypeClasses #-}
4{-# LANGUAGE CPP #-}
4{-# LANGUAGE CPP #-}
5+{-# LANGUAGE PackageImports #-}
5---------------------------------------------------------
6---------------------------------------------------------
7-- Module : Text.Yaml
8-- Module : Text.Yaml
@@ -20,21 +21,18 @@ module Text.Yaml
21 -- * Exceptions
22 -- * Exceptions
22 YamlException (..)
23 YamlException (..)
23− ,
24+ -- * Converting to/from 'YamlObject's
24 -- * Converting to/from 'TextObject's
30 -- * Converting to/from 'TextObject's
25− encodeText
31+ , encodeText'
26− , encodeToText
32+ , decodeText'
27 , decodeText
34 , decodeText
28− , decodeFromText
35+ , decodeTextSuccess
30− -- * Convert to/from 'ScalarObejct's
37− , decodeFileFromTextWrap
39 , testSuite
37 , testSuite
@@ -42,20 +40,17 @@ module Text.Yaml
43import Prelude hiding (readList)
41import Prelude hiding (readList)
44import Text.Libyaml
42import Text.Libyaml
45−import qualified Data.ByteString as B
43+import Data.ByteString (ByteString)
47import Data.Object.Text
44import Data.Object.Text
48−import Data.Object.Scalar
49import System.IO.Unsafe
45import System.IO.Unsafe
50−import Control.Arrow (first)
46+import Control.Monad (join)
51−import Control.Monad (liftM, join)
52−import Control.Monad.Trans
53import Data.Convertible.Text
47import Data.Convertible.Text
54import Data.Attempt
48import Data.Attempt
56−import Data.Text.Lazy (Text)
50+import Data.Object.Yaml
59import Test.Framework (testGroup, Test)
54import Test.Framework (testGroup, Test)
60import Test.Framework.Providers.HUnit
55import Test.Framework.Providers.HUnit
61import Test.Framework.Providers.QuickCheck (testProperty)
56import Test.Framework.Providers.QuickCheck (testProperty)
@@ -63,187 +58,101 @@ import Test.HUnit hiding (Test, path)
63import Test.QuickCheck
58import Test.QuickCheck
65import Control.Monad (replicateM)
60import Control.Monad (replicateM)
66−import Control.Arrow ((***))
67import qualified Data.Text.Lazy as LT
61import qualified Data.Text.Lazy as LT
68import qualified Data.ByteString.Lazy as BL
62import qualified Data.ByteString.Lazy as BL
63+import qualified Data.ByteString as B
65+import Control.Arrow ((***))
71-- Low level, non-exported functions
68-- Low level, non-exported functions
72encode' :: MonadFailure YamlException m
69encode' :: MonadFailure YamlException m
73− => TextObject
70+ => YamlObject
74− -> IO (m B.ByteString)
71+ -> IO (m ByteString)
76− let events = objectToEvents o
77− result <- withEmitter $ flip emitEvents events
72+encode' = withEmitter . flip emitEvents . convertSuccess
82decode' :: MonadFailure YamlException m
74decode' :: MonadFailure YamlException m
83− => B.ByteString
75+ => ByteString
84− -> IO (m TextObject)
76+ -> IO (m YamlObject)
85decode' bs = do
77decode' bs = do
86− events <- withParser (convertSuccess bs) parserParse
78+ events <- withParser bs parserParse
87− return $ liftM eventsToTextObject events
79+ return $ events >>= eventsToYamlObject
89−-- Text API
81+-- Convert to YamlObject directly
90−-- FIXME: this *should* be in a MonadFailure
82+encodeYaml' :: YamlObject -> ByteString
91−
83+encodeYaml' = unsafePerformIO . join . encode'
92−-- FIXME: I think the To and From below are backwards. Maybe just say convert?
84+
93−encodeText :: ConvertSuccess B.ByteString a
85+decodeYaml' :: MonadFailure YamlException m => ByteString -> m YamlObject
94− => TextObject
86+decodeYaml' = unsafePerformIO . decode'
96−encodeText = convertSuccess . unsafePerformIO . join . encode'
88+-- Convert to YamlObject conversions
97−
89+encodeYaml :: ConvertSuccess x YamlObject => x -> ByteString
98−encodeToText :: (ConvertSuccess B.ByteString a, ToObject x Text Text)
90+encodeYaml = encodeYaml' . convertSuccess
100− -> a
92+decodeYaml :: ( ConvertAttempt YamlObject x
101−encodeToText = convertSuccess . unsafePerformIO . join . encode' . toTextObject
93+ , Failure ConversionException m
103−decodeText :: (ConvertSuccess a B.ByteString,
104− MonadFailure YamlException m)
94+ , MonadFailure YamlException m
106− -> m TextObject
96+ => ByteString
107−decodeText = unsafePerformIO . decode' . convertSuccess
97+ -> m x
108−
98+decodeYaml b = decodeYaml' b >>= convertAttemptWrap
109−decodeFromText :: (ConvertSuccess a B.ByteString,
99+
110− FromObject x Text Text)
100+decodeYamlSuccess :: ( ConvertSuccess YamlObject x
111− => a
101+ , MonadFailure YamlException m
113−decodeFromText a = decodeText a >>= fromObject
103+ => ByteString
115−decodeFromTextWrap :: (ConvertSuccess a B.ByteString,
105+decodeYamlSuccess = fmap convertSuccess . decodeYaml'
116− FromObject x Text Text,
106+
108+encodeText' :: TextObject -> ByteString
109+encodeText' = encodeYaml
117− MonadFailure YamlException m,
111+decodeText' :: MonadFailure YamlException m => ByteString -> m TextObject
118− MonadFailure FromObjectException m
112+decodeText' = decodeYamlSuccess
120− => a
114+-- TextObject conversions
121− -> m x
115+encodeText :: ConvertSuccess x TextObject => x -> ByteString
122−decodeFromTextWrap a = decodeText a >>= fromObjectWrap
116+encodeText = encodeText' . convertSuccess
124−-- Scalar API
118+decodeText :: ( MonadFailure YamlException m
125−-- FIXME: this *should* be in a MonadFailure...
119+ , ConvertAttempt TextObject x
126−encodeScalar :: ConvertSuccess B.ByteString a
120+ , Failure ConversionException m
127− => ScalarObject
121+ )
128− -> a
122+ => ByteString
129−encodeScalar = convertSuccess . unsafePerformIO . join . encode' . toTextObject
123+ -> m x
130−
124+decodeText bs = decodeText' bs >>= convertAttemptWrap
131−decodeScalar :: (ConvertSuccess a B.ByteString,
125+
132− MonadFailure YamlException m)
135−decodeScalar = liftM toScalarObject . unsafePerformIO . decode' . convertSuccess
138−encodeFile :: (MonadIO m, MonadFailure YamlException m)
126+decodeTextSuccess :: ( MonadFailure YamlException m
140− -> TextObject
127+ , ConvertSuccess TextObject x
142−encodeFile path o = do
129+ => ByteString
143− content <- liftIO $ encode' o
130+ -> m x
144− content' <- content
131+decodeTextSuccess = fmap convertSuccess . decodeText'
145− liftIO $ B.writeFile path content'
147−encodeFileToText :: (ToObject x Text Text,
149− MonadFailure YamlException m
154−encodeFileToText fp = encodeFile fp . toTextObject
156−decodeFile :: (MonadFailure YamlException m,
161− contents <- liftIO $ B.readFile path
162− join $ liftIO $ decode' contents
164−decodeFileFromTextWrap :: (MonadFailure YamlException m,
165− MonadFailure FromObjectException m,
167− FromObject x Text Text
171−decodeFileFromTextWrap path = decodeFile path >>= fromObjectWrap
173−-- The real worker functions
174−objectToEvents :: TextObject -> [Event]
175−objectToEvents y = concat
176− [ [EventStreamStart, EventDocumentStart]
178− , [EventDocumentEnd, EventStreamEnd]] where
179− writeSingle :: TextObject -> [Event]
180− writeSingle (Scalar text) = [EventScalar $ convertSuccess text]
181− writeSingle (Sequence ys) =
182− (EventSequenceStart : concatMap writeSingle ys)
183− ++ [EventSequenceEnd]
184− writeSingle (Mapping pairs) =
185− EventMappingStart : concatMap writePair pairs ++ [EventMappingEnd]
186− writePair :: (Text, TextObject) -> [Event]
187− writePair (k, v) = EventScalar (convertSuccess k) : writeSingle v
189−eventsToTextObject :: [Event] -> TextObject
190−eventsToTextObject = fst . readSingle . dropWhile isIgnored where
191− isIgnored EventAlias = False
192− isIgnored (EventScalar _) = False
193− isIgnored EventSequenceStart = False
194− isIgnored EventSequenceEnd = False
195− isIgnored EventMappingStart = False
196− isIgnored EventMappingEnd = False
198− readSingle :: [Event] -> (TextObject, [Event])
199− readSingle [] = error "readSingle: no more events"
200− readSingle (EventScalar bs:rest) =
201− (Scalar $ convertSuccess bs, rest)
202− readSingle (EventSequenceStart:rest) = readList [] rest
203− readSingle (EventMappingStart:rest) = readMap [] rest
204− readSingle (x:_) = error $ "readSingle: " ++ show x
205− readList :: [TextObject] -> [Event] -> (TextObject, [Event])
206− readList nodes (EventSequenceEnd:rest) =
207− (Sequence $ reverse nodes, rest)
208− readList nodes events =
209− let (next, rest) = readSingle events
210− in readList (next : nodes) rest
211− readMap :: [(B.ByteString, TextObject)]
213− -> (TextObject, [Event])
214− readMap pairs (EventMappingEnd:rest) =
215− (Mapping $ map (first convertSuccess) $ reverse pairs, rest)
216− readMap pairs (EventScalar bs:events) =
217− let (next, rest) = readSingle events
218− in readMap ((bs, next) : pairs) rest
219− readMap _ (e:_) = error $ "Unexpected event in readMap: " ++ show e
220− readMap _ [] = error "Unexpected empty event list in readMap"
223newtype MyString = MyString String
134newtype MyString = MyString String
224 deriving (Eq, Show)
135 deriving (Eq, Show)
225−instance ConvertAttempt MyString Text where
226− convertAttempt = return . convertSuccess
227instance ConvertSuccess MyString Text where
136instance ConvertSuccess MyString Text where
228 convertSuccess (MyString s) = convertSuccess s
137 convertSuccess (MyString s) = convertSuccess s
229−instance ConvertAttempt Text MyString where
230− convertAttempt = return . convertSuccess
231instance ConvertSuccess Text MyString where
138instance ConvertSuccess Text MyString where
232 convertSuccess = MyString . convertSuccess
139 convertSuccess = MyString . convertSuccess
140+instance ConvertSuccess (Object Text Text) (Object MyString MyString) where
141+ convertSuccess = mapKeysValues convertSuccess convertSuccess
142+instance ConvertSuccess (Object MyString MyString) (Object Text Text) where
143+ convertSuccess = mapKeysValues convertSuccess convertSuccess
234myEquals :: Eq v => Attempt v -> Attempt v -> Bool
145myEquals :: Eq v => Attempt v -> Attempt v -> Bool
235−myEquals (Success v1) (Success v2) = v1 == v2
146+myEquals a1 a2
236−myEquals _ _ = False
147+ | isSuccess a1 && isSuccess a2 = fromSuccess a1 == fromSuccess a2
238propEncodeDecode :: Object MyString MyString -> Bool
150propEncodeDecode :: Object MyString MyString -> Bool
239propEncodeDecode o =
151propEncodeDecode o =
240− fmap h2 (decodeText (encodeText (h1 o) :: B.ByteString))
152+ (decodeTextSuccess $ encodeText' $ convertSuccess o)
241− `myEquals` Success o
153+ `myEquals` return o
243− h1 = mapKeysValues convertSuccess convertSuccess
244− h2 = mapKeysValues convertSuccess convertSuccess
246−toSBS :: ConvertSuccess a B.ByteString => a -> B.ByteString
155+toSBS :: ConvertSuccess a ByteString => a -> ByteString
247toSBS = convertSuccess
156toSBS = convertSuccess
249toLBS :: ConvertSuccess a BL.ByteString => a -> BL.ByteString
158toLBS :: ConvertSuccess a BL.ByteString => a -> BL.ByteString
@@ -258,15 +167,19 @@ caseEmptyStrings = do
259 let m' = map (toSBS *** toSBS) m
168 let m' = map (toSBS *** toSBS) m
260 let m'' = map (toLBS *** toLBS) m
169 let m'' = map (toLBS *** toLBS) m
261− let test' :: (ToObject x Text Text, FromObject x Text Text, Eq x, Show x)
170+ let test' :: (ConvertSuccess x TextObject,
171+ ConvertSuccess TextObject x,
263 -> Assertion
175 -> Assertion
264− test' x = Just x @=?
176+ test' x = do
265− fa (decodeText (encodeText (toTextObject x) :: String)
177+ let bs = encodeText x
266− >>= fromTextObject)
178+ x' <- decodeTextSuccess bs
267− test' m
180+ test' $ toTextObject m
268− test' m'
181+ test' $ toTextObject m'
269− test' m''
182+ test' $ toTextObject m''
271caseLargeObjects :: Assertion
184caseLargeObjects :: Assertion
272caseLargeObjects = do
185caseLargeObjects = do
@@ -274,10 +187,10 @@ caseLargeObjects = do
274 level2 = toTextObject $ replicate 10000 level3
187 level2 = toTextObject $ replicate 10000 level3
275 level1 = toTextObject $ zip (map (: []) ['A'..'Z']) $ repeat level2
188 level1 = toTextObject $ zip (map (: []) ['A'..'Z']) $ repeat level2
276 decoded :: Maybe TextObject
189 decoded :: Maybe TextObject
277− decoded = decodeText $ (encodeText level1 :: Text)
190+ decoded = decodeText $ encodeText level1
278 assertEqual "encode/decode identity" decoded $ Just level1
191 assertEqual "encode/decode identity" decoded $ Just level1
279 putStrLn "encoding the file..."
192 putStrLn "encoding the file..."
280− encodeFile "test.yaml" level1
193+ B.writeFile "test.yaml" $ encodeText' level1
282testSuite :: Test
195testSuite :: Test
283testSuite = testGroup "Text.Yaml"
196testSuite = testGroup "Text.Yaml"