git.iko.soy/typeable/dom-parser
on 413d8d3 help sign in
16 files changed+347−756
since splitsnapshots
unifiedsplit
.travis.yml−32 CHANGELOG.md−44 README.md−5 dom-parser.cabal+11−17 ghc-963.yaml−7 src/Text/XML/DOM/Parser.hs+2−10 src/Text/XML/DOM/Parser/Attributes.hs−64 src/Text/XML/DOM/Parser/Buildable.hs−26 src/Text/XML/DOM/Parser/FromDom.hs → src/Text/XML/DOM/Parser/Class.hs+89−55 src/Text/XML/DOM/Parser/Combinators.hs+166−53 src/Text/XML/DOM/Parser/Content.hs−129 src/Text/XML/DOM/Parser/FromAttribute.hs−112 src/Text/XML/DOM/Parser/Types.hs+63−145 src/Text/XML/Soap.hs−46 stack.yaml+6−1 test/Main.hs+10−10
.travis.ymldeleted−32
@@ -1,32 +0,0 @@
1−# Use new container infrastructure to enable caching
2−sudo: false
3−
4−# Do not choose a language; we provide our own build tools.
5−language: generic
6−
7−env:
8− - STACK_YAML=stack.yaml
9− - STACK_YAML=ghc-802.yaml
10− - STACK_YAML=ghc-822.yaml
11− - STACK_YAML=ghc-841.yaml
12−
13−# Caching so the next build will be fast too.
14−cache:
15− directories:
16− - $HOME/.stack
17−
18−# Ensure necessary system libraries are present
19−addons:
20− apt:
21− packages:
22− - libgmp-dev
23−
24−before_install:
25−# Download and unpack the stack executable
26−- mkdir -p ~/.local/bin
27−- export PATH=$HOME/.local/bin:$PATH
28−- travis_retry curl -L https://www.stackage.org/stack/linux-x86_64 | tar xz --wildcards --strip-components=1 -C ~/.local/bin '*/stack'
29−
30−script:
31−# Build the package, its tests, and its docs and run the tests
32−- stack --no-terminal --install-ghc test --haddock --no-haddock-deps
CHANGELOG.md−44
@@ -1,54 +1,10 @@
11# CHANGELOG
22
3−## HEAD
4−
5−## 3.2.0
6−
7−### Added
8−
9−* Text.XML.Soap module with XmlEnvelope stuff in it.
10−* GHC 9 compatibility.
11−
12−### Removed
13−
14−* Old stackage files.
15−
16−## 3.1.0
17−
18−### Added
19−
20−* Additional builds with GHC 8.0.2 and 8.2.1
21−* Compatibility with GHC 8.4.1
22−
23−## 3.0.0
24−
25−* GHC 8 compatible release
26−
27−### Removed
28−
29−* `open-union` dependency
30−
31−## 2.0.0
32−
33−### Added
34−
35−* `NameMatcher` and `ElemMatcher` as more flexible mechanisms for
36− matching names and elements instead of just matching by name.
37−* Class `FromAttribute` and some default instances
38−
39−### Changed
40−
41−* `DomTraversable` renamed to `Buildable`
42−* Combinators use `NameMatcher` and `ElemMatcher`
43−* Documentation improved
44−
453## 1.0.0
46−
474* Added support for parsing attributes
485* Changed public interface for 'parseContent'
496* Added useful functions
507* Dropped unnecessary dependency from shakespeare
518
529## 0.1.0
53−
5410* Public release
README.mddeleted−5
@@ -1,5 +0,0 @@
1−# dom-parser
2−
3−[![Build Status](https://travis-ci.org/typeable/dom-parser.svg?branch=master)](https://travis-ci.org/typeable/dom-parser)
4−
5−Simple monadic DOM parser
dom-parser.cabal+11−17
@@ -1,5 +1,5 @@
11name: dom-parser
2−version: 3.2.0
2+version: 1.0.0
33synopsis: Simple monadic DOM parser
44license: MIT
55license-file: LICENSE
@@ -7,13 +7,12 @@ author: Typeable.io contributors
77maintainer: makeit@typeable.io
88category: XML
99build-type: Simple
10+extra-source-files: CHANGELOG.md
1011cabal-version: >=1.10
12+
13+extra-source-files: CHANGELOG.md
14+
1115homepage: https://github.com/typeable/dom-parser
12−tested-with: GHC == 9.2.7
13− , GHC == 9.4.8
14− , GHC == 9.6.3
15−extra-source-files: CHANGELOG.md
16− , README.md
1716
1817source-repository head
1918 type: git
@@ -44,26 +43,21 @@ library
4443 , TypeFamilies
4544 , TypeOperators
4645 , UndecidableInstances
47− build-depends: base >= 4.11 && < 5
48− , case-insensitive
46+ build-depends: base >= 4.7 && < 5
4947 , containers
5048 , lens
51− , mtl < 2.3 || > 2.3
52− , scientific
49+ , mtl
50+ , open-union
51+ , semigroups
5352 , text
5453 , transformers
54+ , type-fun
5555 , xml-conduit
56− , xml-conduit-writer
5756 , xml-lens
5857 exposed-modules: Text.XML.DOM.Parser
59− , Text.XML.DOM.Parser.Attributes
60− , Text.XML.DOM.Parser.Buildable
58+ , Text.XML.DOM.Parser.Class
6159 , Text.XML.DOM.Parser.Combinators
62− , Text.XML.DOM.Parser.Content
63− , Text.XML.DOM.Parser.FromAttribute
64− , Text.XML.DOM.Parser.FromDom
6560 , Text.XML.DOM.Parser.Types
66− , Text.XML.Soap
6761
6862test-suite test
6963 type: exitcode-stdio-1.0
ghc-963.yamldeleted−7
@@ -1,7 +0,0 @@
1−resolver: lts-22.6
2−
3−extra-deps:
4− - xml-conduit-writer-0.1.1.5
5−
6−nix:
7− packages: [zlib]
src/Text/XML/DOM/Parser.hs+2−10
@@ -1,17 +1,9 @@
11module Text.XML.DOM.Parser
2− ( module Text.XML.DOM.Parser.Attributes
3− , module Text.XML.DOM.Parser.Buildable
2+ ( module Text.XML.DOM.Parser.Class
43 , module Text.XML.DOM.Parser.Combinators
5− , module Text.XML.DOM.Parser.Content
6− , module Text.XML.DOM.Parser.FromAttribute
7− , module Text.XML.DOM.Parser.FromDom
84 , module Text.XML.DOM.Parser.Types
95 ) where
106
11−import Text.XML.DOM.Parser.Attributes
12−import Text.XML.DOM.Parser.Buildable
7+import Text.XML.DOM.Parser.Class
138import Text.XML.DOM.Parser.Combinators
14−import Text.XML.DOM.Parser.Content
15−import Text.XML.DOM.Parser.FromAttribute
16−import Text.XML.DOM.Parser.FromDom
179import Text.XML.DOM.Parser.Types
src/Text/XML/DOM/Parser/Attributes.hsdeleted−64
@@ -1,64 +0,0 @@
1−module Text.XML.DOM.Parser.Attributes
2− ( parseAttribute
3− , parseAttributeMaybe
4− , getCurrentAttributes
5− , getCurrentAttribute
6− ) where
7−
8−import Control.Lens
9−import Data.Map.Strict as M
10−import Data.Text as T
11−import Text.XML.DOM.Parser.Types
12−import Text.XML.Lens
13−
14−-- | Parses attribute with given name, throws error if attribute is not found.
15−--
16−-- @since 1.0.0
17−parseAttribute
18− :: (Monad m)
19− => NameMatcher
20− -- ^ Attribute name
21− -> (Text -> Either Text a)
22− -- ^ Attribute content parser
23− -> DomParserT Identity m a
24−parseAttribute attrName parser =
25− parseAttributeMaybe attrName parser >>= \case
26− Nothing -> throwParserError $ PEAttributeNotFound attrName
27− Just a -> return a
28−
29−-- | Parses attribute with given name. Returns Nothing if attribute is
30−-- not found.
31−--
32−-- @since 1.0.0
33−parseAttributeMaybe
34− :: (Monad m)
35− => NameMatcher
36− -- ^ Attribute name
37− -> (Text -> Either Text a)
38− -- ^ Attribute content parser
39− -> DomParserT Identity m (Maybe a)
40−parseAttributeMaybe attrName parser =
41− getCurrentAttribute attrName >>= \case
42− Nothing -> return Nothing
43− Just aval -> case parser aval of
44− Left err -> throwParserError $ PEAttributeWrongFormat attrName err
45− Right a -> return $ Just a
46−
47−-- | Retuns map of attributes of current element
48−--
49−-- @since 1.0.0
50−getCurrentAttributes
51− :: (Monad m)
52− => DomParserT Identity m (M.Map Name Text)
53−getCurrentAttributes = view $ pdElements . to runIdentity . attrs
54−
55−-- | Returns element with given name or 'Nothing'
56−--
57−-- @since 1.0.0
58−getCurrentAttribute
59− :: (Monad m)
60− => NameMatcher
61− -> DomParserT Identity m (Maybe Text)
62−getCurrentAttribute attrName
63− = preview $ pdElements . to runIdentity . attrs . to M.toList
64− . traversed . filtered (views _1 (attrName ^. nmMatch)) . _2
src/Text/XML/DOM/Parser/Buildable.hsdeleted−26
@@ -1,26 +0,0 @@
1−module Text.XML.DOM.Parser.Buildable
2− ( Buildable(..)
3− ) where
4−
5−import Data.Functor.Identity
6−import Data.List.NonEmpty as NE
7−import Data.Maybe
8−
9−-- | Class of traversable functors which may be constructed from list
10−class Traversable f => Buildable f where
11− -- | If method return Nothing this means we can not build
12− -- traversable from given list. In this case 'inFilteredTrav' should
13− -- fail traversing.
14− build :: [a] -> Maybe (f a)
15−
16−instance Buildable Identity where
17− build = fmap Identity . listToMaybe
18−
19−instance Buildable [] where
20− build = Just
21−
22−instance Buildable Maybe where
23− build = Just . listToMaybe
24−
25−instance Buildable NonEmpty where
26− build = NE.nonEmpty
src/Text/XML/DOM/Parser/FromDom.hs → src/Text/XML/DOM/Parser/Class.hsrenamed+89−55
@@ -1,32 +1,37 @@
1−module Text.XML.DOM.Parser.FromDom
1+module Text.XML.DOM.Parser.Class
22 ( -- * FromDom
33 FromDom(..)
44 , proxyFromDom
55 -- * Explicit methods for convenience
66 , elementFromDom
7+ , unionFromDom
78 , textFromDom
89 , stringFromDom
910 , charFromDom
11+ , readChar
1012 , intFromDom
1113 , integerFromDom
1214 , doubleFromDom
1315 , fixedFromDom
1416 , boolFromDom
17+ , readBool
1518 , unitFromDom
1619 , voidFromDom
17− , scientificFromDom
1820 ) where
1921
20−import Control.Applicative
21−import Control.Lens
22−import Data.Fixed
23−import Data.Scientific
24−import Data.Text as T hiding (empty)
25−import Data.Typeable
26−import Data.Void
27−import Text.XML
28−import Text.XML.DOM.Parser.Content
29−import Text.XML.DOM.Parser.Types
22+import Control.Applicative
23+import Control.Lens
24+import Data.Fixed
25+import Data.Monoid
26+import Data.OpenUnion
27+import Data.Text (Text)
28+import qualified Data.Text as T
29+import Data.Typeable
30+import Data.Void
31+import Text.XML
32+import Text.XML.DOM.Parser.Combinators
33+import Text.XML.DOM.Parser.Types
34+import TypeFun.Data.List hiding (Union)
3035
3136proxyFromDom
3237 :: forall proxy m a
@@ -35,86 +40,115 @@ proxyFromDom
3540 -> DomParserT Identity m a
3641proxyFromDom _ = fromDom
3742
38−-- | Class of types which can be parsed from single XML element. The
39−-- method 'fromDom' is convenient default to use with 'inElem'
43+-- | Class of types which can be parsed from single XML element.
4044class FromDom a where
4145 fromDom :: (Monad m) => DomParserT Identity m a
4246
4347instance FromDom () where
4448 fromDom = unitFromDom
4549
46−-- | Always successfully parses any DOM to @()@
47−unitFromDom :: (Monad m) => DomParserT Identity m ()
48−unitFromDom = pure ()
49−
5050instance FromDom Void where
5151 fromDom = voidFromDom
5252
53−-- | Never parses successfully. It is just 'empty'
54−voidFromDom :: (Monad m) => DomParserT Identity m Void
55−voidFromDom = empty
56−
5753instance FromDom Text where
5854 fromDom = textFromDom
5955
60−textFromDom :: (Monad m) => DomParserT Identity m Text
61−textFromDom = parseContent Right
62−
6356instance FromDom String where
6457 fromDom = stringFromDom
6558
66−stringFromDom :: (Monad m) => DomParserT Identity m String
67−stringFromDom = parseContent $ Right . T.unpack
68−
6959instance FromDom Char where
7060 fromDom = charFromDom
7161
62+instance FromDom Int where
63+ fromDom = intFromDom
64+
65+instance FromDom Integer where
66+ fromDom = integerFromDom
67+
68+instance FromDom Double where
69+ fromDom = doubleFromDom
70+
71+instance (HasResolution a, Typeable a) => FromDom (Fixed a) where
72+ fromDom = fixedFromDom
73+
74+instance FromDom Bool where
75+ fromDom = boolFromDom
76+
77+instance FromDom (Union '[]) where
78+ fromDom = empty
79+
80+instance ( Typeable a, FromDom a, FromDom (Union as)
81+ , SubList as (a ': as) )
82+ => FromDom (Union (a ': as)) where
83+ -- fromDom :: forall m. (DomParserMonad m) => m a
84+ fromDom = (liftUnion <$> (proxyFromDom (Proxy :: Proxy a)))
85+ <|> (reUnion <$> (proxyFromDom (Proxy :: Proxy (Union as))))
86+
87+instance FromDom Element where
88+ fromDom = elementFromDom
89+
90+elementFromDom :: (Monad m) => DomParserT Identity m Element
91+elementFromDom = view $ pdElements . to runIdentity
92+
93+unionFromDom
94+ :: (Monad m, FromDom (Union as))
95+ => proxy as
96+ -> DomParserT Identity m (Union as)
97+unionFromDom _ = fromDom
98+
99+textFromDom :: (Monad m) => DomParserT Identity m Text
100+textFromDom = parseContent Right
101+
102+stringFromDom :: (Monad m) => DomParserT Identity m String
103+stringFromDom = parseContent $ Right . T.unpack
104+
72105charFromDom :: (Monad m) => DomParserT Identity m Char
73106charFromDom = parseContent readChar
74107
75−instance FromDom Int where
76− fromDom = intFromDom
108+-- | Expects text to be single character
109+--
110+-- @since 1.0.0
111+readChar :: Text -> Either Text Char
112+readChar t = case T.unpack $ T.strip t of
113+ [c] -> Right c
114+ _ -> Left "Should have exactly one non-blank character"
77115
78116intFromDom :: (Monad m) => DomParserT Identity m Int
79117intFromDom = parseContent readContent
80118
81−instance FromDom Integer where
82− fromDom = integerFromDom
83−
84119integerFromDom :: (Monad m) => DomParserT Identity m Integer
85120integerFromDom = parseContent readContent
86121
87−instance FromDom Double where
88− fromDom = doubleFromDom
89−
90122doubleFromDom :: (Monad m) => DomParserT Identity m Double
91123doubleFromDom = parseContent readContent
92124
93−instance (HasResolution a, Typeable a) => FromDom (Fixed a) where
94− fromDom = fixedFromDom
95−
96125fixedFromDom
97126 :: (Monad m, Typeable a, HasResolution a)
98127 => DomParserT Identity m (Fixed a)
99128fixedFromDom = parseContent readContent
100129
101−instance FromDom Scientific where
102− fromDom = scientificFromDom
103−
104−scientificFromDom :: Monad m => DomParserT Identity m Scientific
105−scientificFromDom = parseContent readContent
106−
107−instance FromDom Bool where
108− fromDom = boolFromDom
109−
110−-- | Expects content to be y, yes, t, true or 1 for True Values n, no,
111−-- f, false or 0 for False. Case is not significant, blank characters
112−-- are striped.
130+-- | Expects content to be y, yes, t, true or 1 for True value. n, no,
131+-- f, false or 0 for False value. Case is not significant, blank
132+-- characters are striped.
113133boolFromDom :: (Monad m) => DomParserT Identity m Bool
114134boolFromDom = parseContent readBool
115135
116−instance FromDom Element where
117− fromDom = elementFromDom
136+-- | @since 1.0.0
137+readBool :: Text -> Either Text Bool
138+readBool t =
139+ let
140+ lowt = T.toLower $ T.strip t
141+ tvals = ["y", "yes", "t", "true", "1"]
142+ fvals = ["n", "no", "f", "false", "0"]
143+ in if | lowt `elem` tvals -> Right True
144+ | lowt `elem` fvals -> Right False
145+ | otherwise ->
146+ Left $ "Could not read " <> t <> " as Bool"
118147
119−elementFromDom :: (Monad m) => DomParserT Identity m Element
120−elementFromDom = view $ pdElements . to runIdentity
148+-- | Always successfully parses any DOM to @()@
149+unitFromDom :: (Monad m) => DomParserT Identity m ()
150+unitFromDom = pure ()
151+
152+-- | Never parses successfully. It is just 'mzero'
153+voidFromDom :: (Monad m) => DomParserT Identity m Void
154+voidFromDom = empty
src/Text/XML/DOM/Parser/Combinators.hs+166−53
@@ -2,7 +2,7 @@ module Text.XML.DOM.Parser.Combinators
22 ( -- * Generic combinators to traverse descendants
33 traverseElems
44 , inFilteredTrav
5− -- * Using 'Buildable'
5+ -- * Using 'DomTraversable'
66 , inElemTrav
77 , inElem
88 , inElemAll
@@ -15,106 +15,116 @@ module Text.XML.DOM.Parser.Combinators
1515 , ignoreElem
1616 , ignoreEmpty
1717 , ignoreBlank
18+ -- * Getting current element's properties
19+ , getCurrentName
20+ , getCurrentContent
21+ , getCurrentAttributes
22+ , getCurrentAttribute
23+ -- * Current element's checks
24+ , checkCurrentName
25+ -- * Parsing element's content
26+ , parseContent
27+ , readContent
28+ , maybeReadContent
29+ -- * Parsing attributes
30+ , parseAttribute
1831 ) where
1932
20−import Control.Lens
21−import Control.Monad.Reader
22−import Data.Foldable as F
23−import Data.List as L
24−import Data.List.NonEmpty (NonEmpty(..))
25−import Data.Text as T
26−import Data.Traversable
27−import Text.XML
28−import Text.XML.DOM.Parser.Buildable
29−import Text.XML.DOM.Parser.Types
30−import Text.XML.Lens
33+import Control.Lens
34+import Control.Monad.Except
35+import Control.Monad.Reader
36+import Data.List.NonEmpty (NonEmpty(..))
37+import qualified Data.Map.Strict as M
38+import Data.Monoid
39+import Data.Text (Text)
40+import qualified Data.Text as T
41+import Data.Traversable
42+import Data.Typeable
43+import Text.Read
44+import Text.XML
45+import Text.XML.DOM.Parser.Types
46+import Text.XML.Lens
3147
3248
33−-- | Generic function to traverse arbitrary inner elements.
49+-- | Generic function to traverse arbitrary inner cursors.
3450traverseElems
3551 :: (Monad m, Foldable g, Traversable f)
3652 => ([Element] -> DomParserT g m (f (DomPath, Element)))
3753 -- ^ Takes list of current elements and returns container with
38− -- pairs of subpath (relatively to current elements) and element
54+ -- pairs of subpath (relatively to current element) and element
3955 -- to run parser in
4056 -> DomParserT Identity m a
41− -- ^ Parser to run for each element found in traversable 'f'
57+ -- ^ Parser will be runned for each element found in traversable
4258 -> DomParserT g m (f a)
4359traverseElems trav parser = do
4460 pd <- ask
4561 inner <- trav $ pd ^.. pdElements . folded
4662 for inner $ \(subpath, e) -> do
47− let
48− newpd = ParserData
49− { _pdElements = Identity e
50− , _pdPath = pd ^. pdPath <> subpath }
51− lift $ runReaderT parser newpd
63+ let newpd = ParserData
64+ { _pdElements = Identity e
65+ , _pdPath = pd ^. pdPath <> subpath }
66+ magnify (to $ const newpd) parser -- type of reader is changed, so
67+ -- local does not work
5268
53−-- | Traverses elements located in same path using filtering function
69+-- | Takes function filtering
5470inFilteredTrav
55− :: (Monad m, Foldable g, Buildable f)
71+ :: (Monad m, Foldable g, DomTraversable f)
5672 => ([Element] -> (DomPath, [Element]))
57− -- ^ Takes list of current elements and returns some descendants
58− -- subset and path this descendants located at. Path is should be
59− -- same for all descendants and required for error message
73+ -- ^ Function returning some filtered elements with path suffixes which will
74+ -- be appended to parser's state
6075 -> DomParserT Identity m a
6176 -> DomParserT g m (f a)
6277inFilteredTrav deeper = traverseElems trav
6378 where
6479 trav e = do
6580 let (path, elems) = deeper e
66− case build elems of
81+ case buildDomTraversable elems of
6782 Nothing -> throwParserError $ PENotFound . (<> path)
6883 Just tr -> return $ fmap (path,) tr
6984
70−-- | Runs parser arbitrary times, depending on 'Buildable' instance of
71−-- 'f'. For example if 'f' becomes 'NonEmpty' then 'inElemTrav' finds
72−-- @one or more@ elements matched by given 'ElemMatcher' and run
73−-- parser in each found element, then returns @NonEmpty a@ of results.
7485inElemTrav
75− :: (Monad m, Foldable g, Buildable f)
76− => ElemMatcher -- ^ Tag(s) matcher to traverse in
86+ :: (Monad m, Foldable g, DomTraversable f)
87+ => Text
7788 -> DomParserT Identity m a
7889 -> DomParserT g m (f a)
7990inElemTrav n = inFilteredTrav deeper
8091 where
81− elemsFold = folded . nodes . folded . _Element . elMatch n
82− deeper = (DomPath [_emShow n],) . toListOf elemsFold
92+ deeper = ([n],) . toListOf (folded . nodes . folded . _Element . ell n)
8393
84−-- | Runs parser inside first children element matched by macher
94+-- | Runs parser inside first children element with given name
8595inElem
8696 :: (Monad m, Foldable g)
87− => ElemMatcher
97+ => Text
8898 -> DomParserT Identity m a
8999 -> DomParserT g m a
90100inElem n = fmap runIdentity . inElemTrav n
91101
92102inElemAll
93103 :: (Monad m, Foldable g)
94− => ElemMatcher
104+ => Text
95105 -> DomParserT Identity m a
96106 -> DomParserT g m [a]
97107inElemAll = inElemTrav
98108
99109inElemMay
100110 :: (Monad m, Foldable g)
101− => ElemMatcher
111+ => Text
102112 -> DomParserT Identity m a
103113 -> DomParserT g m (Maybe a)
104114inElemMay = inElemTrav
105115
106116inElemNe
107117 :: (Monad m, Foldable g)
108− => ElemMatcher
118+ => Text
109119 -> DomParserT Identity m a
110120 -> DomParserT g m (NonEmpty a)
111121inElemNe = inElemTrav
112122
113123{- | Dive given parser's current tags set into the given path. The @divePath
114−["a", "b"]@ differs from @inElem "a" $ inElem "b"@. Namely the first variant
124+["a", "b"]@ differs from @inElem "a" . inElem "b"@. Namely the first variant
115125will not fail if occured tag "a" which does not contains tag "b". This
116126behaviour is desireable when you dont want to parse whole XML and just want
117−to pull tags located in some path. The other difference is in traversing inner
127+to pull tags in some path. The other difference is in traversing inner
118128elements. Consider this code
119129
120130@
@@ -132,31 +142,26 @@ which translates like: @a > b > c > fromDom@
132142As you can see, inElem always takes first element and runs inner parser in this
133143single element, unlike 'divePath' which runs inner parser @in all@ descendants
134144in given path.
135−
136−Note also that 'divePath' takes parser parameterized by @[]@ not by
137−'Identity'. This because when you dive using some path you will get a
138−list of found elements and all these elements will be @current@ for
139−parser.
140145-}
141146
142147divePath
143148 :: forall m g a
144149 . (Monad m, Foldable g)
145− => [ElemMatcher]
150+ => [Text]
146151 -> DomParserT [] m a
147152 -> DomParserT g m a
148153divePath path = magnify $ to modElems
149154 where
150155 modElems
151156 = over pdElements (toListOf $ folded . diver)
152− . over pdPath (<> DomPath (L.map _emShow path))
157+ . over pdPath (<> path)
153158 diver :: Fold Element Element
154− diver = F.foldr (.) id $ L.map toDive path
155− toDive n = nodes . folded . _Element . elMatch n
159+ diver = foldr (.) id $ map toDive path
160+ toDive n = nodes . folded . _Element . ell n
156161
157162diveElem
158163 :: (Monad m, Foldable g)
159− => ElemMatcher
164+ => Text
160165 -> DomParserT [] m a
161166 -> DomParserT g m a
162167diveElem p = divePath [p]
@@ -183,7 +188,7 @@ ignoreEmpty
183188 -> DomParserT Identity m (Maybe a)
184189ignoreEmpty = ignoreElem test
185190 where
186− test e = L.null $ e ^. nodes
191+ test e = null $ e ^. nodes
187192
188193-- | If all current elements contains blank content, or contains nothing at all
189194-- , then returns Nothing, else runs parser.
@@ -197,6 +202,114 @@ ignoreBlank = ignoreElem test
197202 let
198203 elems = e ^.. nodes . folded . _Element
199204 cont = mconcat $ e ^.. nodes . folded . _Content
200− in if | not $ L.null elems -> False
205+ in if | not $ null elems -> False
201206 | T.null $ T.strip cont -> True
202207 | otherwise -> False
208+
209+-- | Returns name of current element.
210+--
211+-- @since 1.0.0
212+getCurrentName :: (Monad m) => DomParserT Identity m Text
213+getCurrentName = view $ pdElements . to runIdentity . localName
214+
215+-- | If name of current tag differs from first argument throws 'PENotFound' with
216+-- tag name replaced in last path's segment. Useful for checking root
217+-- document's element name.
218+checkCurrentName
219+ :: (Monad m)
220+ => Text
221+ -> DomParserT Identity m ()
222+checkCurrentName n = do
223+ cn <- getCurrentName
224+ unless (cn == n) $ do
225+ p <- view pdPath
226+ let pinit = if null p then [] else init p
227+ throwError $ ParserErrors [PENotFound $ pinit ++ [n]]
228+ return ()
229+
230+-- | Get current content. If current element contains no content or
231+-- have inner elements then Nothing returned
232+--
233+-- @since 1.0.0
234+getCurrentContent :: (Monad m) => DomParserT Identity m (Maybe Text)
235+getCurrentContent = do
236+ nds <- view $ pdElements . to runIdentity . nodes
237+ let
238+ els :: [Element]
239+ els = nds ^.. folded . _Element
240+ conts :: [Text]
241+ conts = nds ^.. folded . _Content
242+ return $ if
243+ | not $ null els -> Nothing
244+ | null conts -> Nothing
245+ | otherwise -> Just $ mconcat conts
246+
247+-- | Parses content inside current tag. It expects current element set
248+-- consists of exactly ONE element. If current element does not
249+-- contains content or have other elements as childs then throws error
250+parseContent
251+ :: (Monad m)
252+ => (Text -> Either Text a)
253+ -- ^ Content parser, return error msg if value is not parsed
254+ -> DomParserT Identity m a
255+parseContent parse = getCurrentContent >>= \case
256+ Nothing -> throwParserError PEContentNotFound
257+ Just c -> case parse c of
258+ Left e -> throwParserError $ PEWrongFormat e
259+ Right a -> return a
260+
261+-- | If reader returns 'Nothing' then resulting function returns 'Left
262+-- "error message"'
263+--
264+-- @since 1.0.0
265+maybeReadContent
266+ :: forall a
267+ . (Typeable a)
268+ => (Text -> Maybe a)
269+ -- ^ Content or attribute reader
270+ -> Text
271+ -- ^ Content or attribute value
272+ -> Either Text a
273+maybeReadContent f t = maybe (Left msg) Right $ f t
274+ where
275+ msg = "Not readable " <> n <> ": " <> t
276+ n = T.pack $ show $ typeRep (Proxy :: Proxy a)
277+
278+-- | Tries to read given text to value using 'Read'. Useful to use
279+-- with 'parseContent' and 'parseAttribute'
280+readContent
281+ :: (Read a, Typeable a)
282+ => Text
283+ -> Either Text a
284+readContent = maybeReadContent $ readMaybe . T.unpack . T.strip
285+
286+-- | Retuns map of attributes of current element
287+--
288+-- @since 1.0.0
289+getCurrentAttributes :: (Monad m) => DomParserT Identity m (M.Map Name Text)
290+getCurrentAttributes = view $ pdElements . to runIdentity . attrs
291+
292+-- | Returns element with given name or 'Nothing'
293+--
294+-- @since 1.0.0
295+getCurrentAttribute :: (Monad m) => Text -> DomParserT Identity m (Maybe Text)
296+getCurrentAttribute attrName'
297+ = preview $ pdElements . to runIdentity . attr attrName
298+ where
299+ attrName = Name attrName' Nothing Nothing
300+
301+-- | Parses attribute with given name, throws error if attribute is not found.
302+--
303+-- @since 1.0.0
304+parseAttribute
305+ :: (Monad m)
306+ => Text
307+ -- ^ Attribute name
308+ -> (Text -> Either Text a)
309+ -- ^ Attribute content parser
310+ -> DomParserT Identity m a
311+parseAttribute attrName parser = getCurrentAttribute attrName >>= \case
312+ Nothing -> throwParserError $ PEAttributeNotFound attrName
313+ Just aval -> case parser aval of
314+ Left err -> throwParserError $ PEAttributeWrongFormat attrName err
315+ Right a -> return a
src/Text/XML/DOM/Parser/Content.hsdeleted−129
@@ -1,129 +0,0 @@
1−module Text.XML.DOM.Parser.Content
2− (
3− -- * Parsing element's content
4− parseContent
5− -- * Getting current element's properties
6− , getCurrentName
7− , getCurrentContent
8− , checkCurrentName
9− -- * Internal
10− , maybeReadContent
11− , readContent
12− , readBool
13− , readChar
14− ) where
15−
16−import Control.Lens
17−import Control.Monad
18−import Control.Monad.Except
19−import Data.List as L
20−import Data.Monoid
21−import Data.Text as T
22−import Data.Typeable
23−import Prelude as P
24−import Text.Read
25−import Text.XML.DOM.Parser.Types
26−import Text.XML.Lens
27−
28−
29−-- | Parses content inside current tag. It expects current element set
30−-- consists of exactly ONE element.
31−parseContent
32− :: (Monad m)
33− => (Text -> Either Text a)
34− -- ^ Content parser, return error msg if value is not parsed
35− -> DomParserT Identity m a
36−parseContent parse = getCurrentContent >>= \case
37− Nothing -> throwParserError PEContentNotFound
38− Just c -> case parse c of
39− Left e -> throwParserError $ PEContentWrongFormat e
40− Right a -> return a
41−
42−
43−-- | Returns name of current element.
44−--
45−-- @since 1.0.0
46−getCurrentName :: (Monad m) => DomParserT Identity m Name
47−getCurrentName = view $ pdElements . to runIdentity . name
48−
49−-- | If name of current tag differs from first argument throws 'PENotFound' with
50−-- tag name replaced in last path's segment. Useful for checking root
51−-- document's element name.
52−checkCurrentName
53− :: (Monad m)
54− => NameMatcher
55− -> DomParserT Identity m ()
56−checkCurrentName n = do
57− cn <- getCurrentName
58− unless ((n ^. nmMatch) cn) $ do
59− p <- view pdPath
60− let pinit = if L.null (unDomPath p) then [] else L.init $ unDomPath p
61− throwError $ ParserErrors [PENotFound $ DomPath $ pinit ++ [_nmShow n]]
62− return ()
63−
64−-- | Get current content. If current element contains no content or
65−-- have inner elements then Nothing returned
66−--
67−-- @since 1.0.0
68−getCurrentContent :: (Monad m) => DomParserT Identity m (Maybe Text)
69−getCurrentContent = do
70− nds <- view $ pdElements . to runIdentity . nodes
71− let
72− els :: [Element]
73− els = nds ^.. folded . _Element
74− conts :: [Text]
75− conts = nds ^.. folded . _Content
76− return $ if
77− | not $ L.null els -> Nothing
78− | L.null conts -> Nothing
79− | otherwise -> Just $ mconcat conts
80−
81−-- | If reader returns 'Nothing' then resulting function returns 'Left
82−-- "error message"'. 'Typeable' is used for generating usefull error
83−-- message.
84−--
85−-- @since 1.0.0
86−maybeReadContent
87− :: forall a
88− . (Typeable a)
89− => (Text -> Maybe a)
90− -- ^ Content or attribute reader
91− -> Text
92− -- ^ Content or attribute value
93− -> Either Text a
94−maybeReadContent f t = maybe (Left msg) Right $ f t
95− where
96− msg = "Not readable " <> n <> ": " <> t
97− n = T.pack $ P.show $ typeRep (Proxy :: Proxy a)
98−
99−
100−-- | Tries to read given text to value using 'Read'. Useful to use
101−-- with 'parseContent' and 'parseAttribute'. Content is stripped
102−-- before reading.
103−readContent
104− :: (Read a, Typeable a)
105− => Text
106− -> Either Text a
107−readContent = maybeReadContent $ readMaybe . T.unpack . T.strip
108−
109−
110−-- | @since 1.0.0
111−readBool :: Text -> Either Text Bool
112−readBool t =
113− let
114− lowt = T.toLower $ T.strip t
115− tvals = ["y", "yes", "t", "true", "1"]
116− fvals = ["n", "no", "f", "false", "0"]
117− in if
118− | lowt `L.elem` tvals -> Right True
119− | lowt `L.elem` fvals -> Right False
120− | otherwise ->
121− Left $ "Could not read " <> t <> " as Bool"
122−
123−-- | Expects text to be single character
124−--
125−-- @since 1.0.0
126−readChar :: Text -> Either Text Char
127−readChar t = case T.unpack $ T.strip t of
128− [c] -> Right c
129− _ -> Left "Should have exactly one non-blank character"
src/Text/XML/DOM/Parser/FromAttribute.hsdeleted−112
@@ -1,112 +0,0 @@
1−module Text.XML.DOM.Parser.FromAttribute
2− ( -- * FromAttribute
3− FromAttribute(..)
4− , proxyFromAttribute
5− -- * Explicit methods
6− , textFromAttribute
7− , stringFromAttribute
8− , charFromAttribute
9− , intFromAttribute
10− , integerFromAttribute
11− , doubleFromAttribute
12− , fixedFromAttribute
13− , boolFromAttribute
14− , unitFromAttribute
15− , voidFromAttribute
16− , scientificFromAttribute
17− ) where
18−
19−import Data.Fixed
20−import Data.Scientific
21−import Data.Text as T
22−import Data.Typeable
23−import Data.Void
24−import Text.XML.DOM.Parser.Content
25−
26−-- | Class of types which can be get from attribute value. Method
27−-- 'fromAttribute' is a convenient default parameter for
28−-- 'parseAttribute'
29−--
30−-- @since 2.0.0
31−class FromAttribute a where
32− fromAttribute
33− :: Text
34− -- ^ Attribute contents to parse
35− -> Either Text a
36− -- ^ Either error message or result
37−
38−proxyFromAttribute
39− :: FromAttribute a
40− => Proxy a
41− -> Text
42− -> Either Text a
43−proxyFromAttribute _ = fromAttribute
44−
45−instance FromAttribute () where
46− fromAttribute = unitFromAttribute
47−
48−unitFromAttribute :: Text -> Either Text ()
49−unitFromAttribute _ = Right ()
50−
51−instance FromAttribute Void where
52− fromAttribute = voidFromAttribute
53−
54−voidFromAttribute :: Text -> Either Text Void
55−voidFromAttribute _ = Left "Void has been parsed"
56−
57−instance FromAttribute Text where
58− fromAttribute = textFromAttribute
59−
60−textFromAttribute :: Text -> Either Text Text
61−textFromAttribute = Right
62−
63−instance FromAttribute String where
64− fromAttribute = stringFromAttribute
65−
66−stringFromAttribute :: Text -> Either Text String
67−stringFromAttribute = Right . T.unpack
68−
69−instance FromAttribute Char where
70− fromAttribute = charFromAttribute
71−
72−charFromAttribute :: Text -> Either Text Char
73−charFromAttribute = readChar
74−
75−instance FromAttribute Int where
76− fromAttribute = intFromAttribute
77−
78−intFromAttribute :: Text -> Either Text Int
79−intFromAttribute = readContent
80−
81−instance FromAttribute Integer where
82− fromAttribute = integerFromAttribute
83−
84−integerFromAttribute :: Text -> Either Text Integer
85−integerFromAttribute = readContent
86−
87−instance FromAttribute Double where
88− fromAttribute = doubleFromAttribute
89−
90−doubleFromAttribute :: Text -> Either Text Double
91−doubleFromAttribute = readContent
92−
93−instance (Typeable a, HasResolution a) => FromAttribute (Fixed a) where
94− fromAttribute = fixedFromAttribute
95−
96−fixedFromAttribute
97− :: (HasResolution a, Typeable a)
98− => Text
99− -> Either Text (Fixed a)
100−fixedFromAttribute = readContent
101−
102−instance FromAttribute Bool where
103− fromAttribute = boolFromAttribute
104−
105−boolFromAttribute :: Text -> Either Text Bool
106−boolFromAttribute = readBool
107−
108−instance FromAttribute Scientific where
109− fromAttribute = scientificFromAttribute
110−
111−scientificFromAttribute :: Text -> Either Text Scientific
112−scientificFromAttribute = readContent
src/Text/XML/DOM/Parser/Types.hs+63−145
@@ -1,19 +1,6 @@
11module Text.XML.DOM.Parser.Types
2− (-- * Element matching
3− ElemMatcher(..)
4− , emMatch
5− , emShow
6− , matchElemName
7− , elMatch
8− -- * Name matching
9− , NameMatcher(..)
10− , nmMatch
11− , nmShow
12− , matchName
13− , matchLocalName
14− , matchCILocalName
15− -- * Parser internals
16− , DomPath(..)
2+ ( -- * Parser internals
3+ DomPath
174 , ParserError(..)
185 , pePath
196 , peDetails
@@ -23,140 +10,47 @@ module Text.XML.DOM.Parser.Types
2310 , ParserData(..)
2411 , pdElements
2512 , pdPath
13+ -- * Parser type
2614 , DomParserT
2715 , DomParser
2816 , runDomParserT
2917 , runDomParser
3018 -- * Auxiliary
19+ , DomTraversable(..)
3120 , throwParserError
21+ , throwWrongFormat
3222 ) where
3323
34−import Control.Exception
35−import Control.Lens
36−import Control.Monad.Except
37−import Control.Monad.Reader
38−import Data.CaseInsensitive as CI
39−import Data.String
40−import Data.Text as T
41−import GHC.Generics (Generic)
42−import Text.XML
43−import Text.XML.Lens
44−
45−-- | Arbitrary element matcher
46−--
47−-- @since 2.0.0
48−data ElemMatcher = ElemMatcher
49− { _emMatch :: Element -> Bool
50− , _emShow :: Text
51− -- ^ Field for 'Show' instance and bulding usefull errors
52− }
53−
54−makeLenses ''ElemMatcher
55−
56−-- | Instance using instance of 'NameMatcher'
57−instance IsString ElemMatcher where
58− fromString = matchElemName . fromString
59−
60−instance Show ElemMatcher where
61− show = T.unpack . _emShow
62−
63−-- | Match element by name
64−--
65−-- @since 2.0.0
66−matchElemName :: NameMatcher -> ElemMatcher
67−matchElemName (NameMatcher matchName showName) = ElemMatcher
68− { _emMatch = views name matchName
69− , _emShow = showName
70− }
71−
72−-- | Match over elements
73−--
74−-- @since 2.0.0
75−elMatch :: ElemMatcher -> Traversal' Element Element
76−elMatch (ElemMatcher match _) = filtered match
77−
78−-- | Arbitrary name matcher. Match name any way you want, but
79−-- considered to be used as comparator with some name with some rules
80−--
81−-- @since 2.0.0
82−data NameMatcher = NameMatcher
83− { _nmMatch :: Name -> Bool
84− -- ^ Name matching function, usually should be simple comparsion
85− -- function takin in account only local name or other components
86− -- of 'Name'
87− , _nmShow :: Text
88− -- ^ Field for 'Show' instance and bulding usefull errors
89− }
90−
91−-- | Instance use 'matchCILocalName' as most general and liberal
92−-- matching strategy (while XML is often malformed).
93−--
94−-- @since 2.0.0
95−instance IsString NameMatcher where
96− fromString = matchCILocalName . T.pack
97−
98−instance Show NameMatcher where
99− show = T.unpack . _nmShow
100−
101−-- | Makes matcher which matches only local part of name igoring
102−-- namespace and prefix. Local name matching is case sensitive.
103−--
104−-- @since 2.0.0
105−matchLocalName :: Text -> NameMatcher
106−matchLocalName tname = NameMatcher
107− { _nmMatch = \n -> nameLocalName n == tname
108− , _nmShow = tname
109− }
110−
111−-- | Makes matcher which matches only local part of name igoring
112−-- namespace and prefix. Local name matching is case insensitive. This
113−-- is the most common case.
114−--
115−-- @since 2.0.0
116−matchCILocalName :: Text -> NameMatcher
117−matchCILocalName tname = NameMatcher
118− { _nmMatch = \n -> CI.mk (nameLocalName n) == CI.mk tname
119− , _nmShow = tname
120− }
121−
122−makeLenses ''NameMatcher
123−
124−-- | Makes matcher which match name by 'Eq' with given
125−--
126−-- @since 2.0.0
127−matchName :: Name -> NameMatcher
128−matchName n = NameMatcher
129− { _nmMatch = (== n)
130− , _nmShow = nameLocalName n
131− }
132−
133−-- | Path some element should be found at. Path starts from the root
134−-- element of the document. Errors are much more usefull with path.
135−newtype DomPath = DomPath
136− { unDomPath :: [Text]
137− } deriving (Eq, Ord, Show, Semigroup, Monoid)
24+import Control.Exception
25+import Control.Lens
26+import Control.Monad.Except
27+import Control.Monad.Reader
28+import Data.List.NonEmpty (NonEmpty(..))
29+import qualified Data.List.NonEmpty as NE
30+import Data.Maybe
31+import Data.Text (Text)
32+import GHC.Generics (Generic)
33+import Text.XML
34+import Text.XML.Lens
35+
36+type DomPath = [Text]
13837
13938-- | DOM parser error description.
14039data ParserError
14140 -- | Tag not found which should be.
14241 = PENotFound
14342 { _pePath :: DomPath
144− -- ^ Path of element error occured in
14543 }
14644
147− -- | Expected attribute but not found
148− --
149− -- @since 1.0.0
150− | PEAttributeNotFound
151− { _peAttributeName :: NameMatcher
152− , _pePath :: DomPath
45+ -- | Tag contents has wrong format, (could not read text to value)
46+ | PEWrongFormat
47+ { _peDetails :: Text
48+ , _pePath :: DomPath -- ^ path of element
15349 }
15450
15551 -- | Could not parse attribute
156− --
157− -- @since 1.0.0
15852 | PEAttributeWrongFormat
159− { _peAttributeName :: NameMatcher
53+ { _peAttributeName :: Text
16054 , _peDetails :: Text
16155 , _pePath :: DomPath
16256 }
@@ -166,17 +60,17 @@ data ParserError
16660 { _pePath :: DomPath
16761 }
16862
169− -- | Tag contents has wrong format, (could not read text to value)
170− | PEContentWrongFormat
171− { _peDetails :: Text
172− , _pePath :: DomPath
63+ -- | Expected attribute but not found
64+ | PEAttributeNotFound
65+ { _peAttributeName :: Text
66+ , _pePath :: DomPath
17367 }
17468
17569 -- | Some other error
17670 | PEOther
17771 { _peDetails :: Text
17872 , _pePath :: DomPath
179− } deriving (Show, Generic)
73+ } deriving (Eq, Ord, Show, Generic)
18074
18175makeLenses ''ParserError
18276
@@ -184,16 +78,15 @@ instance Exception ParserError
18478
18579newtype ParserErrors = ParserErrors
18680 { unParserErrors :: [ParserError]
187− } deriving (Show, Semigroup, Monoid, Generic)
81+ } deriving (Ord, Eq, Show, Monoid, Generic)
18882
18983makePrisms ''ParserErrors
19084
19185instance Exception ParserErrors
19286
19387
194−{- | Parser scope.
195−
196−Functor argument is usually @Identity@ or @[]@.
88+{- | Parser scope parser runs in. Functor argument is usually @Identity@ or
89+@[]@.
19790
19891If functor is @Identity@ then parser expects exactly ONE current element. This
19992is common behavior for content parsers, or parsers expecting strict XML
@@ -226,11 +119,10 @@ runDomParserT
226119 -> DomParserT Identity m a
227120 -> m (Either ParserErrors a)
228121runDomParserT doc par =
229− let
230− pd = ParserData
231− { _pdElements = doc ^. root . to pure
232− , _pdPath = DomPath [doc ^. root . name . to nameLocalName]
233− }
122+ let pd = ParserData
123+ { _pdElements = doc ^. root . to pure
124+ , _pdPath = [doc ^. root . localName]
125+ }
234126 in runExceptT $ runReaderT par pd
235127
236128runDomParser
@@ -239,10 +131,36 @@ runDomParser
239131 -> Either ParserErrors a
240132runDomParser doc par = runIdentity $ runDomParserT doc par
241133
134+-- | Class of traversable functors which may be constructed from list. Or may
135+-- not.
136+class Traversable f => DomTraversable f where
137+ -- | If method return Nothing this means we can not build traversable from
138+ -- given list. In this case combinator should fail traversing.
139+ buildDomTraversable :: [a] -> Maybe (f a)
140+
141+instance DomTraversable Identity where
142+ buildDomTraversable = fmap Identity . listToMaybe
143+
144+instance DomTraversable [] where
145+ buildDomTraversable = Just
146+
147+instance DomTraversable Maybe where
148+ buildDomTraversable = Just . listToMaybe
149+
150+instance DomTraversable NonEmpty where
151+ buildDomTraversable = NE.nonEmpty
152+
242153throwParserError
243154 :: (MonadError ParserErrors m, MonadReader (ParserData f) m)
244− => (DomPath -> ParserError)
155+ => ([Text] -> ParserError)
245156 -> m a
246157throwParserError mkerr = do
247158 path <- view pdPath
248− throwError $ ParserErrors $ [mkerr path]
159+ throwError $ ParserErrors [mkerr path]
160+
161+-- | Throw 'PEWrongFormat' as very common case
162+throwWrongFormat
163+ :: (MonadError ParserErrors m, MonadReader (ParserData f) m)
164+ => Text
165+ -> m a
166+throwWrongFormat err = throwParserError $ PEWrongFormat err
src/Text/XML/Soap.hsdeleted−46
@@ -1,46 +0,0 @@
1−module Text.XML.Soap where
2−
3−import Control.Lens
4−import Data.String
5−import Text.XML
6−import Text.XML.DOM.Parser
7−import Text.XML.Writer
8−
9−data XmlEnvelope h b = XmlEnvelope
10− { _xeHeader :: !(Maybe h)
11− , _xeBody :: !(Maybe b)
12− } deriving (Eq, Show)
13−
14−makeLenses ''XmlEnvelope
15−
16−xmlEnvelopeToDocument :: (ToXML h, ToXML b) => XmlEnvelope h b -> Document
17−xmlEnvelopeToDocument xe = doc
18− where
19− sname n = fromString $ "{http://schemas.xmlsoap.org/soap/envelope/}" <> n
20− doc =
21− Document
22− { documentPrologue = Prologue [] Nothing []
23− , documentRoot =
24− Element
25− { elementName = sname "Envelope"
26− , elementAttributes = mempty
27− , elementNodes = [NodeElement header, NodeElement body]
28− }
29− , documentEpilogue = []
30− }
31− header =
32− Element
33− { elementName = sname "Header"
34− , elementAttributes = mempty
35− , elementNodes = render . toXML $ xe ^. xeHeader
36− }
37− body =
38− Element
39− { elementName = sname "Body"
40− , elementAttributes = mempty
41− , elementNodes = render . toXML $ xe ^. xeBody
42− }
43−
44−instance (FromDom h, FromDom b) => FromDom (XmlEnvelope h b) where
45− fromDom = XmlEnvelope <$> inElemMay "Header" fromDom
46− <*> inElemMay "Body" fromDom
stack.yamlmode 120000 → 100644+6−1
@@ -1 +0,0 @@
1−ghc-963.yaml
@@ -0,0 +1,6 @@
1+packages:
2+ - .
3+extra-deps:
4+ - open-union-0.2.0.0
5+ - type-fun-0.1.1
6+resolver: lts-5.18
test/Main.hs+10−10
@@ -33,7 +33,7 @@ testStructureAttributes = do
3333 int <- parseAttribute "int" readContent
3434 bool <- parseAttribute "bool" readBool
3535 return $ TestStructure
36− { tsName = nameLocalName name
36+ { tsName = name
3737 , tsInts = [int]
3838 , tsBools = pure bool }
3939
@@ -156,7 +156,7 @@ specParser
156156 :: String -- ^ Name of spec
157157 -> Document -- ^ Document to parse
158158 -> (a -> Maybe String) -- ^ Value checker, Nothing if all ok
159− -> DomParser Identity a -- ^ Parser itself
159+ -> DomParser Identity a -- ^ Parser itself
160160 -> Spec
161161specParser name doc check parser = it name $ do
162162 result <- either throwIO return $ runDomParser doc parser
@@ -166,9 +166,9 @@ specParser name doc check parser = it name $ do
166166
167167specParserEq
168168 :: (Eq a, Show a)
169− => String -- ^ Name of spec
170− -> Document -- ^ Document to parse
171− -> a -- ^ Value parser should return
169+ => String -- ^ Name of spec
170+ -> Document -- ^ Document to parse
171+ -> a -- ^ Value parser should return
172172 -> DomParser Identity a -- ^ Parser itself
173173 -> Spec
174174specParserEq name doc a parser = specParser name doc check parser
@@ -193,7 +193,7 @@ specParserFailedPath
193193 :: (Show a)
194194 => String -- ^ Name of spec
195195 -> Document
196− -> DomPath -- ^ Expteced path in error
196+ -> [Text] -- ^ Expteced path in error
197197 -> DomParser Identity a
198198 -> Spec
199199specParserFailedPath name doc path parser = it name $ example $ do
@@ -226,7 +226,7 @@ combinationsSpec = do
226226 describe "deep" $ do
227227 let parser = inElem "a" $ inElem "b" textFromDom
228228 specParserFailedPath "docDeepMultiple2" docDeepMultiple2
229− (DomPath ["root", "a", "b"]) parser
229+ ["root", "a", "b"] parser
230230 -- should fail here because first tag "a" does not contains
231231 -- tag "b" which is expected by parser @inElem "b"@
232232
@@ -276,7 +276,7 @@ combinationsSpec = do
276276 specParserEq "docDeepMultiple1" docDeepMultiple1 "content1" parser
277277 specParserEq "docDeepMultiple2" docDeepMultiple2 "content1" parser
278278 describe "fails" $ do
279− specParserFailedPath "docSimple" docSimple (DomPath ["root", "a", "b"]) parser
279+ specParserFailedPath "docSimple" docSimple ["root", "a", "b"] parser
280280 describe "multiple elements" $ do
281281 let
282282 parser = diveElem "a" $ inElemAll "b" textFromDom
@@ -311,9 +311,9 @@ combinationsSpec = do
311311 inElem "a" $ do
312312 checkCurrentName "a"
313313 describe "fails" $ do
314− specParserFailedPath "root element" docSimple (DomPath ["toor"]) $ do
314+ specParserFailedPath "root element" docSimple ["toor"] $ do
315315 checkCurrentName "toor"
316− specParserFailedPath "inner element" docSimple (DomPath ["root", "b"]) $ do
316+ specParserFailedPath "inner element" docSimple ["root", "b"] $ do
317317 inElem "a" $ checkCurrentName "b"
318318
319319contentSpec :: Spec