@@ -5,7 +5,7 @@
55module Data.THGen.XML.Internal where
66
77import Control.DeepSeq
8−import Control.Lens hiding (repeated, enum, (&), Strict)
8+import Control.Lens hiding (repeated, enum, (&))
99import Control.Lens.Internal.FieldTH (makeFieldOpticsForDec)
1010import qualified Data.Char as C
1111import Data.List.NonEmpty (NonEmpty)
@@ -15,7 +15,7 @@ import Data.THGen.Compat as THC
1515import Data.THGen.Enum
1616import qualified Data.Text as T
1717import GHC.Generics (Generic)
18−import Language.Haskell.TH as TH hiding (Strict)
18+import Language.Haskell.TH as TH
1919import Prelude hiding ((+), (*), (^))
2020import qualified Text.XML as X
2121import Text.XML.DOM.Parser hiding (parseContent)
@@ -23,22 +23,27 @@ import Text.XML.DOM.Parser.Internal.Content
2323import Text.XML.ParentAttributes
2424import qualified Text.XML.Writer as XW
2525
26−data ParserMode
27− = Strict
28− | Lenient
2926
30−data GenType
31− = Parser
32− | Generator
33− | ParserAndGenerator
34− | LenientParser
35− | LenientParserAndGenerator
27+{- | Marks the type of code generation:
28+@Parser@ - generate only instances for xml parsing (@FromDom@)
29+@Generator@ - generate only @ToXML@ instances
30+@Both@ - generate @FromDom@ and @ToXML@ instances
31+-}
32+data GenType = Parser | Generator | Both
3633
37−isLenientType :: GenType -> Bool
38−isLenientType = \case
39− LenientParser -> True
40− LenientParserAndGenerator -> True
41− _ -> False
34+{- | Marks the style of code generation regarding records fields
35+@LensRenaming@ - fields get renamed by prefixing the field name with
36+'_' and lowercased capital letter used in the type name.
37+e.g. for
38+
39+> "MyNewDataType" =:= record genType LensRenaming
40+> * "field1"
41+
42+field will be renamed to `_mndtField1` and lens `mndtField1` will be created.
43+
44+@NoLens@ - no field renaming and no lens generation.
45+-}
46+data GenerateLens = LensRenaming | NoLens
4247
4348data XmlFieldPlural
4449 = XmlFieldPluralMandatory -- Occurs exactly 1 time (Identity)
@@ -69,7 +74,7 @@ data IsoXmlDescRecordPart
6974 | IsoXmlDescRecordAttribute IsoXmlDescAttribute
7075 | IsoXmlDescRecordContent IsoXmlDescContent
7176
72−data IsoXmlDescRecord = IsoXmlDescRecord GenType [IsoXmlDescRecordPart]
77+data IsoXmlDescRecord = IsoXmlDescRecord GenType GenerateLens [IsoXmlDescRecordPart]
7378
7479makePrisms ''IsoXmlDescRecord
7580
@@ -90,26 +95,34 @@ appendField
9095 -> IsoXmlDescRecord
9196 -> IsoXmlDescPreField
9297 -> IsoXmlDescRecord
93−appendField plural (IsoXmlDescRecord genType fields) (IsoXmlDescPreField name ty) =
94− let xfield = IsoXmlDescRecordField $ IsoXmlDescField plural name ty
95− in IsoXmlDescRecord genType (xfield:fields)
98+appendField
99+ plural
100+ (IsoXmlDescRecord genType generateLens fields)
101+ (IsoXmlDescPreField name ty) =
102+ let xfield = IsoXmlDescRecordField $ IsoXmlDescField plural name ty
103+ in IsoXmlDescRecord genType generateLens (xfield:fields)
96104
97105appendAttribute
98106 :: XmlAttributePlural
99107 -> IsoXmlDescRecord
100108 -> IsoXmlDescPreAttribute
101109 -> IsoXmlDescRecord
102−appendAttribute plural (IsoXmlDescRecord genType fields) (IsoXmlDescPreAttribute name ty) =
103− let xattribute = IsoXmlDescRecordAttribute $ IsoXmlDescAttribute plural name ty
104− in IsoXmlDescRecord genType (xattribute:fields)
110+appendAttribute
111+ plural
112+ (IsoXmlDescRecord genType generateLens fields)
113+ (IsoXmlDescPreAttribute name ty) =
114+ let xattribute = IsoXmlDescRecordAttribute $ IsoXmlDescAttribute plural name ty
115+ in IsoXmlDescRecord genType generateLens (xattribute:fields)
105116
106117appendContent
107118 :: IsoXmlDescRecord
108119 -> IsoXmlDescPreContent
109120 -> IsoXmlDescRecord
110−appendContent (IsoXmlDescRecord genType fields) (IsoXmlDescPreContent name ty) =
111− let xcontent = IsoXmlDescRecordContent $ IsoXmlDescContent name ty
112− in IsoXmlDescRecord genType (xcontent:fields)
121+appendContent
122+ (IsoXmlDescRecord genType generateLens fields)
123+ (IsoXmlDescPreContent name ty) =
124+ let xcontent = IsoXmlDescRecordContent $ IsoXmlDescContent name ty
125+ in IsoXmlDescRecord genType generateLens (xcontent:fields)
113126
114127(!), (?), (*), (+) :: IsoXmlDescRecord -> IsoXmlDescPreField -> IsoXmlDescRecord
115128(!) = appendField XmlFieldPluralMandatory
@@ -147,11 +160,11 @@ class Description name desc | desc -> name where
147160infix 0 =:=
148161
149162instance Description PrefixName IsoXmlDescRecord where
150− prefixName =:= (IsoXmlDescRecord genType descRecordParts) =
151− isoXmlGenerateDatatype genType prefixName (reverse descRecordParts)
163+ prefixName =:= (IsoXmlDescRecord genType generateLens descRecordParts) =
164+ isoXmlGenerateDatatype genType generateLens prefixName (reverse descRecordParts)
152165
153−record :: GenType -> IsoXmlDescRecord
154−record gt = IsoXmlDescRecord gt []
166+record :: GenType -> GenerateLens -> IsoXmlDescRecord
167+record gt gl = IsoXmlDescRecord gt gl []
155168
156169enum :: GenType -> IsoXmlDescEnum
157170enum gt = IsoXmlDescEnum gt []
@@ -160,7 +173,7 @@ instance Description ExhaustivenessName IsoXmlDescEnum where
160173 exhaustivenessName =:= (IsoXmlDescEnum genType descEnumCons) =
161174 isoXmlGenerateEnum genType exhaustivenessName (reverse descEnumCons)
162175
163−instance (m ~ Q) => IsString (m TH.Type -> IsoXmlDescPreField) where
176+instance IsString (TH.TypeQ -> IsoXmlDescPreField) where
164177 fromString = IsoXmlDescPreField
165178
166179instance IsString IsoXmlDescPreField where
@@ -168,10 +181,10 @@ instance IsString IsoXmlDescPreField where
168181 where
169182 ty = (TH.conT . TH.mkName) ("Xml" ++ over _head C.toUpper (xmlLocalName name))
170183
171−instance (m ~ Q) => IsString (m TH.Type -> IsoXmlDescPreAttribute) where
184+instance IsString (TH.TypeQ -> IsoXmlDescPreAttribute) where
172185 fromString = IsoXmlDescPreAttribute
173186
174−instance (m ~ Q) => IsString (m Type -> IsoXmlDescPreContent) where
187+instance IsString (TH.TypeQ -> IsoXmlDescPreContent) where
175188 fromString = IsoXmlDescPreContent
176189
177190instance IsString IsoXmlDescPreAttribute where
@@ -197,7 +210,7 @@ instance IsString ExhaustivenessName where
197210 fromString strName = ExhaustivenessName strName NonExhaustive
198211
199212makeNamePrefix :: String -> String
200−makeNamePrefix = map C.toLower . filter (\c -> C.isUpper c || C.isDigit c)
213+makeNamePrefix = map C.toLower . filter C.isUpper
201214
202215funSimple :: TH.Name -> TH.ExpQ -> TH.DecQ
203216funSimple name body = TH.funD name [ TH.clause [] (TH.normalB body) [] ]
@@ -225,14 +238,11 @@ isoXmlGenerateEnum genType (ExhaustivenessName strName' exh) enumCons = do
225238 (return [])
226239 [t|ToXmlAttribute $(TH.conT name)|]
227240 [funSimple 'toXmlAttribute [e|T.pack . show|]]
228− genFromDomInst mode = do
241+ genFromDomInst = do
229242 TH.instanceD
230243 (return [])
231244 [t|FromDom $(TH.conT name)|]
232− [case mode of
233− Strict -> funSimple 'fromDom [e|parseContent readContent|]
234− Lenient -> funSimple 'fromDom [e|ignoreBlank $ parseContent readContent|]
235− ]
245+ [funSimple 'fromDom [e|parseContent readContent|]]
236246 genFromAttributeInst = do
237247 TH.instanceD
238248 (return [])
@@ -244,36 +254,36 @@ isoXmlGenerateEnum genType (ExhaustivenessName strName' exh) enumCons = do
244254 toXmlAttributeInst <- genToXmlAttributeInst
245255 return $ enumDecls ++ [toXmlInst, toXmlAttributeInst]
246256 Parser -> do
247− fromDomInst <- genFromDomInst Strict
257+ fromDomInst <- genFromDomInst
248258 fromAttributeInst <- genFromAttributeInst
249259 return $ enumDecls ++ [fromDomInst, fromAttributeInst]
250− LenientParser -> do
251− fromDomInst <- genFromDomInst Lenient
252− fromAttributeInst <- genFromAttributeInst
253− return $ enumDecls ++ [fromDomInst, fromAttributeInst]
254− ParserAndGenerator -> do
260+ Both -> do
255261 toXmlInst <- genToXmlInst
256262 toXmlAttributeInst <- genToXmlAttributeInst
257− fromDomInst <- genFromDomInst Strict
258− fromAttributeInst <- genFromAttributeInst
259− return $ enumDecls ++ [toXmlInst, toXmlAttributeInst,
260− fromDomInst, fromAttributeInst]
261− LenientParserAndGenerator -> do
262− toXmlInst <- genToXmlInst
263− toXmlAttributeInst <- genToXmlAttributeInst
264− fromDomInst <- genFromDomInst Lenient
263+ fromDomInst <- genFromDomInst
265264 fromAttributeInst <- genFromAttributeInst
266265 return $ enumDecls ++ [toXmlInst, toXmlAttributeInst,
267266 fromDomInst, fromAttributeInst]
268267
269−isoXmlGenerateDatatype :: GenType -> PrefixName -> [IsoXmlDescRecordPart] -> TH.DecsQ
270−isoXmlGenerateDatatype genType (PrefixName strName' strPrefix') descRecordParts = do
268+isoXmlGenerateDatatype
269+ :: GenType
270+ -> GenerateLens
271+ -> PrefixName
272+ -> [IsoXmlDescRecordPart]
273+ -> TH.DecsQ
274+isoXmlGenerateDatatype
275+ genType
276+ generateLens
277+ (PrefixName strName' strPrefix')
278+ descRecordParts = do
271279 let
272280 isNewtype = length descRecordParts == 1
273281 strName = "Xml" ++ strName'
274282 strPrefix = "x" ++ strPrefix'
275283 name = TH.mkName strName
276− fieldName str = "_" ++ strPrefix ++ over _head C.toUpper str
284+ fieldName str = case generateLens of
285+ LensRenaming -> "_" ++ strPrefix ++ over _head C.toUpper str
286+ NoLens -> str
277287 termDecl <- do
278288 let
279289 fields = do
@@ -284,14 +294,11 @@ isoXmlGenerateDatatype genType (PrefixName strName' strPrefix') descRecordParts
284294 IsoXmlDescField fieldPlural rawName fieldType = descField
285295 fieldStrName = xmlLocalName rawName
286296 fName = TH.mkName (fieldName fieldStrName)
287− fType' = case fieldPlural of
297+ fType = case fieldPlural of
288298 XmlFieldPluralMandatory -> fieldType
289299 XmlFieldPluralOptional -> [t| Maybe $fieldType |]
290300 XmlFieldPluralRepeated -> [t| [$fieldType] |]
291301 XmlFieldPluralMultiplied -> [t| NonEmpty $fieldType |]
292− fType = if isLenientType genType
293− then [t| Maybe $fType' |]
294− else fType'
295302 in if isNewtype
296303 then THC.varStrictType fName (THC.nonStrictType fType)
297304 else THC.varStrictType fName (THC.strictType fType)
@@ -318,7 +325,9 @@ isoXmlGenerateDatatype genType (PrefixName strName' strPrefix') descRecordParts
318325 -- generate a newtype instead to do less allocations later
319326 then THC.newtypeD name (TH.recC name fields) [''Eq, ''Show, ''Generic]
320327 else THC.dataD name [TH.recC name fields] [''Eq, ''Show, ''Generic]
321− lensDecls <- makeFieldOpticsForDec lensRules termDecl
328+ lensDecls <- case generateLens of
329+ NoLens -> pure []
330+ LensRenaming -> makeFieldOpticsForDec lensRules termDecl
322331 nfDataInst <- do
323332 TH.instanceD
324333 (return [])
@@ -326,7 +335,7 @@ isoXmlGenerateDatatype genType (PrefixName strName' strPrefix') descRecordParts
326335 [ ]
327336
328337 let
329− genFromDomInst mode = do
338+ genFromDomInst = do
330339 let
331340 exprHeader = [e|pure $(TH.conE name)|]
332341 exprRecordParts = do
@@ -340,9 +349,8 @@ isoXmlGenerateDatatype genType (PrefixName strName' strPrefix') descRecordParts
340349 fieldParse = case fieldPlural of
341350 XmlFieldPluralMandatory -> [e|inElem|]
342351 _ -> [e|inElemTrav|]
343− in case mode of
344− Strict -> [e|$fieldParse $exprFieldStrName fromDom|]
345− Lenient -> [e|$fieldParse $exprFieldStrName (ignoreBlank fromDom)|]
352+ in
353+ [e|$fieldParse $exprFieldStrName fromDom|]
346354 IsoXmlDescRecordAttribute descAttribute ->
347355 let
348356 IsoXmlDescAttribute attributePlural attributeStrName _ = descAttribute
@@ -428,21 +436,12 @@ isoXmlGenerateDatatype genType (PrefixName strName' strPrefix') descRecordParts
428436 return $ [termDecl] ++ lensDecls ++
429437 [toXmlInst, toXmlParentAttributesInst, nfDataInst]
430438 Parser -> do
431− fromDomInst <- genFromDomInst Strict
439+ fromDomInst <- genFromDomInst
432440 return $ [termDecl] ++ lensDecls ++ [fromDomInst, nfDataInst]
433− LenientParser -> do
434− fromDomInst <- genFromDomInst Lenient
435− return $ [termDecl] ++ lensDecls ++ [fromDomInst, nfDataInst]
436− ParserAndGenerator -> do
437− toXmlInst <- genToXmlInst
438− toXmlParentAttributesInst <- genToXmlParentAttributeInst
439− fromDomInst <- genFromDomInst Strict
440− return $ [termDecl] ++ lensDecls ++
441− [fromDomInst, toXmlInst, toXmlParentAttributesInst, nfDataInst]
442− LenientParserAndGenerator -> do
441+ Both -> do
443442 toXmlInst <- genToXmlInst
444443 toXmlParentAttributesInst <- genToXmlParentAttributeInst
445− fromDomInst <- genFromDomInst Lenient
444+ fromDomInst <- genFromDomInst
446445 return $ [termDecl] ++ lensDecls ++
447446 [fromDomInst, toXmlInst, toXmlParentAttributesInst, nfDataInst]
448447