@@ -3,14 +3,16 @@
3{-# LANGUAGE MultiParamTypeClasses #-}
3{-# LANGUAGE MultiParamTypeClasses #-}
4{-# LANGUAGE DeriveDataTypeable #-}
4{-# LANGUAGE DeriveDataTypeable #-}
5{-# LANGUAGE TypeSynonymInstances #-}
5{-# LANGUAGE TypeSynonymInstances #-}
6module Data.Object.Yaml
7module Data.Object.Yaml
7− ( -- * The event stream
8+ ( -- * 'YamlDoc' definition and IO
8− Event (..)
9+ YamlDoc (..)
9− , Style (..)
10+ , readYamlDoc
10− , Tag (..)
11+ , writeYamlDoc
11 -- * 'YamlObject' definition
12 -- * 'YamlObject' definition
13 , YamlScalar (..)
13 , YamlScalar (..)
14 , YamlObject
16 , YamlObject
15 -- * Performing conversions
17 -- * Performing conversions
16 , eventsToYamlObject
18 , eventsToYamlObject
@@ -18,80 +20,53 @@ module Data.Object.Yaml
18 , YamlException (..)
20 , YamlException (..)
19 -- * Re-export
21 -- * Re-export
20 , module Data.Object.Base
22 , module Data.Object.Base
23import Data.ByteString (ByteString)
28import Data.ByteString (ByteString)
29+import qualified Data.ByteString as B
24import Data.Object.Base
30import Data.Object.Base
25import Data.Object.Text
31import Data.Object.Text
26−import Data.Object.Scalar
27import Data.Convertible.Text
32import Data.Convertible.Text
28import Data.Attempt
33import Data.Attempt
30−import Data.Typeable (Typeable)
35+import Data.Object.Yaml.Internal
36+import Data.Object.Yaml.Lib
31−import Control.Exception (Exception)
37+import Control.Monad ((<=<), join)
32−
38+import System.IO.Unsafe
35− | EventStreamStart
41+import Data.Object
36− | EventStreamEnd
42+import Test.Framework (testGroup, Test)
37− | EventDocumentStart
43+import Test.Framework.Providers.HUnit
38− | EventDocumentEnd
44+import Test.Framework.Providers.QuickCheck (testProperty)
39− | EventAlias
45+import Test.HUnit hiding (Test, path)
40− | EventScalar ByteString Tag Style
46+import Test.QuickCheck
47−data Style = Any
48+import Control.Monad (replicateM)
48− | Plain
49+import qualified Data.Text.Lazy as LT
49− | SingleQuoted
50+import qualified Data.ByteString.Lazy as BL
50− | DoubleQuoted
51+import qualified Data.ByteString as B
51− | Literal
52+import Data.Attempt
52− | Folded
53+import Control.Arrow ((***))
53− deriving (Show, Eq, Enum, Bounded, Ord)
54+#endif
66−instance Show Tag where
67− show StrTag = "tag:yaml.org,2002:str"
68− show FloatTag = "tag:yaml.org,2002:float"
69− show NullTag = "tag:yaml.org,2002:null"
70− show BoolTag = "tag:yaml.org,2002:bool"
71− show SetTag = "tag:yaml.org,2002:set"
72− show IntTag = "tag:yaml.org,2002:int"
73− show SeqTag = "tag:yaml.org,2002:seq"
74− show MapTag = "tag:yaml.org,2002:map"
77−instance ConvertSuccess Tag [Char] where
78− convertSuccess = show
79−instance ConvertSuccess ByteString Tag where
80− convertSuccess = (convertSuccess :: String -> Tag)
82−instance ConvertSuccess [Char] Tag where
83− convertSuccess "tag:yaml.org,2002:str" = StrTag
84− convertSuccess "tag:yaml.org,2002:float" = FloatTag
85− convertSuccess "tag:yaml.org,2002:null" = NullTag
86− convertSuccess "tag:yaml.org,2002:bool" = BoolTag
87− convertSuccess "tag:yaml.org,2002:set" = SetTag
88− convertSuccess "tag:yaml.org,2002:int" = IntTag
89− convertSuccess "tag:yaml.org,2002:seq" = SeqTag
90− convertSuccess "tag:yaml.org,2002:map" = MapTag
91− convertSuccess "" = NoTag
92− convertSuccess s = UriTag s
94newtype YamlDoc = YamlDoc { unYamlDoc :: ByteString }
56newtype YamlDoc = YamlDoc { unYamlDoc :: ByteString }
57+instance ConvertSuccess [Event] YamlDoc where
58+ -- Note: the join here will swallow up 'YamlException's.
59+ -- However, these should never occur, as that would imply an error
60+ -- in the C library or bindings.
61+ convertSuccess = YamlDoc . unsafePerformIO . join . encode
62+instance ConvertAttempt YamlDoc [Event] where
63+ convertAttempt = unsafePerformIO . decode . unYamlDoc
65+readYamlDoc :: FilePath -> IO YamlDoc
66+readYamlDoc = fmap YamlDoc . B.readFile
68+writeYamlDoc :: FilePath -> YamlDoc -> IO ()
69+writeYamlDoc fp = B.writeFile fp . unYamlDoc
96data YamlScalar = YamlScalar
71data YamlScalar = YamlScalar
97 { value :: ByteString
72 { value :: ByteString
@@ -102,9 +77,9 @@ data YamlScalar = YamlScalar
102instance ConvertSuccess YamlScalar Event where
77instance ConvertSuccess YamlScalar Event where
103 convertSuccess (YamlScalar v t s) = EventScalar v (convertSuccess t) s
78 convertSuccess (YamlScalar v t s) = EventScalar v (convertSuccess t) s
105−instance ConvertSuccess YamlScalar [Char] where
80+instance ConvertSuccess YamlScalar String where
106 convertSuccess = convertSuccess . value
81 convertSuccess = convertSuccess . value
107−instance ConvertSuccess [Char] YamlScalar where
82+instance ConvertSuccess String YamlScalar where
108 convertSuccess t = YamlScalar (convertSuccess t) NoTag Any
83 convertSuccess t = YamlScalar (convertSuccess t) NoTag Any
110instance ConvertSuccess YamlScalar Text where
85instance ConvertSuccess YamlScalar Text where
@@ -112,31 +87,16 @@ instance ConvertSuccess YamlScalar Text where
112instance ConvertSuccess Text YamlScalar where
87instance ConvertSuccess Text YamlScalar where
113 convertSuccess t = YamlScalar (convertSuccess t) NoTag Any
88 convertSuccess t = YamlScalar (convertSuccess t) NoTag Any
115−-- FIXME the following are incredibly stupid conversions which ignore tag
90+-- FIXME add Scalar instances
116−-- and style information.
91+{-
117instance ConvertSuccess YamlScalar Scalar where
92instance ConvertSuccess YamlScalar Scalar where
118 convertSuccess = Text . convertSuccess
93 convertSuccess = Text . convertSuccess
119instance ConvertSuccess Scalar YamlScalar where
94instance ConvertSuccess Scalar YamlScalar where
120 convertSuccess s = convertSuccess (convertSuccess s :: Text)
95 convertSuccess s = convertSuccess (convertSuccess s :: Text)
122type YamlObject = Object YamlScalar YamlScalar
98type YamlObject = Object YamlScalar YamlScalar
126− { parserProblem :: String
127− , parserContext :: String
128− , parserOffset :: Int
130− | YamlEmitterException { emitterProblem :: String }
132− | YamlInvalidEventStreamBeginning [Event]
133− | YamlInvalidEventStreamEnd [Event]
134− | YamlPrematureEventStreamEnd
136− | YamlInvalidStartingEvent Event
137− deriving (Show, Typeable)
138−instance Exception YamlException
140-- Emit a YamlObject to an event stream
100-- Emit a YamlObject to an event stream
141instance ConvertSuccess YamlObject [Event] where
101instance ConvertSuccess YamlObject [Event] where
142 convertSuccess o = EventStreamStart
102 convertSuccess o = EventStreamStart
@@ -152,8 +112,15 @@ instance ConvertSuccess YamlObject [Event] where
152 : foldr ($) (EventMappingEnd : rest) (map helperPairs pairs)
112 : foldr ($) (EventMappingEnd : rest) (map helperPairs pairs)
153 helperPairs :: (YamlScalar, YamlObject) -> [Event] -> [Event]
113 helperPairs :: (YamlScalar, YamlObject) -> [Event] -> [Event]
154 helperPairs (k, v) rest = convertSuccess k : helper v rest
114 helperPairs (k, v) rest = convertSuccess k : helper v rest
115+instance ConvertSuccess YamlObject YamlDoc where
116+ convertSuccess = cs . (cs :: YamlObject -> [Event])
117+instance ConvertAttempt YamlDoc YamlObject where
118+ convertAttempt = ca <=< (ca :: YamlDoc -> Attempt [Event])
120+instance ConvertAttempt [Event] YamlObject where
121+ convertAttempt = eventsToYamlObject
156−-- Parse a YamlObject from an event stream
123+-- | Parse a YamlObject from an event stream
157eventsToYamlObject :: MonadFailure YamlException m
124eventsToYamlObject :: MonadFailure YamlException m
158 => [Event]
125 => [Event]
159 -> m YamlObject
126 -> m YamlObject
@@ -204,12 +171,123 @@ eventsToYamlObject (EventStreamStart:EventDocumentStart:events) = h1 events
204 readMap (f . ((:) (key', val))) rest'
171 readMap (f . ((:) (key', val))) rest'
205eventsToYamlObject e = failure $ YamlInvalidEventStreamBeginning e
172eventsToYamlObject e = failure $ YamlInvalidEventStreamBeginning e
207−instance ConvertSuccess (Object YamlScalar YamlScalar) (Object Text Text) where
174+instance ConvertSuccess YamlObject TextObject where
175+ convertSuccess = mapKeysValues cs cs
176+instance ConvertSuccess TextObject YamlObject where
208− convertSuccess = mapKeysValues convertSuccess convertSuccess
177+ convertSuccess = mapKeysValues cs cs
209−instance ConvertSuccess (Object Text Text) (Object YamlScalar YamlScalar) where
178+instance ConvertSuccess TextObject YamlDoc where
210− convertSuccess = mapKeysValues convertSuccess convertSuccess
179+ convertSuccess = cs . (cs :: TextObject -> YamlObject)
180+instance ConvertAttempt YamlDoc TextObject where
181+ convertAttempt = fmap cs . (ca :: YamlDoc -> Attempt YamlObject)
212instance ConvertSuccess (Object YamlScalar YamlScalar) (Object String Scalar) where
184instance ConvertSuccess (Object YamlScalar YamlScalar) (Object String Scalar) where
213 convertSuccess = mapKeysValues convertSuccess convertSuccess
185 convertSuccess = mapKeysValues convertSuccess convertSuccess
214instance ConvertSuccess (Object String Scalar) (Object YamlScalar YamlScalar) where
186instance ConvertSuccess (Object String Scalar) (Object YamlScalar YamlScalar) where
215 convertSuccess = mapKeysValues convertSuccess convertSuccess
187 convertSuccess = mapKeysValues convertSuccess convertSuccess
191+newtype MyString = MyString String
193+instance ConvertSuccess MyString Text where
194+ convertSuccess (MyString s) = convertSuccess s
195+instance ConvertSuccess Text MyString where
196+ convertSuccess = MyString . convertSuccess
197+instance ConvertSuccess (Object Text Text) (Object MyString MyString) where
198+ convertSuccess = mapKeysValues convertSuccess convertSuccess
199+instance ConvertSuccess (Object MyString MyString) (Object Text Text) where
200+ convertSuccess = mapKeysValues convertSuccess convertSuccess
202+myEquals :: Eq v => Attempt v -> Attempt v -> Bool
204+ | isSuccess a1 && isSuccess a2 = fromSuccess a1 == fromSuccess a2
207+propEncodeDecode :: Object MyString MyString -> Bool
209+ let to :: TextObject
214+ in return to `myEquals` to'
216+toSBS :: ConvertSuccess a ByteString => a -> ByteString
217+toSBS = convertSuccess
219+toLBS :: ConvertSuccess a BL.ByteString => a -> BL.ByteString
220+toLBS = convertSuccess
222+caseEmptyStrings :: Assertion
223+caseEmptyStrings = do
229+ let m' = map (toSBS *** toSBS) m
230+ let m'' = map (toLBS *** toLBS) m
231+ let test' :: (ConvertSuccess x TextObject,
232+ ConvertSuccess TextObject x,
238+ let to :: TextObject
242+ to' :: Attempt TextObject
246+ test' $ toTextObject m
247+ test' $ toTextObject m'
248+ test' $ toTextObject m''
251+-- FIXME remove the following
252+instance ToObject [(String, String)] Text Text where
253+ toObject = Mapping . map (cs *** toObject)
254+instance ToObject [(ByteString, ByteString)] Text Text where
255+ toObject = Mapping . map (cs *** toObject)
256+instance ToObject [(BL.ByteString, BL.ByteString)] Text Text where
257+ toObject = Mapping . map (cs *** toObject)
258+instance ToObject [(String, TextObject)] Text Text where
259+ toObject = Mapping . map (cs *** id)
262+caseLargeObjects :: Assertion
263+caseLargeObjects = do
264+ let level3 = toTextObject (convertSuccess "X" :: Text)
265+ level2 = toTextObject $ replicate 10000 level3
266+ level1 = toTextObject $ zip (map (: []) ['A'..'Z']) $ repeat level2
267+ decoded :: Maybe TextObject
268+ decoded = fa $ ca $ (cs level1 :: YamlDoc)
269+ assertEqual "encode/decode identity" decoded $ Just level1
270+ putStrLn "encoding the file..."
271+ writeYamlDoc "test.yaml" $ cs level1
274+testSuite = testGroup "Text.Yaml"
275+ [ testProperty "propEncodeDecode" propEncodeDecode
276+ , testCase "empty strings" caseEmptyStrings
277+ , testCase "encode large objects" caseLargeObjects
280+instance Arbitrary (Object MyString MyString) where
281+ coarbitrary = undefined
282+ arbitrary = oneof [arbS, arbL, arbM] where
283+ arbS = Scalar `fmap` (arbitrary :: Gen MyString)
284+ arbL = Sequence `fmap` vector 1
285+ arbM = Mapping `fmap` vector 1
287+instance Arbitrary MyString where
288+ coarbitrary = undefined
291+ s <- replicateM (size `mod` 5) $ elements ['A'..'Z']
292+ return $! MyString s