on master help sign in
16 files changed+742−357
since splitsnapshots
unifiedsplit
.travis.yml+27 CHANGELOG.md+22 README.md+9 ghc-802.yaml+8 ghc-822.yaml+1 ghc-844.yaml+3 release+35 src/Data/THGen/Compat.hs+61 src/Data/THGen/Enum.hs+13−6 src/Data/THGen/XML.hs+20−326 src/Data/THGen/XML/Internal.hs+416 src/Text/XML/DOM/Parser/Internal/Content.hs+28 stack.yaml+3−8 test/Main.hs+26−1 test/TestDefs.hs+25−5 xml-isogen.cabal+45−11
.travis.ymlnew file+27
@@ -0,0 +1,27 @@
1+sudo: false
2+
3+language: generic
4+
5+env:
6+ - STACK_YAML=stack.yaml
7+ - STACK_YAML=ghc-802.yaml
8+ - STACK_YAML=ghc-822.yaml
9+ - STACK_YAML=ghc-844.yaml
10+
11+cache:
12+ directories:
13+ - $HOME/.stack
14+
15+addons:
16+ apt:
17+ packages:
18+ - libgmp-dev
19+
20+before_install:
21+# Download and unpack the stack executable
22+- mkdir -p ~/.local/bin
23+- export PATH=$HOME/.local/bin:$PATH
24+- travis_retry curl -L https://www.stackage.org/stack/linux-x86_64 | tar xz --wildcards --strip-components=1 -C ~/.local/bin '*/stack'
25+
26+script:
27+- stack --no-terminal --install-ghc test --haddock --no-haddock-deps
CHANGELOG.md+22
@@ -1,3 +1,25 @@
1+HEAD
2+------
3+
4+0.3.0
5+------
6+
7+* Add namespaces support
8+* Builds with GHC 8.4
9+* Derive NFData for all generated datatypes
10+
11+0.2.1
12+------
13+
14+* Workaround for the GHC 8.2.1 attribute generation bug
15+
16+* Add tests checking run-time behaviour of generated code
17+
18+0.2.0
19+-------
20+
21+* Builds with GHC 7.10.3, 8.0.2 and 8.2.1
22+
1230.1.0
224-------
325
README.mdnew file+9
@@ -0,0 +1,9 @@
1+# xml-isogen
2+
3+[![Build Status](https://travis-ci.org/typeable/xml-isogen.svg?branch=master)](https://travis-ci.org/typeable/xml-isogen)
4+
5+TemplateHaskell generators for XML-isomorphic data types, including
6+instances for parsing and rendering. A convenient DSL to define those
7+types.
8+
9+This is similar to XSD but is Haskell-specific.
ghc-802.yamlnew file+8
@@ -0,0 +1,8 @@
1+resolver: lts-9.21
2+
3+packages:
4+- .
5+
6+extra-deps:
7+- dom-parser-3.0.0
8+- generic-arbitrary-0.1.0
ghc-822.yamlnew file+1
@@ -0,0 +1 @@
1+resolver: lts-11.22
ghc-844.yamlnew file+3
@@ -0,0 +1,3 @@
1+resolver: lts-12.16
2+extra-deps:
3+- dom-parser-3.1.0
releasenew file+35
@@ -0,0 +1,35 @@
1+#!/bin/bash
2+
3+set -o xtrace
4+
5+dist=`stack path --dist-dir --stack-yaml ./stack.yaml 2> /dev/null`
6+
7+echo -e "\033[1;36mTesting library...\033[0m"
8+stack test 2> /dev/null
9+
10+echo -e "\033[1;36mRunning sdist...\033[0m"
11+cabal sdist
12+
13+echo -e "\033[1;36mGenerating documentation...\033[0m"
14+stack haddock 2> /dev/null
15+
16+if [ "$?" -eq "0" ]; then
17+ echo -e "\033[1;32mUploading package to Hackage...\033[0m"
18+ read -p "Hackage username: " username
19+ read -p "Hackage password: " -s password
20+ echo ""
21+ cabal upload -u $username -p $password dist/$1-$2.tar.gz
22+
23+ docdir=$dist/doc/html
24+ cd $docdir
25+ doc=$1-$2-docs
26+
27+ echo -e "Compressing documentation from \033[1;34m$docdir\033[0m for \033[1;35m$1\033[0m-\033[1;33m$2\033[1;30m"
28+ cp -r $1 $doc
29+ tar -c -v -z --format=ustar -f $doc.tar.gz $doc
30+ echo -e "\033[1;32mUploading documentation to Hackage...\033[0m"
31+ curl -X PUT -H 'Content-Type: application/x-tar' -H 'Content-Encoding: gzip' --data-binary "@$doc.tar.gz" "https://$username:$password@hackage.haskell.org/package/$1-$2/docs"
32+ exit $?
33+else
34+ echo -e "\033[1;31mNot in a stack-powered project\033[0m"
35+fi
src/Data/THGen/Compat.hsnew file+61
@@ -0,0 +1,61 @@
1+{-# LANGUAGE CPP #-}
2+
3+module Data.THGen.Compat
4+ ( dataD
5+ , newtypeD
6+ , nonStrictType
7+ , strictType
8+ , varStrictType
9+ ) where
10+
11+import qualified Language.Haskell.TH as TH
12+
13+dataD :: TH.Name -> [TH.ConQ] -> [TH.Name] -> TH.DecQ
14+#if MIN_VERSION_template_haskell(2,12,0)
15+dataD name cons derivs = TH.dataD (return []) name [] Nothing cons [derivCls]
16+ where
17+ derivCls = TH.derivClause Nothing $ fmap TH.conT derivs
18+#elif MIN_VERSION_template_haskell(2,11,0)
19+dataD name cons derivs = TH.dataD (return []) name [] Nothing cons derivCls
20+ where
21+ derivCls = traverse TH.conT derivs
22+#else
23+dataD name = TH.dataD (return []) name []
24+#endif
25+
26+newtypeD :: TH.Name -> TH.ConQ -> [TH.Name] -> TH.DecQ
27+#if MIN_VERSION_template_haskell(2,12,0)
28+newtypeD name cons derivs = TH.newtypeD (return []) name [] Nothing cons [derivCls]
29+ where
30+ derivCls = TH.derivClause Nothing $ fmap TH.conT derivs
31+#elif MIN_VERSION_template_haskell(2,11,0)
32+newtypeD name cons derivs = TH.newtypeD (return []) name [] Nothing cons derivCls
33+ where
34+ derivCls = traverse TH.conT derivs
35+#else
36+newtypeD name = TH.newtypeD (return []) name []
37+#endif
38+
39+#if MIN_VERSION_template_haskell(2,11,0)
40+nonStrictType :: TH.TypeQ -> TH.BangTypeQ
41+nonStrictType = TH.bangType (TH.bang TH.noSourceUnpackedness TH.noSourceStrictness)
42+#else
43+nonStrictType :: TH.TypeQ -> TH.StrictTypeQ
44+nonStrictType = TH.strictType TH.notStrict
45+#endif
46+
47+#if MIN_VERSION_template_haskell(2,11,0)
48+strictType :: TH.TypeQ -> TH.BangTypeQ
49+strictType = TH.bangType (TH.bang TH.noSourceUnpackedness TH.sourceStrict)
50+#else
51+strictType :: TH.TypeQ -> TH.StrictTypeQ
52+strictType = TH.strictType TH.isStrict
53+#endif
54+
55+#if MIN_VERSION_template_haskell(2,11,0)
56+varStrictType :: TH.Name -> TH.BangTypeQ -> TH.VarBangTypeQ
57+varStrictType = TH.varBangType
58+#else
59+varStrictType :: TH.Name -> TH.StrictTypeQ -> TH.VarStrictTypeQ
60+varStrictType = TH.varStrictType
61+#endif
src/Data/THGen/Enum.hs+13−6
@@ -25,13 +25,17 @@ module Data.THGen.Enum
2525 ) where
2626
2727import Control.Applicative
28+import Control.DeepSeq
2829import Control.Lens (over, _head, (<&>))
2930import Control.Monad
3031import qualified Data.Char as C
32+import Data.THGen.Compat
33+import GHC.Generics (Generic)
3134import qualified Language.Haskell.TH as TH
3235import qualified Test.QuickCheck as QC
3336import qualified Text.Read as R
3437
38+
3539data Exhaustiveness = Exhaustive | NonExhaustive
3640 deriving (Eq, Ord, Show)
3741
@@ -74,13 +78,11 @@ enumGenerate (EnumDesc exh strName strVals) = do
7478 unknownConstr = case exh of
7579 Exhaustive -> []
7680 NonExhaustive ->
77− [TH.normalC unknownVal [TH.strictType TH.isStrict [t|String|]]]
78− TH.dataD
79− (return [])
81+ [TH.normalC unknownVal [strictType [t|String|]]]
82+ dataD
8083 name
81− []
8284 (constrs ++ unknownConstr)
83− ([''Eq, ''Ord] ++ if (exh == Exhaustive) then [''Enum, ''Bounded] else [])
85+ ([''Eq, ''Ord, ''Generic] ++ if (exh == Exhaustive) then [''Enum, ''Bounded] else [])
8486 showInstDecl <- do
8587 unknownMatch <- case exh of
8688 Exhaustive -> return []
@@ -127,4 +129,9 @@ enumGenerate (EnumDesc exh strName strVals) = do
127129 (return [])
128130 [t|QC.Arbitrary $(TH.conT name)|]
129131 [funSimple 'QC.arbitrary arbExpr]
130− return [dataDecl, readInstDecl, showInstDecl, arbInstance]
132+ nfDataInst <- do
133+ TH.instanceD
134+ (return [])
135+ [t|NFData $(TH.conT name)|]
136+ []
137+ return [dataDecl, readInstDecl, showInstDecl, arbInstance, nfDataInst]
src/Data/THGen/XML.hs+20−326
@@ -1,5 +1,4 @@
1−{-# LANGUAGE UndecidableInstances #-}
2−{-# LANGUAGE FunctionalDependencies #-}
1+{-# LANGUAGE NoImplicitPrelude #-}
32
43{- |
54
@@ -23,7 +22,7 @@ wrapper around the field type:
2322
2423Example 1.
2524
26−> "Color" =:= enum
25+> "Color" =:= enum Both
2726> & "R"
2827> & "G"
2928> & "B"
@@ -41,7 +40,7 @@ to be either @R@, @G@ or @B@.
4140
4241Example 2.
4342
44−> "Message" =:= record
43+> "Message" =:= record Both
4544> ! "author"
4645> + "recipient"
4746> ? "message" [t|Text|]
@@ -68,10 +67,10 @@ enumeration is exhaustive (contains only the listed constructors) or
6867non-exhaustive (also contains a constructor for unknown values; this
6968is the default):
7069
71−> "Enum1" Exhaustive =:= enum
70+> "Enum1" Exhaustive =:= enum Generator
7271> ...
7372
74−> "Enum2" NonExhaustive =:= enum
73+> "Enum2" NonExhaustive =:= enum Generator
7574> ...
7675
7776To define a record, use the `record` function followed by the name of
@@ -79,7 +78,7 @@ the data type to be generated. The prefix for the record fields is
7978inferred automatically by taking all of the uppercase letters in the
8079name. You can override it manually like so:
8180
82−> "Reference" "ref" =:= record
81+> "Reference" "ref" =:= record Parser
8382> ...
8483
8584To describe a record field you must supply its name as it appears
@@ -94,10 +93,14 @@ The type of the field is inferred automatically from its name, so
9493if the field is called @"author"@ its type will be @Author@. You can
9594override the type by specifying it in quasiquotes like so:
9695
97−> "Message" =:= record
98−> ! "author" [t|Person|]
96+> "Message" =:= record Both
97+> ! "author" [t|Person|].
9998> ...
10099
100+Both @record@ and @enum@ take a @GenType@ argument which tells it to generate
101+generator, parser or both. It's being used to optimize compilation time by
102+skipping the unnecessary TH instance generation.
103+
101104-}
102105
103106
@@ -105,6 +108,7 @@ module Data.THGen.XML
105108 ( Exhaustiveness(..)
106109 , PrefixName(..)
107110 , ExhaustivenessName(..)
111+ , GenType(..)
108112 , record
109113 , enum
110114 , (!)
@@ -114,325 +118,15 @@ module Data.THGen.XML
114118 , (!%)
115119 , (?%)
116120 , (&)
121+ , (^)
117122 , (=:=)
118123 -- Re-exports
119− , T.Text
120− , Int
121− , Integer
124+ , Text
125+ , P.Int
126+ , P.Integer
122127 ) where
123128
124−import Control.Lens hiding (repeated, enum, (&))
125−import Control.Lens.Internal.FieldTH (makeFieldOpticsForDec)
126−import qualified Data.Char as C
127−import Data.List.NonEmpty (NonEmpty)
128−import Data.Maybe (maybeToList, mapMaybe)
129−import Data.String
130129import Data.THGen.Enum
131−import qualified Data.Text as T
132−import qualified Language.Haskell.TH as TH
133−import Prelude hiding ((+), (*))
134−import Text.XML.DOM.Parser
135−import Text.XML.ParentAttributes
136−import qualified Text.XML.Writer as XW
137−import qualified Text.XML as X
138−
139−data XmlFieldPlural
140− = XmlFieldPluralMandatory -- Occurs exactly 1 time (Identity)
141− | XmlFieldPluralOptional -- Occurs 0 or 1 times (Maybe)
142− | XmlFieldPluralRepeated -- Occurs 0 or more times (List)
143− | XmlFieldPluralMultiplied -- Occurs 1 or more times (NonEmpty)
144−
145−data XmlAttributePlural
146− = XmlAttributePluralMandatory -- Occurs exactly 1 time (Identity)
147− | XmlAttributePluralOptional -- Occurs 0 or 1 times (Maybe)
148−
149−data PrefixName = PrefixName String String
150−
151−data IsoXmlDescPreField = IsoXmlDescPreField String TH.TypeQ
152−
153−data IsoXmlDescPreAttribute = IsoXmlDescPreAttribute String TH.TypeQ
154−
155−data IsoXmlDescField = IsoXmlDescField XmlFieldPlural String TH.TypeQ
156−
157−data IsoXmlDescAttribute = IsoXmlDescAttribute XmlAttributePlural String TH.TypeQ
158−
159−data IsoXmlDescRecordPart
160− = IsoXmlDescRecordField IsoXmlDescField
161− | IsoXmlDescRecordAttribute IsoXmlDescAttribute
162−
163−newtype IsoXmlDescRecord = IsoXmlDescRecord [IsoXmlDescRecordPart]
164−
165−makePrisms ''IsoXmlDescRecord
166−
167−data ExhaustivenessName = ExhaustivenessName String Exhaustiveness
168−
169−newtype IsoXmlDescEnumCon
170− = IsoXmlDescEnumCon { unIsoXmlDescEnumCon :: String }
171−
172−instance IsString IsoXmlDescEnumCon where
173− fromString = IsoXmlDescEnumCon
174−
175−data IsoXmlDescEnum = IsoXmlDescEnum [IsoXmlDescEnumCon]
176−
177−makePrisms ''IsoXmlDescEnum
178−
179−appendField
180− :: XmlFieldPlural
181− -> IsoXmlDescRecord
182− -> IsoXmlDescPreField
183− -> IsoXmlDescRecord
184−appendField plural xrec (IsoXmlDescPreField name ty) =
185− let xfield = IsoXmlDescRecordField $ IsoXmlDescField plural name ty
186− in over _IsoXmlDescRecord (xfield:) xrec
187−
188−appendAttribute
189− :: XmlAttributePlural
190− -> IsoXmlDescRecord
191− -> IsoXmlDescPreAttribute
192− -> IsoXmlDescRecord
193−appendAttribute plural xrec (IsoXmlDescPreAttribute name ty) =
194− let xattribute = IsoXmlDescRecordAttribute $ IsoXmlDescAttribute plural name ty
195− in over _IsoXmlDescRecord (xattribute:) xrec
196−
197−(!), (?), (*), (+) :: IsoXmlDescRecord -> IsoXmlDescPreField -> IsoXmlDescRecord
198−(!) = appendField XmlFieldPluralMandatory
199−(?) = appendField XmlFieldPluralOptional
200−(*) = appendField XmlFieldPluralRepeated
201−(+) = appendField XmlFieldPluralMultiplied
202−
203−(!%), (?%) :: IsoXmlDescRecord -> IsoXmlDescPreAttribute -> IsoXmlDescRecord
204−(!%) = appendAttribute XmlAttributePluralMandatory
205−(?%) = appendAttribute XmlAttributePluralOptional
206−
207−infixl 2 !
208−infixl 2 ?
209−infixl 2 *
210−infixl 2 +
211−infixl 2 !%
212−infixl 2 ?%
213−
214−appendEnumCon :: IsoXmlDescEnum -> IsoXmlDescEnumCon -> IsoXmlDescEnum
215−appendEnumCon xenum xenumcon =
216− over _IsoXmlDescEnum (xenumcon:) xenum
217−
218−(&) :: IsoXmlDescEnum -> IsoXmlDescEnumCon -> IsoXmlDescEnum
219−(&) = appendEnumCon
220−
221−infixl 2 &
222−
223−class Description name desc | desc -> name where
224− (=:=) :: name -> desc -> TH.DecsQ
225−
226−infix 0 =:=
227−
228−instance Description PrefixName IsoXmlDescRecord where
229− prefixName =:= descRecord =
230− let descRecordParts = descRecord ^. _IsoXmlDescRecord
231− in isoXmlGenerateRecord prefixName (reverse descRecordParts)
232−
233−record :: IsoXmlDescRecord
234−record = IsoXmlDescRecord []
235−
236−enum :: IsoXmlDescEnum
237−enum = IsoXmlDescEnum []
238−
239−instance Description ExhaustivenessName IsoXmlDescEnum where
240− exhaustivenessName =:= descEnum =
241− let descEnumCons = descEnum ^. _IsoXmlDescEnum
242− in isoXmlGenerateEnum exhaustivenessName (reverse descEnumCons)
243−
244−instance IsString (TH.TypeQ -> IsoXmlDescPreField) where
245− fromString = IsoXmlDescPreField
246−
247−instance IsString IsoXmlDescPreField where
248− fromString name = IsoXmlDescPreField name ty
249− where
250− ty = (TH.conT . TH.mkName) ("Xml" ++ over _head C.toUpper name)
251−
252−instance IsString (TH.TypeQ -> IsoXmlDescPreAttribute) where
253− fromString = IsoXmlDescPreAttribute
254−
255−instance IsString IsoXmlDescPreAttribute where
256− fromString name = IsoXmlDescPreAttribute name ty
257− where
258− ty = (TH.conT . TH.mkName) ("Xml" ++ over _head C.toUpper name)
259−
260−instance s ~ String => IsString (s -> PrefixName) where
261− fromString = PrefixName
262−
263−instance IsString PrefixName where
264− fromString strName = PrefixName strName (makeNamePrefix strName)
265−
266−instance e ~ Exhaustiveness => IsString (e -> ExhaustivenessName) where
267− fromString = ExhaustivenessName
268−
269−instance IsString ExhaustivenessName where
270− fromString strName = ExhaustivenessName strName NonExhaustive
271−
272−makeNamePrefix :: String -> String
273−makeNamePrefix = map C.toLower . filter C.isUpper
274−
275−funSimple :: TH.Name -> TH.ExpQ -> TH.DecQ
276−funSimple name body = TH.funD name [ TH.clause [] (TH.normalB body) [] ]
277−
278−isoXmlGenerateEnum
279− :: ExhaustivenessName -> [IsoXmlDescEnumCon] -> TH.DecsQ
280−isoXmlGenerateEnum (ExhaustivenessName strName' exh) enumCons = do
281− let
282− strName = "Xml" ++ strName'
283− strVals = map unIsoXmlDescEnumCon enumCons
284− enumDesc = EnumDesc exh strName strVals
285− name = TH.mkName strName
286− enumDecls <- enumGenerate enumDesc
287− toXmlInst <- do
288− TH.instanceD
289− (return [])
290− [t|XW.ToXML $(TH.conT name)|]
291− [funSimple 'XW.toXML [e|XW.toXML . T.pack . show|]]
292− toXmlAttributeInst <- do
293− TH.instanceD
294− (return [])
295− [t|ToXmlAttribute $(TH.conT name)|]
296− [funSimple 'toXmlAttribute [e|T.pack . show|]]
297− fromDomInst <- do
298− TH.instanceD
299− (return [])
300− [t|FromDom $(TH.conT name)|]
301− [funSimple 'fromDom [e|parseContent readContent|]]
302− fromAttributeInst <- do
303− TH.instanceD
304− (return [])
305− [t|FromAttribute $(TH.conT name)|]
306− [funSimple 'fromAttribute [e|readContent|]]
307− return $ enumDecls ++ [toXmlInst, toXmlAttributeInst,
308− fromDomInst, fromAttributeInst]
309−
310−isoXmlGenerateRecord :: PrefixName -> [IsoXmlDescRecordPart] -> TH.DecsQ
311−isoXmlGenerateRecord (PrefixName strName' strPrefix') descRecordParts = do
312− let
313− strName = "Xml" ++ strName'
314− strPrefix = "x" ++ strPrefix'
315− name = TH.mkName strName
316− fieldName str = "_" ++ strPrefix ++ over _head C.toUpper str
317− dataDecl <- do
318− let
319− fields = do
320− descRecordPart <- descRecordParts
321− return $ case descRecordPart of
322− IsoXmlDescRecordField descField ->
323− let
324− IsoXmlDescField fieldPlural fieldStrName fieldType = descField
325− fName = TH.mkName (fieldName fieldStrName)
326− fType = case fieldPlural of
327− XmlFieldPluralMandatory -> fieldType
328− XmlFieldPluralOptional -> [t| Maybe $fieldType |]
329− XmlFieldPluralRepeated -> [t| [$fieldType] |]
330− XmlFieldPluralMultiplied -> [t| NonEmpty $fieldType |]
331− in
332− TH.varStrictType fName (TH.strictType TH.isStrict fType)
333− IsoXmlDescRecordAttribute descAttribute ->
334− let
335− IsoXmlDescAttribute
336− attributePlural attributeStrName attributeType = descAttribute
337− fName = TH.mkName (fieldName attributeStrName)
338− fType = case attributePlural of
339− XmlAttributePluralMandatory -> attributeType
340− XmlAttributePluralOptional -> [t| Maybe $attributeType |]
341− in
342− TH.varStrictType fName (TH.strictType TH.isStrict fType)
343− TH.dataD
344− (return [])
345− name
346− []
347− [TH.recC name fields]
348− [''Eq, ''Show]
349− lensDecls <- makeFieldOpticsForDec lensRules dataDecl
350− fromDomInst <- do
351− let
352− exprHeader = [e|pure $(TH.conE name)|]
353− exprRecordParts = do
354− descRecordPart <- descRecordParts
355− return $ case descRecordPart of
356− IsoXmlDescRecordField descField ->
357− let
358− IsoXmlDescField fieldPlural fieldStrName _ = descField
359− exprFieldStrName = TH.litE (TH.stringL fieldStrName)
360− fieldParse = case fieldPlural of
361− XmlFieldPluralMandatory -> [e|inElem|]
362− _ -> [e|inElemTrav|]
363− in
364− [e|$fieldParse $exprFieldStrName fromDom|]
365− IsoXmlDescRecordAttribute descAttribute ->
366− let
367− IsoXmlDescAttribute attributePlural attributeStrName _ = descAttribute
368− exprAttributeStrName = TH.litE (TH.stringL attributeStrName)
369− attributeParse = case attributePlural of
370− XmlAttributePluralMandatory -> [e|parseAttribute|]
371− XmlAttributePluralOptional -> [e|parseAttributeMaybe|]
372− in
373− [e|$attributeParse $exprAttributeStrName fromAttribute|]
374− fromDomExpr = foldl (\e fe -> [e| $e <*> $fe |]) exprHeader exprRecordParts
375− TH.instanceD
376− (return [])
377− [t|FromDom $(TH.conT name)|]
378− [ funSimple 'fromDom fromDomExpr ]
379−
380− toXmlInst <- do
381− objName <- TH.newName strPrefix
382− let
383− exprFields = do
384− descRecordPart <- descRecordParts
385− IsoXmlDescField fieldPlural fieldStrName _ <-
386− maybeToList $ case descRecordPart of
387− IsoXmlDescRecordField descField -> Just descField
388− _ -> Nothing
389− let
390− fName = TH.mkName (fieldName fieldStrName)
391− exprFieldStrName = TH.litE (TH.stringL fieldStrName)
392− exprForField = case fieldPlural of
393− XmlFieldPluralMandatory -> [e|id|]
394− _ -> [e|traverse|]
395− exprFieldValue = [e|$(TH.varE fName) $(TH.varE objName)|]
396− exprFieldRender = [e|mkElement $exprFieldStrName|]
397− return [e|$exprForField $exprFieldRender $exprFieldValue|]
398− toXmlExpr
399− = TH.lamE [if null exprFields then TH.wildP else TH.varP objName]
400− $ foldr (\fe e -> [e|$fe *> $e|]) [e|return ()|] exprFields
401− TH.instanceD
402− (return [])
403− [t|XW.ToXML $(TH.conT name)|]
404− [funSimple 'XW.toXML toXmlExpr]
405−
406− toXmlParentAttributesInst <- do
407− objName <- TH.newName strPrefix
408− let
409− exprAttributes = do
410− descRecordPart <- descRecordParts
411− IsoXmlDescAttribute attributePlural attributeStrName _ <-
412− maybeToList $ case descRecordPart of
413− IsoXmlDescRecordAttribute descAttribute -> Just descAttribute
414− _ -> Nothing
415− let
416− fName = TH.mkName (fieldName attributeStrName)
417− exprAttrStrName = TH.litE (TH.stringL attributeStrName)
418− exprAttrValue = [e|$(TH.varE fName) $(TH.varE objName)|]
419− exprAttrWrap = case attributePlural of
420− XmlAttributePluralMandatory -> [e|Just . toXmlAttribute|]
421− XmlAttributePluralOptional -> [e|fmap toXmlAttribute|]
422− return [e|($exprAttrStrName, $exprAttrWrap $exprAttrValue)|]
423− toXmlParentAttributesExpr
424− = TH.lamE [if null exprAttributes then TH.wildP else TH.varP objName]
425− $ [e|mapMaybe distribPair $(TH.listE exprAttributes)|]
426− TH.instanceD
427− (return [])
428− [t|ToXmlParentAttributes $(TH.conT name)|]
429− [funSimple 'toXmlParentAttributes toXmlParentAttributesExpr]
430−
431− return $ [dataDecl] ++ lensDecls ++
432− [fromDomInst, toXmlInst, toXmlParentAttributesInst]
433−
434−mkElement :: (XW.ToXML a, ToXmlParentAttributes a) => X.Name -> a -> XW.XML
435−mkElement name a = XW.elementA name (toXmlParentAttributes a) (XW.toXML a)
436−
437−distribPair :: Functor f => (a, f b) -> f (a, b)
438−distribPair (a, fb) = (a,) <$> fb
130+import Data.THGen.XML.Internal
131+import Data.Text
132+import qualified Prelude as P
src/Data/THGen/XML/Internal.hsnew file+416
@@ -0,0 +1,416 @@
1+{-# LANGUAGE CPP #-}
2+{-# LANGUAGE UndecidableInstances #-}
3+{-# LANGUAGE FunctionalDependencies #-}
4+
5+module Data.THGen.XML.Internal where
6+
7+import Control.DeepSeq
8+import Control.Lens hiding (repeated, enum, (&))
9+import Control.Lens.Internal.FieldTH (makeFieldOpticsForDec)
10+import qualified Data.Char as C
11+import Data.List.NonEmpty (NonEmpty)
12+import Data.Maybe (maybeToList, mapMaybe)
13+import Data.String
14+import Data.THGen.Compat as THC
15+import Data.THGen.Enum
16+import qualified Data.Text as T
17+import GHC.Generics (Generic)
18+import Language.Haskell.TH as TH
19+import Prelude hiding ((+), (*), (^))
20+import qualified Text.XML as X
21+import Text.XML.DOM.Parser hiding (parseContent)
22+import Text.XML.DOM.Parser.Internal.Content
23+import Text.XML.ParentAttributes
24+import qualified Text.XML.Writer as XW
25+
26+
27+data GenType = Parser | Generator | Both
28+
29+data XmlFieldPlural
30+ = XmlFieldPluralMandatory -- Occurs exactly 1 time (Identity)
31+ | XmlFieldPluralOptional -- Occurs 0 or 1 times (Maybe)
32+ | XmlFieldPluralRepeated -- Occurs 0 or more times (List)
33+ | XmlFieldPluralMultiplied -- Occurs 1 or more times (NonEmpty)
34+
35+data XmlAttributePlural
36+ = XmlAttributePluralMandatory -- Occurs exactly 1 time (Identity)
37+ | XmlAttributePluralOptional -- Occurs 0 or 1 times (Maybe)
38+
39+data PrefixName = PrefixName String String
40+
41+data IsoXmlDescPreField = IsoXmlDescPreField String TH.TypeQ
42+
43+data IsoXmlDescPreAttribute = IsoXmlDescPreAttribute String TH.TypeQ
44+
45+data IsoXmlDescPreContent = IsoXmlDescPreContent String TH.TypeQ
46+
47+data IsoXmlDescField = IsoXmlDescField XmlFieldPlural String TH.TypeQ
48+
49+data IsoXmlDescAttribute = IsoXmlDescAttribute XmlAttributePlural String TH.TypeQ
50+
51+data IsoXmlDescContent = IsoXmlDescContent String TH.TypeQ
52+
53+data IsoXmlDescRecordPart
54+ = IsoXmlDescRecordField IsoXmlDescField
55+ | IsoXmlDescRecordAttribute IsoXmlDescAttribute
56+ | IsoXmlDescRecordContent IsoXmlDescContent
57+
58+data IsoXmlDescRecord = IsoXmlDescRecord GenType [IsoXmlDescRecordPart]
59+
60+makePrisms ''IsoXmlDescRecord
61+
62+data ExhaustivenessName = ExhaustivenessName String Exhaustiveness
63+
64+newtype IsoXmlDescEnumCon
65+ = IsoXmlDescEnumCon { unIsoXmlDescEnumCon :: String }
66+
67+instance IsString IsoXmlDescEnumCon where
68+ fromString = IsoXmlDescEnumCon
69+
70+data IsoXmlDescEnum = IsoXmlDescEnum GenType [IsoXmlDescEnumCon]
71+
72+makePrisms ''IsoXmlDescEnum
73+
74+appendField
75+ :: XmlFieldPlural
76+ -> IsoXmlDescRecord
77+ -> IsoXmlDescPreField
78+ -> IsoXmlDescRecord
79+appendField plural (IsoXmlDescRecord genType fields) (IsoXmlDescPreField name ty) =
80+ let xfield = IsoXmlDescRecordField $ IsoXmlDescField plural name ty
81+ in IsoXmlDescRecord genType (xfield:fields)
82+
83+appendAttribute
84+ :: XmlAttributePlural
85+ -> IsoXmlDescRecord
86+ -> IsoXmlDescPreAttribute
87+ -> IsoXmlDescRecord
88+appendAttribute plural (IsoXmlDescRecord genType fields) (IsoXmlDescPreAttribute name ty) =
89+ let xattribute = IsoXmlDescRecordAttribute $ IsoXmlDescAttribute plural name ty
90+ in IsoXmlDescRecord genType (xattribute:fields)
91+
92+appendContent
93+ :: IsoXmlDescRecord
94+ -> IsoXmlDescPreContent
95+ -> IsoXmlDescRecord
96+appendContent (IsoXmlDescRecord genType fields) (IsoXmlDescPreContent name ty) =
97+ let xcontent = IsoXmlDescRecordContent $ IsoXmlDescContent name ty
98+ in IsoXmlDescRecord genType (xcontent:fields)
99+
100+(!), (?), (*), (+) :: IsoXmlDescRecord -> IsoXmlDescPreField -> IsoXmlDescRecord
101+(!) = appendField XmlFieldPluralMandatory
102+(?) = appendField XmlFieldPluralOptional
103+(*) = appendField XmlFieldPluralRepeated
104+(+) = appendField XmlFieldPluralMultiplied
105+
106+(!%), (?%) :: IsoXmlDescRecord -> IsoXmlDescPreAttribute -> IsoXmlDescRecord
107+(!%) = appendAttribute XmlAttributePluralMandatory
108+(?%) = appendAttribute XmlAttributePluralOptional
109+
110+(^) :: IsoXmlDescRecord -> IsoXmlDescPreContent -> IsoXmlDescRecord
111+(^) = appendContent
112+
113+infixl 2 !
114+infixl 2 ?
115+infixl 2 *
116+infixl 2 +
117+infixl 2 !%
118+infixl 2 ?%
119+infixl 2 ^
120+
121+appendEnumCon :: IsoXmlDescEnum -> IsoXmlDescEnumCon -> IsoXmlDescEnum
122+appendEnumCon (IsoXmlDescEnum genType enumCons) xenumcon =
123+ IsoXmlDescEnum genType (xenumcon:enumCons)
124+
125+(&) :: IsoXmlDescEnum -> IsoXmlDescEnumCon -> IsoXmlDescEnum
126+(&) = appendEnumCon
127+
128+infixl 2 &
129+
130+class Description name desc | desc -> name where
131+ (=:=) :: name -> desc -> TH.DecsQ
132+
133+infix 0 =:=
134+
135+instance Description PrefixName IsoXmlDescRecord where
136+ prefixName =:= (IsoXmlDescRecord genType descRecordParts) =
137+ isoXmlGenerateDatatype genType prefixName (reverse descRecordParts)
138+
139+record :: GenType -> IsoXmlDescRecord
140+record gt = IsoXmlDescRecord gt []
141+
142+enum :: GenType -> IsoXmlDescEnum
143+enum gt = IsoXmlDescEnum gt []
144+
145+instance Description ExhaustivenessName IsoXmlDescEnum where
146+ exhaustivenessName =:= (IsoXmlDescEnum genType descEnumCons) =
147+ isoXmlGenerateEnum genType exhaustivenessName (reverse descEnumCons)
148+
149+instance IsString (TH.TypeQ -> IsoXmlDescPreField) where
150+ fromString = IsoXmlDescPreField
151+
152+instance IsString IsoXmlDescPreField where
153+ fromString name = IsoXmlDescPreField name ty
154+ where
155+ ty = (TH.conT . TH.mkName) ("Xml" ++ over _head C.toUpper (xmlLocalName name))
156+
157+instance IsString (TH.TypeQ -> IsoXmlDescPreAttribute) where
158+ fromString = IsoXmlDescPreAttribute
159+
160+instance IsString (TH.TypeQ -> IsoXmlDescPreContent) where
161+ fromString = IsoXmlDescPreContent
162+
163+instance IsString IsoXmlDescPreAttribute where
164+ fromString name = IsoXmlDescPreAttribute name ty
165+ where
166+ ty = (TH.conT . TH.mkName) ("Xml" ++ over _head C.toUpper name)
167+
168+instance IsString IsoXmlDescPreContent where
169+ fromString name = IsoXmlDescPreContent name ty
170+ where
171+ ty = (TH.conT . TH.mkName) ("Xml" ++ over _head C.toUpper name)
172+
173+instance s ~ String => IsString (s -> PrefixName) where
174+ fromString = PrefixName
175+
176+instance IsString PrefixName where
177+ fromString strName = PrefixName strName (makeNamePrefix strName)
178+
179+instance e ~ Exhaustiveness => IsString (e -> ExhaustivenessName) where
180+ fromString = ExhaustivenessName
181+
182+instance IsString ExhaustivenessName where
183+ fromString strName = ExhaustivenessName strName NonExhaustive
184+
185+makeNamePrefix :: String -> String
186+makeNamePrefix = map C.toLower . filter C.isUpper
187+
188+funSimple :: TH.Name -> TH.ExpQ -> TH.DecQ
189+funSimple name body = TH.funD name [ TH.clause [] (TH.normalB body) [] ]
190+
191+isoXmlGenerateEnum
192+ :: GenType
193+ -> ExhaustivenessName
194+ -> [IsoXmlDescEnumCon]
195+ -> TH.DecsQ
196+isoXmlGenerateEnum genType (ExhaustivenessName strName' exh) enumCons = do
197+ let
198+ strName = "Xml" ++ strName'
199+ strVals = map unIsoXmlDescEnumCon enumCons
200+ enumDesc = EnumDesc exh strName strVals
201+ name = TH.mkName strName
202+ enumDecls <- enumGenerate enumDesc
203+ let
204+ genToXmlInst = do
205+ TH.instanceD
206+ (return [])
207+ [t|XW.ToXML $(TH.conT name)|]
208+ [funSimple 'XW.toXML [e|XW.toXML . T.pack . show|]]
209+ genToXmlAttributeInst = do
210+ TH.instanceD
211+ (return [])
212+ [t|ToXmlAttribute $(TH.conT name)|]
213+ [funSimple 'toXmlAttribute [e|T.pack . show|]]
214+ genFromDomInst = do
215+ TH.instanceD
216+ (return [])
217+ [t|FromDom $(TH.conT name)|]
218+ [funSimple 'fromDom [e|parseContent readContent|]]
219+ genFromAttributeInst = do
220+ TH.instanceD
221+ (return [])
222+ [t|FromAttribute $(TH.conT name)|]
223+ [funSimple 'fromAttribute [e|readContent|]]
224+ case genType of
225+ Generator -> do
226+ toXmlInst <- genToXmlInst
227+ toXmlAttributeInst <- genToXmlAttributeInst
228+ return $ enumDecls ++ [toXmlInst, toXmlAttributeInst]
229+ Parser -> do
230+ fromDomInst <- genFromDomInst
231+ fromAttributeInst <- genFromAttributeInst
232+ return $ enumDecls ++ [fromDomInst, fromAttributeInst]
233+ Both -> do
234+ toXmlInst <- genToXmlInst
235+ toXmlAttributeInst <- genToXmlAttributeInst
236+ fromDomInst <- genFromDomInst
237+ fromAttributeInst <- genFromAttributeInst
238+ return $ enumDecls ++ [toXmlInst, toXmlAttributeInst,
239+ fromDomInst, fromAttributeInst]
240+
241+isoXmlGenerateDatatype :: GenType -> PrefixName -> [IsoXmlDescRecordPart] -> TH.DecsQ
242+isoXmlGenerateDatatype genType (PrefixName strName' strPrefix') descRecordParts = do
243+ let
244+ isNewtype = length descRecordParts == 1
245+ strName = "Xml" ++ strName'
246+ strPrefix = "x" ++ strPrefix'
247+ name = TH.mkName strName
248+ fieldName str = "_" ++ strPrefix ++ over _head C.toUpper str
249+ termDecl <- do
250+ let
251+ fields = do
252+ descRecordPart <- descRecordParts
253+ return $ case descRecordPart of
254+ IsoXmlDescRecordField descField ->
255+ let
256+ IsoXmlDescField fieldPlural rawName fieldType = descField
257+ fieldStrName = xmlLocalName rawName
258+ fName = TH.mkName (fieldName fieldStrName)
259+ fType = case fieldPlural of
260+ XmlFieldPluralMandatory -> fieldType
261+ XmlFieldPluralOptional -> [t| Maybe $fieldType |]
262+ XmlFieldPluralRepeated -> [t| [$fieldType] |]
263+ XmlFieldPluralMultiplied -> [t| NonEmpty $fieldType |]
264+ in if isNewtype
265+ then THC.varStrictType fName (THC.nonStrictType fType)
266+ else THC.varStrictType fName (THC.strictType fType)
267+ IsoXmlDescRecordAttribute descAttribute ->
268+ let
269+ IsoXmlDescAttribute
270+ attributePlural attributeStrName attributeType = descAttribute
271+ fName = TH.mkName (fieldName attributeStrName)
272+ fType = case attributePlural of
273+ XmlAttributePluralMandatory -> attributeType
274+ XmlAttributePluralOptional -> [t| Maybe $attributeType |]
275+ in if isNewtype
276+ then THC.varStrictType fName (THC.nonStrictType fType)
277+ else THC.varStrictType fName (THC.strictType fType)
278+ IsoXmlDescRecordContent descContent ->
279+ let
280+ IsoXmlDescContent contentStrName contentType = descContent
281+ fName = TH.mkName (fieldName contentStrName)
282+ fType = contentType
283+ in if isNewtype
284+ then THC.varStrictType fName (THC.nonStrictType fType)
285+ else THC.varStrictType fName (THC.strictType fType)
286+ if isNewtype
287+ -- generate a newtype instead to do less allocations later
288+ then THC.newtypeD name (TH.recC name fields) [''Eq, ''Show, ''Generic]
289+ else THC.dataD name [TH.recC name fields] [''Eq, ''Show, ''Generic]
290+ lensDecls <- makeFieldOpticsForDec lensRules termDecl
291+ nfDataInst <- do
292+ TH.instanceD
293+ (return [])
294+ [t|NFData $(TH.conT name)|]
295+ [ ]
296+
297+ let
298+ genFromDomInst = do
299+ let
300+ exprHeader = [e|pure $(TH.conE name)|]
301+ exprRecordParts = do
302+ descRecordPart <- descRecordParts
303+ return $ case descRecordPart of
304+ IsoXmlDescRecordField descField ->
305+ let
306+ IsoXmlDescField fieldPlural rawName _ = descField
307+ fieldStrName = xmlLocalName rawName
308+ exprFieldStrName = TH.litE (TH.stringL fieldStrName)
309+ fieldParse = case fieldPlural of
310+ XmlFieldPluralMandatory -> [e|inElem|]
311+ _ -> [e|inElemTrav|]
312+ in
313+ [e|$fieldParse $exprFieldStrName fromDom|]
314+ IsoXmlDescRecordAttribute descAttribute ->
315+ let
316+ IsoXmlDescAttribute attributePlural attributeStrName _ = descAttribute
317+ exprAttributeStrName = TH.litE (TH.stringL attributeStrName)
318+ attributeParse = case attributePlural of
319+ XmlAttributePluralMandatory -> [e|parseAttribute|]
320+ XmlAttributePluralOptional -> [e|parseAttributeMaybe|]
321+ in
322+ [e|$attributeParse $exprAttributeStrName fromAttribute|]
323+ IsoXmlDescRecordContent _ -> [e|parseContent fromAttribute|]
324+ fromDomExpr = foldl (\e fe -> [e| $e <*> $fe |]) exprHeader exprRecordParts
325+ TH.instanceD
326+ (return [])
327+ [t|FromDom $(TH.conT name)|]
328+ [ funSimple 'fromDom fromDomExpr ]
329+
330+ genToXmlInst = do
331+ objName <- TH.newName strPrefix
332+ let
333+ exprFields = do
334+ descRecordPart <- descRecordParts
335+ case descRecordPart of
336+ IsoXmlDescRecordField (IsoXmlDescField fieldPlural rawName _) -> do
337+ let
338+ fieldStrName = xmlLocalName rawName
339+ fName = TH.mkName (fieldName fieldStrName)
340+ exprFieldStrName = TH.litE (TH.stringL rawName)
341+ exprForField = case fieldPlural of
342+ XmlFieldPluralMandatory -> [e|id|]
343+ _ -> [e|traverse|]
344+ exprFieldValue = [e|$(TH.varE fName) $(TH.varE objName)|]
345+ exprFieldRender =
346+ [e|(\a ->
347+ XW.elementA $exprFieldStrName (toXmlParentAttributes a) a)|]
348+ return [e|$exprForField $exprFieldRender $exprFieldValue|]
349+ IsoXmlDescRecordContent (IsoXmlDescContent rawName _) -> do
350+ let
351+ fieldStrName = xmlLocalName rawName
352+ fName = TH.mkName (fieldName fieldStrName)
353+ exprFieldValue = [e|$(TH.varE fName) $(TH.varE objName)|]
354+ return [e|XW.content . toXmlAttribute $ $exprFieldValue|]
355+ _ -> []
356+ toXmlExpr
357+ = TH.lamE [if null exprFields then TH.wildP else TH.varP objName]
358+ $ foldr (\fe e -> [e|$fe *> $e|]) [e|return ()|] exprFields
359+ TH.instanceD
360+ (return [])
361+ [t|XW.ToXML $(TH.conT name)|]
362+ [funSimple 'XW.toXML toXmlExpr]
363+ genToXmlParentAttributeInst = do
364+ objName <- TH.newName strPrefix
365+ let
366+ exprAttributes = do
367+ descRecordPart <- descRecordParts
368+ IsoXmlDescAttribute attributePlural attributeStrName _ <-
369+ maybeToList $ case descRecordPart of
370+ IsoXmlDescRecordAttribute descAttribute -> Just descAttribute
371+ _ -> Nothing
372+ let
373+ fName = TH.mkName (fieldName attributeStrName)
374+ exprAttrStrName = TH.litE (TH.stringL attributeStrName)
375+ exprAttrValue = [e|$(TH.varE fName) $(TH.varE objName)|]
376+ exprAttrWrap = case attributePlural of
377+ XmlAttributePluralMandatory -> [e|Just . toXmlAttribute|]
378+ XmlAttributePluralOptional -> [e|fmap toXmlAttribute|]
379+ return [e|($exprAttrStrName, $exprAttrWrap $exprAttrValue)|]
380+ toXmlParentAttributesExpr
381+ = TH.lamE [if null exprAttributes then TH.wildP else TH.varP objName]
382+ $ [e|mapMaybe distribPair $(TH.listE exprAttributes)|]
383+#if __GLASGOW_HASKELL__ < 800
384+ TH.instanceD
385+#else
386+ TH.instanceWithOverlapD (Just TH.Overlapping)
387+#endif
388+ (return [])
389+ [t|ToXmlParentAttributes $(TH.conT name)|]
390+ [funSimple 'toXmlParentAttributes toXmlParentAttributesExpr]
391+
392+ case genType of
393+ Generator -> do
394+ toXmlInst <- genToXmlInst
395+ toXmlParentAttributesInst <- genToXmlParentAttributeInst
396+ return $ [termDecl] ++ lensDecls ++
397+ [toXmlInst, toXmlParentAttributesInst, nfDataInst]
398+ Parser -> do
399+ fromDomInst <- genFromDomInst
400+ return $ [termDecl] ++ lensDecls ++ [fromDomInst, nfDataInst]
401+ Both -> do
402+ toXmlInst <- genToXmlInst
403+ toXmlParentAttributesInst <- genToXmlParentAttributeInst
404+ fromDomInst <- genFromDomInst
405+ return $ [termDecl] ++ lensDecls ++
406+ [fromDomInst, toXmlInst, toXmlParentAttributesInst, nfDataInst]
407+
408+distribPair :: Functor f => (a, f b) -> f (a, b)
409+distribPair (a, fb) = (a,) <$> fb
410+
411+-- | Get a local part of (possibly) fully qualified 'X.Name':
412+--
413+-- >>> xmlLocalName "{http://example.com/ns/my-namespace}my-name"
414+-- "my-name"
415+xmlLocalName :: String -> String
416+xmlLocalName = T.unpack . X.nameLocalName . fromString
src/Text/XML/DOM/Parser/Internal/Content.hsnew file+28
@@ -0,0 +1,28 @@
1+{-# LANGUAGE OverloadedStrings #-}
2+module Text.XML.DOM.Parser.Internal.Content where
3+import Control.Lens
4+import Control.Monad.Reader
5+import Data.Text as T
6+import Text.XML
7+import Text.XML.DOM.Parser hiding (getCurrentContent, parseContent)
8+
9+-- Get current content. If there are more than one content
10+-- they are intercalate with space like in xpath `text()`
11+--
12+getCurrentContent :: (Monad m) => DomParserT Identity m Text
13+getCurrentContent
14+ = T.intercalate " "
15+ . (\e -> [T.strip cs| (NodeContent cs) <- e])
16+ . elementNodes
17+ . runIdentity
18+ . _pdElements
19+ <$> ask
20+
21+parseContent
22+ :: (Monad m)
23+ => (Text -> Either Text a)
24+ -- ^ Content parser, return error msg if value is not parsed
25+ -> DomParserT Identity m a
26+parseContent parse
27+ = getCurrentContent
28+ >>= either (throwParserError . PEContentWrongFormat) return . parse
stack.yaml+3−8
@@ -1,9 +1,4 @@
1−resolver: lts-6.27
2−
3−packages:
4−- "."
5−
1+resolver: lts-10.0
62extra-deps:
7−- dom-parser-2.0.0
8−- type-fun-0.0.1
9−- open-union-0.2.0.0
3+- dom-parser-3.0.0
4+- generic-arbitrary-0.1.0
test/Main.hs+26−1
@@ -1,6 +1,31 @@
1+{-# OPTIONS -fno-warn-unused-imports #-}
12module Main where
23
4+import Control.Exception
5+import Text.XML
6+import Data.Default
7+import System.IO.Unsafe
8+import Test.Hspec
9+import Test.Hspec.QuickCheck
10+import Test.QuickCheck
311import TestDefs
12+import Text.XML.DOM.Parser
13+import Text.XML.Writer
14+
15+isomorphicFoo :: XmlRoot -> Property
16+isomorphicFoo a = a === isogen
17+ where
18+ isogen :: XmlRoot
19+ isogen =
20+ let
21+ doc = document "Root" $ toXML a
22+ res = case runDomParser doc fromDom of
23+ Left e -> unsafePerformIO $ do
24+ pprint doc
25+ throwIO e
26+ Right a' -> a'
27+ in res
428
529main :: IO ()
6−main = return ()
30+main = hspec $ do
31+ prop "isomorphic" isomorphicFoo
test/TestDefs.hs+25−5
@@ -1,18 +1,19 @@
11{-# LANGUAGE NoImplicitPrelude #-}
2−{-# LANGUAGE OverloadedStrings #-}
3−{-# LANGUAGE TemplateHaskell #-}
42
53{-# OPTIONS -ddump-splices #-}
64
75module TestDefs where
86
7+-- import Data.List.NonEmpty
98import Data.THGen.XML
9+import Test.QuickCheck.Arbitrary.Generic
10+import Test.QuickCheck.Instances ()
1011
11−"Bar" =:= enum
12+"Bar" =:= enum Both
1213 & "baroque"
1314 & "bartender"
1415
15−"Quux" Exhaustive =:= enum
16+"Quux" Exhaustive =:= enum Both
1617 & "ALL"
1718 & "YOUR"
1819 & "BASE"
@@ -21,7 +22,26 @@ import Data.THGen.XML
2122 & "TO"
2223 & "US"
2324
24−"Foo" =:= record
25+"Foo" =:= record Both
26+ ^ "Shmuux" [t|XmlQuux|]
2527 + "Bar"
2628 ? "Baz" [t|Text|]
2729 !% "Quux"
30+ ?% "Muux" [t|XmlQuux|]
31+
32+"Root" =:= record Both
33+ ! "{http://example.com/ns/my-namespace}Foo"
34+
35+instance Arbitrary XmlRoot where
36+ arbitrary = genericArbitrary
37+ shrink = genericShrink
38+
39+instance Arbitrary XmlFoo where
40+ arbitrary = genericArbitrary
41+ shrink = genericShrink
42+
43+#if __GLASGOW_HASKELL__ < 800
44+instance Arbitrary (NonEmpty XmlBar) where
45+ arbitrary = genericArbitrary
46+ shrink = genericShrink
47+#endif
xml-isogen.cabal+45−11
@@ -1,5 +1,5 @@
11name: xml-isogen
2−version: 0.1.0
2+version: 0.3.1
33synopsis: Generate XML-isomorphic types
44description:
55 TemplateHaskell generators for XML-isomorphic data types, including
@@ -14,17 +14,30 @@ author: Typeable.io contributors
1414maintainer: makeit@typeable.io
1515category: Data
1616build-type: Simple
17−extra-source-files: CHANGELOG.md
1817cabal-version: >=1.22
18+homepage: https://github.com/typeable/xml-isogen
19+tested-with: GHC == 7.10.3
20+ , GHC == 8.0.2
21+ , GHC == 8.2.2
22+ , GHC == 8.4.1
23+extra-source-files: CHANGELOG.md
24+ , README.md
25+
26+source-repository head
27+ type: git
28+ location: git@github.com:typeable/xml-isogen.git
1929
2030library
2131 exposed-modules: Data.THGen.Enum
22− Data.THGen.XML
23− Text.XML.ParentAttributes
24−
32+ , Data.THGen.XML
33+ , Data.THGen.XML.Internal
34+ , Text.XML.ParentAttributes
35+ other-modules: Data.THGen.Compat
36+ , Text.XML.DOM.Parser.Internal.Content
2537 build-depends: QuickCheck >= 2.8
2638 , base >=4.8 && <5
2739 , dom-parser >= 2.0.0
40+ , deepseq
2841 , lens >= 4.13
2942 , mtl >= 2.2
3043 , semigroups >= 0.18
@@ -36,17 +49,38 @@ library
3649 hs-source-dirs: src
3750
3851 default-language: Haskell2010
39− default-extensions: TemplateHaskell
40− LambdaCase
41− FlexibleInstances
42− TypeFamilies
43− TupleSections
52+ default-extensions: CPP
53+ , FlexibleInstances
54+ , LambdaCase
55+ , TemplateHaskell
56+ , TupleSections
57+ , TypeFamilies
4458
4559test-suite spec
4660 type: exitcode-stdio-1.0
4761 hs-source-dirs: test
4862 main-is: Main.hs
4963 other-modules: TestDefs
50− build-depends: base, xml-isogen
64+ build-depends: base
65+ , QuickCheck
66+ , data-default
67+ , deepseq
68+ , dom-parser
69+ , generic-arbitrary
70+ , hspec
71+ , quickcheck-instances
72+ , semigroups
73+ , xml-conduit
74+ , xml-conduit-writer
75+ , xml-isogen
5176 default-language: Haskell2010
77+ default-extensions: CPP
78+ , DeriveGeneric
79+ , FlexibleInstances
80+ , LambdaCase
81+ , OverloadedStrings
82+ , StandaloneDeriving
83+ , TemplateHaskell
84+ , TupleSections
85+ , TypeFamilies
5286 ghc-options: -Wall -fno-warn-missing-signatures