git.iko.soy/typeable/dom-parser
on master help sign in
Upd: refactoring done
commitb5da244bebf101cba9760637198ef1df274b0036 authorAlexey Uimanov <s9gf4ult@gmail.com> dateWed 4 Jan 2017, 03:37 (10 years ago) parent7b06cfc
4 files changed+155−155
src/Text/XML/DOM/Parser/Combinators.hs+3−120 src/Text/XML/DOM/Parser/Common.hs+151−1 src/Text/XML/DOM/Parser/FromDom.hs−2 src/Text/XML/DOM/Parser/Types.hs+1−32
src/Text/XML/DOM/Parser/Combinators.hs+3−120
@@ -15,27 +15,13 @@ 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− -- * Parsing attributes
28− , parseAttribute
29− , parseAttributeMaybe
3018 ) where
3119
3220import Control.Lens
33−import Control.Monad.Except
3421import Control.Monad.Reader
3522import Data.Foldable as F
3623import Data.List as L
3724import Data.List.NonEmpty (NonEmpty(..))
38−import Data.Map.Strict as M
3925import Data.Monoid
4026import Data.Text as T
4127import Data.Traversable
@@ -68,7 +54,7 @@ traverseElems trav parser = do
6854
6955-- | Takes function filtering
7056inFilteredTrav
71− :: (Monad m, Foldable g, DomTraversable f)
57+ :: (Monad m, Foldable g, Buildable f)
7258 => ([Element] -> (DomPath, [Element]))
7359 -- ^ Function returning some filtered elements with path suffixes which will
7460 -- be appended to parser's state
@@ -78,12 +64,12 @@ inFilteredTrav deeper = traverseElems trav
7864 where
7965 trav e = do
8066 let (path, elems) = deeper e
81− case buildDomTraversable elems of
67+ case build elems of
8268 Nothing -> throwParserError $ PENotFound . (<> path)
8369 Just tr -> return $ fmap (path,) tr
8470
8571inElemTrav
86− :: (Monad m, Foldable g, DomTraversable f)
72+ :: (Monad m, Foldable g, Buildable f)
8773 => NameMatcher -- ^ Name of tag to traverse in
8874 -> DomParserT Identity m a
8975 -> DomParserT g m (f a)
@@ -205,106 +191,3 @@ ignoreBlank = ignoreElem test
205191 in if | not $ L.null elems -> False
206192 | T.null $ T.strip cont -> True
207193 | otherwise -> False
208−
209−-- | Returns name of current element.
210−--
211−-- @since 1.0.0
212−getCurrentName :: (Monad m) => DomParserT Identity m Name
213−getCurrentName = view $ pdElements . to runIdentity . name
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− => NameMatcher
221− -> DomParserT Identity m ()
222−checkCurrentName n = do
223− cn <- getCurrentName
224− unless ((n ^. nmMatch) cn) $ do
225− p <- view pdPath
226− let pinit = if L.null (unDomPath p) then [] else L.init $ unDomPath p
227− throwError $ ParserErrors [PENotFound $ DomPath $ pinit ++ [_nmShow 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 $ L.null els -> Nothing
244− | L.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−-- | Retuns map of attributes of current element
262−--
263−-- @since 1.0.0
264−getCurrentAttributes
265− :: (Monad m)
266− => DomParserT Identity m (M.Map Name Text)
267−getCurrentAttributes = view $ pdElements . to runIdentity . attrs
268−
269−-- | Returns element with given name or 'Nothing'
270−--
271−-- @since 1.0.0
272−getCurrentAttribute
273− :: (Monad m)
274− => NameMatcher
275− -> DomParserT Identity m (Maybe Text)
276−getCurrentAttribute attrName
277− = preview $ pdElements . to runIdentity . attrs . to M.toList
278− . traversed . filtered (views _1 (attrName ^. nmMatch)) . _2
279−
280−-- | Parses attribute with given name, throws error if attribute is not found.
281−--
282−-- @since 1.0.0
283−parseAttribute
284− :: (Monad m)
285− => NameMatcher
286− -- ^ Attribute name
287− -> (Text -> Either Text a)
288− -- ^ Attribute content parser
289− -> DomParserT Identity m a
290−parseAttribute attrName parser =
291− parseAttributeMaybe attrName parser >>= \case
292− Nothing -> throwParserError $ PEAttributeNotFound attrName
293− Just a -> return a
294−
295−-- | Parses attribute with given name.
296−--
297−-- @since 1.0.0
298−parseAttributeMaybe
299− :: (Monad m)
300− => NameMatcher
301− -- ^ Attribute name
302− -> (Text -> Either Text a)
303− -- ^ Attribute content parser
304− -> DomParserT Identity m (Maybe a)
305−parseAttributeMaybe attrName parser =
306− getCurrentAttribute attrName >>= \case
307− Nothing -> return Nothing
308− Just aval -> case parser aval of
309− Left err -> throwParserError $ PEAttributeWrongFormat attrName err
310− Right a -> return $ Just a
src/Text/XML/DOM/Parser/Common.hs+151−1
@@ -1,10 +1,58 @@
1−module Text.XML.DOM.Parser.Common where
1+module Text.XML.DOM.Parser.Common
2+ ( -- * Buildable
3+ Buildable(..)
4+ -- * Content readers
5+ , readBool
6+ , readChar
7+ -- ** Reader constructors
8+ , maybeReadContent
9+ , readContent
10+ -- * Getting current element's properties
11+ , getCurrentName
12+ , getCurrentContent
13+ , getCurrentAttributes
14+ , getCurrentAttribute
15+ , checkCurrentName
16+ -- * Parsing element's content
17+ , parseContent
18+ -- * Parsing attributes
19+ , parseAttribute
20+ , parseAttributeMaybe
21+ ) where
222
23+import Control.Lens
24+import Control.Monad
25+import Control.Monad.Except
26+import Data.List as L
27+import Data.List.NonEmpty as NE
28+import Data.Map.Strict as M
29+import Data.Maybe
330import Data.Monoid
431import Data.Text as T
532import Data.Typeable
633import Text.Read
34+import Text.XML.DOM.Parser.Types
35+import Text.XML.Lens
736
37+-- | Class of traversable functors which may be constructed from list. Or may
38+-- not.
39+class Traversable f => Buildable f where
40+ -- | If method return Nothing this means we can not build
41+ -- traversable from given list. In this case 'inFilteredTrav' should
42+ -- fail traversing.
43+ build :: [a] -> Maybe (f a)
44+
45+instance Buildable Identity where
46+ build = fmap Identity . listToMaybe
47+
48+instance Buildable [] where
49+ build = Just
50+
51+instance Buildable Maybe where
52+ build = Just . listToMaybe
53+
54+instance Buildable NonEmpty where
55+ build = NE.nonEmpty
856
957-- | @since 1.0.0
1058readBool :: Text -> Either Text Bool
@@ -52,3 +100,105 @@ readContent
52100 => Text
53101 -> Either Text a
54102readContent = maybeReadContent $ readMaybe . T.unpack . T.strip
103+
104+-- | Returns name of current element.
105+--
106+-- @since 1.0.0
107+getCurrentName :: (Monad m) => DomParserT Identity m Name
108+getCurrentName = view $ pdElements . to runIdentity . name
109+
110+-- | If name of current tag differs from first argument throws 'PENotFound' with
111+-- tag name replaced in last path's segment. Useful for checking root
112+-- document's element name.
113+checkCurrentName
114+ :: (Monad m)
115+ => NameMatcher
116+ -> DomParserT Identity m ()
117+checkCurrentName n = do
118+ cn <- getCurrentName
119+ unless ((n ^. nmMatch) cn) $ do
120+ p <- view pdPath
121+ let pinit = if L.null (unDomPath p) then [] else L.init $ unDomPath p
122+ throwError $ ParserErrors [PENotFound $ DomPath $ pinit ++ [_nmShow n]]
123+ return ()
124+
125+-- | Get current content. If current element contains no content or
126+-- have inner elements then Nothing returned
127+--
128+-- @since 1.0.0
129+getCurrentContent :: (Monad m) => DomParserT Identity m (Maybe Text)
130+getCurrentContent = do
131+ nds <- view $ pdElements . to runIdentity . nodes
132+ let
133+ els :: [Element]
134+ els = nds ^.. folded . _Element
135+ conts :: [Text]
136+ conts = nds ^.. folded . _Content
137+ return $ if
138+ | not $ L.null els -> Nothing
139+ | L.null conts -> Nothing
140+ | otherwise -> Just $ mconcat conts
141+
142+-- | Parses content inside current tag. It expects current element set
143+-- consists of exactly ONE element.
144+parseContent
145+ :: (Monad m)
146+ => (Text -> Either Text a)
147+ -- ^ Content parser, return error msg if value is not parsed
148+ -> DomParserT Identity m a
149+parseContent parse = getCurrentContent >>= \case
150+ Nothing -> throwParserError PEContentNotFound
151+ Just c -> case parse c of
152+ Left e -> throwParserError $ PEWrongFormat e
153+ Right a -> return a
154+
155+-- | Retuns map of attributes of current element
156+--
157+-- @since 1.0.0
158+getCurrentAttributes
159+ :: (Monad m)
160+ => DomParserT Identity m (M.Map Name Text)
161+getCurrentAttributes = view $ pdElements . to runIdentity . attrs
162+
163+-- | Returns element with given name or 'Nothing'
164+--
165+-- @since 1.0.0
166+getCurrentAttribute
167+ :: (Monad m)
168+ => NameMatcher
169+ -> DomParserT Identity m (Maybe Text)
170+getCurrentAttribute attrName
171+ = preview $ pdElements . to runIdentity . attrs . to M.toList
172+ . traversed . filtered (views _1 (attrName ^. nmMatch)) . _2
173+
174+-- | Parses attribute with given name, throws error if attribute is not found.
175+--
176+-- @since 1.0.0
177+parseAttribute
178+ :: (Monad m)
179+ => NameMatcher
180+ -- ^ Attribute name
181+ -> (Text -> Either Text a)
182+ -- ^ Attribute content parser
183+ -> DomParserT Identity m a
184+parseAttribute attrName parser =
185+ parseAttributeMaybe attrName parser >>= \case
186+ Nothing -> throwParserError $ PEAttributeNotFound attrName
187+ Just a -> return a
188+
189+-- | Parses attribute with given name.
190+--
191+-- @since 1.0.0
192+parseAttributeMaybe
193+ :: (Monad m)
194+ => NameMatcher
195+ -- ^ Attribute name
196+ -> (Text -> Either Text a)
197+ -- ^ Attribute content parser
198+ -> DomParserT Identity m (Maybe a)
199+parseAttributeMaybe attrName parser =
200+ getCurrentAttribute attrName >>= \case
201+ Nothing -> return Nothing
202+ Just aval -> case parser aval of
203+ Left err -> throwParserError $ PEAttributeWrongFormat attrName err
204+ Right a -> return $ Just a
src/Text/XML/DOM/Parser/FromDom.hs−2
@@ -23,14 +23,12 @@ module Text.XML.DOM.Parser.FromDom
2323import Control.Applicative
2424import Control.Lens
2525import Data.Fixed
26−import Data.Monoid
2726import Data.OpenUnion
2827import Data.Scientific
2928import Data.Text as T hiding (empty)
3029import Data.Typeable
3130import Data.Void
3231import Text.XML
33−import Text.XML.DOM.Parser.Combinators
3432import Text.XML.DOM.Parser.Common
3533import Text.XML.DOM.Parser.Types
3634import TypeFun.Data.List hiding (Union)
src/Text/XML/DOM/Parser/Types.hs+1−32
@@ -1,5 +1,5 @@
11module Text.XML.DOM.Parser.Types
2− ( -- * Local name
2+ ( -- * Name matching
33 NameMatcher(..)
44 , nmMatch
55 , nmShow
@@ -18,15 +18,12 @@ module Text.XML.DOM.Parser.Types
1818 , ParserData(..)
1919 , pdElements
2020 , pdPath
21− -- * Parser type
2221 , DomParserT
2322 , DomParser
2423 , runDomParserT
2524 , runDomParser
2625 -- * Auxiliary
27− , DomTraversable(..)
2826 , throwParserError
29− , throwWrongFormat
3027 ) where
3128
3229import Control.Exception
@@ -34,8 +31,6 @@ import Control.Lens
3431import Control.Monad.Except
3532import Control.Monad.Reader
3633import Data.CaseInsensitive as CI
37−import Data.List.NonEmpty as NE
38−import Data.Maybe
3934import Data.String
4035import Data.Text as T
4136import GHC.Generics (Generic)
@@ -184,25 +179,6 @@ runDomParser
184179 -> Either ParserErrors a
185180runDomParser doc par = runIdentity $ runDomParserT doc par
186181
187−-- | Class of traversable functors which may be constructed from list. Or may
188−-- not.
189−class Traversable f => DomTraversable f where
190− -- | If method return Nothing this means we can not build traversable from
191− -- given list. In this case combinator should fail traversing.
192− buildDomTraversable :: [a] -> Maybe (f a)
193−
194−instance DomTraversable Identity where
195− buildDomTraversable = fmap Identity . listToMaybe
196−
197−instance DomTraversable [] where
198− buildDomTraversable = Just
199−
200−instance DomTraversable Maybe where
201− buildDomTraversable = Just . listToMaybe
202−
203−instance DomTraversable NonEmpty where
204− buildDomTraversable = NE.nonEmpty
205−
206182throwParserError
207183 :: (MonadError ParserErrors m, MonadReader (ParserData f) m)
208184 => (DomPath -> ParserError)
@@ -210,10 +186,3 @@ throwParserError
210186throwParserError mkerr = do
211187 path <- view pdPath
212188 throwError $ ParserErrors $ [mkerr path]
213−
214−-- | Throw 'PEWrongFormat' as very common case
215−throwWrongFormat
216− :: (MonadError ParserErrors m, MonadReader (ParserData f) m)
217− => Text
218− -> m a
219−throwWrongFormat err = throwParserError $ PEWrongFormat err