hexpat-pickle-generic 0.1.3 → 0.1.5
raw patch · 3 files changed
+435/−79 lines, 3 filesdep +QuickCheckdep +hexpatdep +hexpat-pickle-genericdep −hexpat-pickledep −textPVP: major bump suggested
API removals or changes: PVP suggests a major version bump
Dependencies added: QuickCheck, hexpat, hexpat-pickle-generic, test-framework, test-framework-quickcheck2
Dependencies removed: hexpat-pickle, text
API changes (from Hackage documentation)
- Text.XML.Expat.Pickle.Generic: PU :: (t -> a) -> (t -> Either String a) -> (a -> t) -> PU t a
- Text.XML.Expat.Pickle.Generic: data PU t a :: * -> * -> *
- Text.XML.Expat.Pickle.Generic: decodeXML :: IsXML a => ByteString -> Either String a
- Text.XML.Expat.Pickle.Generic: encodeXML :: IsXML a => a -> ByteString
- Text.XML.Expat.Pickle.Generic: instance (Constructor c, GIsXML [UNode ByteString] f) => GIsXML [UNode ByteString] (M1 C c f)
- Text.XML.Expat.Pickle.Generic: instance (Datatype d, GIsXML t f) => GIsXML t (M1 D d f)
- Text.XML.Expat.Pickle.Generic: instance (GIsXML [t] f, GIsXML [t] g) => GIsXML [t] (f :*: g)
- Text.XML.Expat.Pickle.Generic: instance (GIsXML t f, GIsXML t g) => GIsXML t (f :+: g)
- Text.XML.Expat.Pickle.Generic: instance (Selector s, GIsXML [UNode ByteString] f) => GIsXML [UNode ByteString] (M1 S s f)
- Text.XML.Expat.Pickle.Generic: instance GIsXML [t] U1
- Text.XML.Expat.Pickle.Generic: instance IsXML ByteString
- Text.XML.Expat.Pickle.Generic: instance IsXML Int
- Text.XML.Expat.Pickle.Generic: instance IsXML Integer
- Text.XML.Expat.Pickle.Generic: instance IsXML Text
- Text.XML.Expat.Pickle.Generic: instance IsXML a => GIsXML [UNode ByteString] (K1 i a)
- Text.XML.Expat.Pickle.Generic: instance IsXML a => IsXML (Maybe a)
- Text.XML.Expat.Pickle.Generic: instance IsXML a => IsXML [a]
- Text.XML.Expat.Pickle.Generic: unpickleTree' :: PU t a -> t -> Either String a
- Text.XML.Expat.Pickle.Generic: xpGenericString :: GenericXMLString t => PU [UNode ByteString] t
- Text.XML.Expat.Pickle.Generic: xpList0 :: Show tag => PU [Node tag text] a -> PU [Node tag text] [a]
+ Text.XML.Expat.Pickle.Generic: XMLPU :: (a -> t) -> (t -> Either String a) -> Maybe ByteString -> XMLPU t a
+ Text.XML.Expat.Pickle.Generic: data XMLPU t a
+ Text.XML.Expat.Pickle.Generic: fromXML :: IsXML a => ByteString -> Either String a
+ Text.XML.Expat.Pickle.Generic: instance [overlap ok] (Constructor c, GIsXML a) => GIsXML (C1 c a)
+ Text.XML.Expat.Pickle.Generic: instance [overlap ok] (Datatype d, GIsXML a) => GIsXML (D1 d a)
+ Text.XML.Expat.Pickle.Generic: instance [overlap ok] (GIsXML a, GIsXML b) => GIsXML (a :*: b)
+ Text.XML.Expat.Pickle.Generic: instance [overlap ok] (GIsXML a, GIsXML b) => GIsXML (a :+: b)
+ Text.XML.Expat.Pickle.Generic: instance [overlap ok] (IsXML a, IsXML b) => IsXML (Either a b)
+ Text.XML.Expat.Pickle.Generic: instance [overlap ok] (Selector s, GIsXML a) => GIsXML (S1 s a)
+ Text.XML.Expat.Pickle.Generic: instance [overlap ok] (Selector s, IsXML a) => GIsXML (S1 s (K1 i [a]))
+ Text.XML.Expat.Pickle.Generic: instance [overlap ok] IsXML ByteString
+ Text.XML.Expat.Pickle.Generic: instance [overlap ok] IsXML Double
+ Text.XML.Expat.Pickle.Generic: instance [overlap ok] IsXML Float
+ Text.XML.Expat.Pickle.Generic: instance [overlap ok] IsXML Int
+ Text.XML.Expat.Pickle.Generic: instance [overlap ok] IsXML Integer
+ Text.XML.Expat.Pickle.Generic: instance [overlap ok] IsXML a => GIsXML (K1 i a)
+ Text.XML.Expat.Pickle.Generic: instance [overlap ok] IsXML a => IsXML (Maybe a)
+ Text.XML.Expat.Pickle.Generic: root :: XMLPU t a -> Maybe ByteString
+ Text.XML.Expat.Pickle.Generic: rootXMLPickler :: (Generic a, GIsXML (Rep a)) => ByteString -> XMLPU [Node] a
+ Text.XML.Expat.Pickle.Generic: toIndentedXML :: IsXML a => Int -> a -> ByteString
+ Text.XML.Expat.Pickle.Generic: toXML :: IsXML a => a -> ByteString
+ Text.XML.Expat.Pickle.Generic: xmlListElement :: XMLOptions -> ByteString
+ Text.XML.Expat.Pickle.Generic: xpElem :: ByteString -> PU [Node] a -> PU [Node] a
+ Text.XML.Expat.Pickle.Generic: xpElemList :: ByteString -> PU [Node] a -> PU [Node] [a]
+ Text.XML.Expat.Pickle.Generic: xpEmpty :: (Read a, Show a) => PU [Node] a
+ Text.XML.Expat.Pickle.Generic: xpLift :: a -> PU [n] a
+ Text.XML.Expat.Pickle.Generic: xpOption :: PU [n] a -> PU [n] (Maybe a)
+ Text.XML.Expat.Pickle.Generic: xpPair :: PU [n] a -> PU [n] b -> PU [n] (a, b)
+ Text.XML.Expat.Pickle.Generic: xpUnit :: PU [n] ()
- Text.XML.Expat.Pickle.Generic: XMLOptions :: (String -> String) -> (String -> String) -> XMLOptions
+ Text.XML.Expat.Pickle.Generic: XMLOptions :: (String -> ByteString) -> (String -> ByteString) -> ByteString -> XMLOptions
- Text.XML.Expat.Pickle.Generic: genericXMLPickler :: (Generic x, GIsXML t (Rep x)) => Options -> PU t x
+ Text.XML.Expat.Pickle.Generic: genericXMLPickler :: (Generic x, GIsXML (Rep x)) => Options -> PU [Node] x
- Text.XML.Expat.Pickle.Generic: pickleTree :: PU t a -> a -> t
+ Text.XML.Expat.Pickle.Generic: pickleTree :: XMLPU t a -> a -> t
- Text.XML.Expat.Pickle.Generic: unpickleTree :: PU t a -> t -> a
+ Text.XML.Expat.Pickle.Generic: unpickleTree :: XMLPU t a -> t -> Either String a
- Text.XML.Expat.Pickle.Generic: xmlCtorModifier :: XMLOptions -> String -> String
+ Text.XML.Expat.Pickle.Generic: xmlCtorModifier :: XMLOptions -> String -> ByteString
- Text.XML.Expat.Pickle.Generic: xmlFieldModifier :: XMLOptions -> String -> String
+ Text.XML.Expat.Pickle.Generic: xmlFieldModifier :: XMLOptions -> String -> ByteString
- Text.XML.Expat.Pickle.Generic: xmlPickler :: IsXML a => PU [UNode ByteString] a
+ Text.XML.Expat.Pickle.Generic: xmlPickler :: IsXML a => PU [Node] a
- Text.XML.Expat.Pickle.Generic: xpContent :: GenericXMLString text => PU text a -> PU [Node tag text] a
+ Text.XML.Expat.Pickle.Generic: xpContent :: PU ByteString a -> PU [Node] a
- Text.XML.Expat.Pickle.Generic: xpEither :: PU t a -> PU t b -> PU t (Either a b)
+ Text.XML.Expat.Pickle.Generic: xpEither :: PU [t] a -> PU [t] b -> PU [t] (Either a b)
- Text.XML.Expat.Pickle.Generic: xpList :: Show tag => PU [Node tag text] a -> PU [Node tag text] [a]
+ Text.XML.Expat.Pickle.Generic: xpList :: PU [Node] a -> PU [Node] [a]
- Text.XML.Expat.Pickle.Generic: xpPrim :: (Read b, Show b, GenericXMLString t) => PU [Node a t] b
+ Text.XML.Expat.Pickle.Generic: xpPrim :: (Read a, Show a) => PU ByteString a
- Text.XML.Expat.Pickle.Generic: xpSum :: PU t (f r) -> PU t (g r) -> PU t ((f :+: g) r)
+ Text.XML.Expat.Pickle.Generic: xpSum :: PU [t] (f r) -> PU [t] (g r) -> PU [t] ((f :+: g) r)
- Text.XML.Expat.Pickle.Generic: xpText :: GenericXMLString text => PU text text
+ Text.XML.Expat.Pickle.Generic: xpText :: PU ByteString ByteString
- Text.XML.Expat.Pickle.Generic: xpText0 :: PU text text
+ Text.XML.Expat.Pickle.Generic: xpText0 :: PU ByteString ByteString
- Text.XML.Expat.Pickle.Generic: xpWrap :: (a -> b, b -> a) -> PU t a -> PU t b
+ Text.XML.Expat.Pickle.Generic: xpWrap :: (a -> b, b -> a) -> PU [n] a -> PU [n] b
Files
- hexpat-pickle-generic.cabal +22/−6
- src/Text/XML/Expat/Pickle/Generic.hs +242/−73
- test/Main.hs +171/−0
hexpat-pickle-generic.cabal view
@@ -1,5 +1,5 @@ name: hexpat-pickle-generic-version: 0.1.3+version: 0.1.5 synopsis: Picklers for de/serialising Generic data types to and from XML license: BSD3 license-file: LICENSE@@ -12,8 +12,7 @@ cabal-version: >= 1.10 description:- An IsXML class and GHC.Generics implementation for pickleable- data types using hexpat and hexpat-pickle.+ An IsXML class and GHC.Generics implementation for pickleable data types using hexpat. extra-source-files: README.md@@ -34,7 +33,24 @@ -O2 build-depends:- base >= 4.6 && < 5+ base >= 4.6 && < 5 , bytestring- , hexpat-pickle- , text+ , hexpat++test-suite hexpat-pickle-generic-tests+ default-language: Haskell2010+ type: exitcode-stdio-1.0+ hs-source-dirs: test+ main-is: Main.hs++ ghc-options:+ -Wall+ -fwarn-tabs++ build-depends:+ base >= 4.6 && < 5+ , bytestring+ , hexpat-pickle-generic+ , QuickCheck >= 2.5+ , test-framework >= 0.8+ , test-framework-quickcheck2 >= 0.3
src/Text/XML/Expat/Pickle/Generic.hs view
@@ -1,10 +1,16 @@ {-# LANGUAGE DefaultSignatures #-}+{-# LANGUAGE DefaultSignatures #-}+{-# LANGUAGE DeriveGeneric #-} {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE FunctionalDependencies #-}+{-# LANGUAGE KindSignatures #-} {-# LANGUAGE MultiParamTypeClasses #-}+{-# LANGUAGE OverlappingInstances #-} {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE TypeOperators #-}+{-# LANGUAGE UndecidableInstances #-} {-# LANGUAGE ViewPatterns #-} {-# OPTIONS_GHC -fno-warn-missing-signatures #-}@@ -25,11 +31,12 @@ IsXML (..) -- * Functions- , encodeXML- , decodeXML+ , toXML+ , toIndentedXML+ , fromXML - -- * Re-exported Data Types- , PU (..)+ -- * Data Types+ , XMLPU (..) -- * Options , XMLOptions (..)@@ -37,65 +44,101 @@ -- * Generics , genericXMLPickler+ , rootXMLPickler -- * Combinators+ , xpWrap+ , xpList+ , xpElemList+ , xpElem , xpSum , xpEither- , xpGenericString-- -- * Re-exported Combinators- , xpText0+ , xpPrim+ , xpEmpty+ , xpOption+ , xpPair+ , xpUnit+ , xpLift , xpText- , xpList0- , xpList+ , xpText0 , xpContent- , xpPrim- , xpWrap ) where import Data.ByteString (ByteString)-import Data.Char (isLower)-import Data.Text (Text)+import qualified Data.ByteString.Char8 as BS+import Data.Char (isLower, isSpace)+import Data.Either+import Data.Maybe+import Data.Monoid import GHC.Generics-import qualified Text.XML.Expat.Pickle as Pickle-import Text.XML.Expat.Pickle hiding (xpPrim)+import Text.XML.Expat.Format+import Text.XML.Expat.Tree hiding (Node) -- -- Class -- +type Node = UNode ByteString++data XMLPU t a = XMLPU+ { pickleTree :: a -> t+ , unpickleTree :: t -> Either String a+ , root :: Maybe ByteString+ }++type PU = XMLPU+ class IsXML a where- xmlPickler :: PU [UNode ByteString] a+ xmlPickler :: PU [Node] a - default xmlPickler :: (Generic a, GIsXML [UNode ByteString] (Rep a))- => PU [UNode ByteString] a+ default xmlPickler :: (Generic a, GIsXML (Rep a)) => PU [Node] a xmlPickler = genericXMLPickler defaultXMLOptions -- -- Functions -- -encodeXML :: IsXML a => a -> ByteString-encodeXML = pickleXML' (xpRoot xmlPickler)+toXML :: IsXML a => a -> ByteString+toXML = format' . maybe head (\n -> Element n []) (root pu) . pickleTree pu+ where+ pu = xmlPickler -decodeXML :: IsXML a => ByteString -> Either String a-decodeXML = unpickleXML' defaultParseOptions (xpRoot xmlPickler)+toIndentedXML :: IsXML a => Int -> a -> ByteString+toIndentedXML i = format'+ . indent i+ . maybe head (\n -> Element n []) (root pu)+ . pickleTree pu+ where+ pu = xmlPickler +fromXML :: IsXML a => ByteString -> Either String a+fromXML = either (Left . show) unwrap . parse' defaultParseOptions+ where+ unwrap e@(Element n _ cs) = case root pu of+ Just x | x == n -> unpickleTree pu cs+ Just _ -> Left "Unexpected root element"+ Nothing -> unpickleTree pu [e]+ unwrap _ = Left "Unexpected root element"++ pu = xmlPickler+ ----- Defining Picklers+-- Options -- data XMLOptions = XMLOptions- { xmlCtorModifier :: String -> String+ { xmlCtorModifier :: String -> ByteString -- ^ Function applied to constructor tags.- , xmlFieldModifier :: String -> String+ , xmlFieldModifier :: String -> ByteString -- ^ Function applied to record field labels.+ , xmlListElement :: ByteString+ -- ^ Default element name to wrap list items with. } type Options = XMLOptions defaultXMLOptions :: Options-defaultXMLOptions = XMLOptions id (dropWhile isLower)+defaultXMLOptions = XMLOptions BS.pack (BS.pack . dropWhile isLower) "Value" -- -- Generics@@ -104,46 +147,93 @@ genericXMLPickler opts = (to, from) `xpWrap` (gXMLPickler opts) (genericXMLPickler opts) -class GIsXML t f where- gXMLPickler :: Options -> PU t a -> PU t (f a)+rootXMLPickler name =+ (genericXMLPickler defaultXMLOptions) { root = Just name } -instance IsXML a => GIsXML [UNode ByteString] (K1 i a) where- gXMLPickler _ _ = (K1, unK1) `xpWrap` xmlPickler+class GIsXML f where+ gXMLPickler :: Options -> PU [Node] a -> PU [Node] (f a) -instance GIsXML [t] U1 where- gXMLPickler _ _ = (const U1, const ()) `xpWrap` xpUnit+instance IsXML a => GIsXML (K1 i a) where+ gXMLPickler _ _ = (K1, unK1) `xpWrap` xmlPickler -instance (GIsXML t f, GIsXML t g) => GIsXML t (f :+: g) where+instance (GIsXML a, GIsXML b) => GIsXML (a :+: b) where gXMLPickler opts f = gXMLPickler opts f `xpSum` gXMLPickler opts f -instance (GIsXML [t] f, GIsXML [t] g) => GIsXML [t] (f :*: g) where+instance (GIsXML a, GIsXML b) => GIsXML (a :*: b) where gXMLPickler opts f = xpWrap (uncurry (:*:), \(a :*: b) -> (a, b)) (gXMLPickler opts f `xpPair` gXMLPickler opts f) -instance (Datatype d, GIsXML t f) => GIsXML t (M1 D d f) where+instance (Datatype d, GIsXML a) => GIsXML (D1 d a) where gXMLPickler opts = xpWrap (M1, unM1) . gXMLPickler opts -instance (Constructor c, GIsXML [UNode ByteString] f)- => GIsXML [UNode ByteString] (M1 C c f) where- gXMLPickler opts f = xpElemNodes- (gxFromString . xmlCtorModifier opts $ conName (undefined :: M1 C c f r))- ((M1, unM1) `xpWrap` gXMLPickler opts f)+instance (Constructor c, GIsXML a) => GIsXML (C1 c a) where+ gXMLPickler opts f = (xpWrap (M1, unM1) $ gXMLPickler opts f)+ { root = Just . xmlCtorModifier opts $ conName (undefined :: C1 c a p)+ } -instance (Selector s, GIsXML [UNode ByteString] f)- => GIsXML [UNode ByteString] (M1 S s f) where- gXMLPickler opts f = xpElemNodes- (gxFromString . xmlFieldModifier opts $ selName (undefined :: M1 S s f r))+instance (Selector s, GIsXML a) => GIsXML (S1 s a) where+ gXMLPickler opts f = xpElem+ (xmlFieldModifier opts $ selName (undefined :: S1 s a p)) ((M1, unM1) `xpWrap` gXMLPickler opts f) +instance (Selector s, IsXML a) => GIsXML (S1 s (K1 i [a])) where+ gXMLPickler opts _ = xpElem+ (xmlFieldModifier opts $ selName (undefined :: t s (K1 i [a]) p))+ ((M1 . K1, unK1 . unM1) `xpWrap` xpList (xpElem key pu))+ where+ key = fromMaybe (xmlListElement opts) $ root pu+ pu = xmlPickler+ -- -- Combinators -- -xpPrim :: (Read b, Show b, GenericXMLString t) => PU [Node a t] b-xpPrim = xpContent Pickle.xpPrim+xpWrap :: (a -> b, b -> a) -> PU [n] a -> PU [n] b+xpWrap (f, g) pu = XMLPU+ { pickleTree = pickleTree pu . g+ , unpickleTree = fmap f . unpickleTree pu+ , root = root pu+ } -xpSum :: PU t (f r) -> PU t (g r) -> PU t ((f :+: g) r)+xpElemList :: ByteString -> PU [Node] a -> PU [Node] [a]+xpElemList name = xpList . xpElem name++xpList :: PU [Node] a -> PU [Node] [a]+xpList pu = XMLPU+ { pickleTree = concatMap (pickleTree pu)+ , unpickleTree = concatEithers . unpickle+ , root = root pu+ }+ where+ unpickle (e@(Element _ _ _):es) = unpickleTree pu [e] : unpickle es+ unpickle (_:es) = unpickle es+ unpickle [] = []++ concatEithers xs = case partitionEithers xs of+ ([], rs) -> Right rs+ (l:_, _) -> Left l++xpElem :: ByteString -> PU [Node] a -> PU [Node] a+xpElem name pu = XMLPU+ { pickleTree = \x -> [Element name [] (pickleTree pu x)]+ , unpickleTree = \t ->+ let children = map matching t+ in case catMaybes children of+ [] -> Left $ "can't find " ++ tag+ (x:_) -> case x of+ Left e -> Left $ "in " ++ tag ++ ", " ++ e+ r -> r+ , root = Just name+ }+ where+ matching (Element n _ cs)+ | n == name = Just $ unpickleTree pu cs+ matching _ = Nothing++ tag = "<" ++ gxToString name ++ ">"++xpSum :: PU [t] (f r) -> PU [t] (g r) -> PU [t] ((f :+: g) r) xpSum left right = (inp, out) `xpWrap` xpEither left right where inp (Left x) = L1 x@@ -152,42 +242,121 @@ out (L1 x) = Left x out (R1 x) = Right x -xpEither :: PU t a -> PU t b -> PU t (Either a b)-xpEither ~(PU _ uea pa) ~(PU ub ueb pb) =- PU unpickle unpickleEither pickle- where- unpickle t = case uea t of- Right x -> Left x- Left _ -> Right $ ub t+xpEither :: PU [t] a -> PU [t] b -> PU [t] (Either a b)+xpEither pa pb = XMLPU+ { pickleTree = either (pickleTree pa) (pickleTree pb)+ , unpickleTree = \t -> case unpickleTree pa t of+ Right x -> Right . Left $ x+ Left _ -> Right `fmap` unpickleTree pb t+ , root = listToMaybe $ catMaybes [root pa, root pb]+ } - unpickleEither t = case uea t of- Right x -> Right . Left $ x- Left _ -> Right `fmap` ueb t+xpPrim :: (Read a, Show a) => PU ByteString a+xpPrim = XMLPU+ { pickleTree = BS.pack . show+ , unpickleTree = \t ->+ let s = BS.unpack t+ in case reads s of+ [(x, "")] -> Right x+ _ -> Left $ "failed to read text: " ++ s+ , root = Nothing+ } - pickle (Left x) = pa x- pickle (Right y) = pb y+xpEmpty :: (Read a, Show a) => PU [Node] a+xpEmpty = XMLPU+ { pickleTree = \x -> [Element (BS.pack $ show x) [] []]+ , unpickleTree = \t -> case t of+ [(Element n [] [])] -> let s = BS.unpack n+ in case reads s of+ [(x, "")] -> Right x+ _ -> Left $ "failed to read text: " ++ s+ _ -> Left "Expected empty element"+ , root = Nothing+ } -xpGenericString :: GenericXMLString t => PU [UNode ByteString] t-xpGenericString = (gxFromByteString, gxToByteString) `xpWrap` xpContent xpText0+xpOption :: PU [n] a -> PU [n] (Maybe a)+xpOption pu = XMLPU+ { pickleTree = maybe [] (pickleTree pu)+ , unpickleTree = Right . either (const Nothing) Just . unpickleTree pu+ , root = root pu+ } +xpPair :: PU [n] a -> PU [n] b -> PU [n] (a, b)+xpPair pa pb = XMLPU+ { pickleTree = \(a, b) -> pickleTree pa a ++ pickleTree pb b+ , unpickleTree = \t ->+ case (unpickleTree pa t, unpickleTree pb t) of+ (Right a, Right b) -> Right (a, b)+ (Left e, _) -> Left $ "in 1st of pair, " ++ e+ (_, Left e) -> Left $ "in 2nd of pair, " ++ e+ , root = listToMaybe $ catMaybes [root pa, root pb]+ }++xpUnit :: PU [n] ()+xpUnit = xpLift ()++xpLift :: a -> PU [n] a+xpLift a = XMLPU+ { pickleTree = const []+ , unpickleTree = const $ Right a+ , root = Nothing+ }++xpText :: PU ByteString ByteString+xpText = XMLPU+ { pickleTree = id+ , unpickleTree = \t -> if BS.null t then Left "empty text" else Right t+ , root = Nothing+ }++xpText0 :: PU ByteString ByteString+xpText0 = XMLPU+ { pickleTree = id+ , unpickleTree = Right+ , root = Nothing+ }++xpContent :: PU ByteString a -> PU [Node] a+xpContent pu = XMLPU+ { pickleTree = \t ->+ let txt = pickleTree pu t+ in if gxNullString txt then [] else [Text txt]+ , unpickleTree = unpickleTree pu . mconcat . map extract+ , root = root pu+ }+ where+ extract (Element _ _ cs) = strip . mconcat $ map extract cs+ extract (Text txt) = strip txt++ strip = snd . BS.break valid . fst . BS.breakEnd valid++ valid c+ | isSpace c = False+ | c == '\r' = False+ | c == '\n' = False+ | otherwise = True+ -- -- Instances -- +instance IsXML a => IsXML (Maybe a) where+ xmlPickler = xpOption xmlPickler++instance (IsXML a, IsXML b) => IsXML (Either a b) where+ xmlPickler = xmlPickler `xpEither` xmlPickler+ instance IsXML Int where- xmlPickler = xpPrim+ xmlPickler = xpContent xpPrim instance IsXML Integer where- xmlPickler = xpPrim--instance IsXML Text where- xmlPickler = xpGenericString+ xmlPickler = xpContent xpPrim -instance IsXML ByteString where- xmlPickler = xpGenericString+instance IsXML Double where+ xmlPickler = xpContent xpPrim -instance IsXML a => IsXML (Maybe a) where- xmlPickler = xpOption xmlPickler+instance IsXML Float where+ xmlPickler = xpContent xpPrim -instance IsXML a => IsXML [a] where- xmlPickler = xpList0 xmlPickler+instance IsXML ByteString where+ xmlPickler = xpContent xpText
+ test/Main.hs view
@@ -0,0 +1,171 @@+{-# LANGUAGE DeriveGeneric #-}+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE RecordWildCards #-}++{-# OPTIONS_GHC -fno-warn-orphans #-}+{-# OPTIONS_GHC -fno-warn-unused-binds #-}++-- Module : Main+-- Copyright : (c) 2013 Brendan Hay <brendan.g.hay@gmail.com>+-- License : This Source Code Form is subject to the terms of+-- the Mozilla Public License, v. 2.0.+-- A copy of the MPL can be found in the LICENSE file or+-- you can obtain it at http://mozilla.org/MPL/2.0/.+-- Maintainer : Brendan Hay <brendan.g.hay@gmail.com>+-- Stability : experimental+-- Portability : non-portable (GHC extensions)++module Main (main) where++import Control.Applicative+import Data.ByteString (ByteString)+import qualified Data.ByteString.Char8 as BS+import Data.List (stripPrefix)+import Data.Maybe+import Data.String+import GHC.Generics+import Test.Framework+import Test.Framework.Providers.QuickCheck2+import Test.QuickCheck+import Text.XML.Expat.Pickle.Generic++main :: IO ()+main = defaultMain+ [ testGroup "Isomorphisms"+ [ testProperty "Flat" (xml :: Iso Flat)+ , testProperty "Nested" (xml :: Iso Nested)+ , testProperty "Maybe" (xml :: Iso MaybeRec)+ , testProperty "Complex" (xml :: Iso Complex)+ , testProperty "List" (xml :: Iso ListRec)+ , testProperty "Nullary" (xml :: Iso Nullary)+ ]+ , testGroup "Generic Option Modifiers"+ [ testProperty "Constructors" (xml :: Iso Ctors)+ , testProperty "Fields" (xml :: Iso Fields)+ ]+ ]++data Flat = Flat+ { fooInt :: Int+ , fooByteString :: ByteString+ } deriving (Eq, Show, Generic)++instance IsXML Flat++instance Arbitrary Flat where+ arbitrary = Flat <$> arbitrary <*> arbitrary++data Nested = Nested+ { barInt :: Int+ , barInteger :: Integer+ , barFlat :: Flat+ } deriving (Eq, Show, Generic)++instance IsXML Nested++instance Arbitrary Nested where+ arbitrary = Nested <$> arbitrary <*> arbitrary <*> arbitrary++data MaybeRec = MaybeRec+ { bazFlat :: Maybe Flat+ , bazInt :: Int+ } deriving (Eq, Show, Generic)++instance IsXML MaybeRec++instance Arbitrary MaybeRec where+ arbitrary = MaybeRec <$> arbitrary <*> arbitrary++data Complex = Complex+ { waldoMaybeRec :: MaybeRec+ , waldoMaybe :: Maybe Flat+ } deriving (Eq, Show, Generic)++instance IsXML Complex++instance Arbitrary Complex where+ arbitrary = Complex+ <$> arbitrary+ <*> arbitrary++data ListRec = ListRec+ { wibList :: [Flat]+ } deriving (Eq, Show, Generic)++instance IsXML ListRec++instance Arbitrary ListRec where+ arbitrary = ListRec <$> arbitrary++data Nullary = PrefixXyzzy | PrefixThud+ deriving (Eq, Read, Show, Generic)++instance IsXML Nullary where+ xmlPickler = (xpContent xpPrim)+ { root = Just "Nullary"+ }++instance Arbitrary Nullary where+ arbitrary = elements [PrefixXyzzy, PrefixThud]++data Fields = Fields+ { thisPrefixInt :: Int+ , thisPrefixFlat :: Flat+ } deriving (Eq, Show, Generic)++instance IsXML Fields where+ xmlPickler = genericXMLPickler $ defaultXMLOptions+ { xmlFieldModifier = reverse+ }++instance Arbitrary Fields where+ arbitrary = Fields <$> arbitrary <*> arbitrary++data Ctors = PrefixCtors+ { ctorInt :: Int+ , ctorFlat :: Flat+ } deriving (Eq, Show, Generic)++instance IsXML Ctors where+ xmlPickler = genericXMLPickler $ defaultXMLOptions+ { xmlCtorModifier = \s -> fromMaybe s $ stripPrefix "Prefix" s+ }++instance Arbitrary Ctors where+ arbitrary = PrefixCtors <$> arbitrary <*> arbitrary++instance Arbitrary ByteString where+ arbitrary = fmap fromString . listOf1 $ oneof+ [ choose ('\48', '\57')+ , choose ('\65', '\90')+ , choose ('\97', '\122')+ ]++type Iso a = Isomorphism a -> Bool++data Isomorphism a = Iso+ { domain :: a+ , codomain :: ByteString+ , identity :: Either String a+ }++instance Show a => Show (Isomorphism a) where+ show Iso{..} = unlines+ [ "[Input]"+ , show domain+ , ""+ , "[Encoded]"+ , BS.unpack codomain+ , ""+ , "[Parsed]"+ , show identity+ ]++instance (Eq a, Arbitrary a, Show a, IsXML a) => Arbitrary (Isomorphism a) where+ arbitrary = do+ inp <- arbitrary+ let enc = toIndentedXML 2 inp+ return . Iso inp enc $ fromXML enc++xml :: (Eq a, Arbitrary a, IsXML a) => Isomorphism a -> Bool+xml (Iso d _ i) = either (const False) (== d) i