git.iko.soy/typeable/xml-lens
on master help sign in
.github/workflows/haskell.ymlnew file+37
since splitsnapshots
unifiedsplit
@@ -0,0 +1,37 @@
1+name: Haskell CI
2+
3+on: [push, pull_request]
4+
5+jobs:
6+ build:
7+
8+ runs-on: ubuntu-latest
9+ strategy:
10+ matrix:
11+ ghc: [ '8.8.4', '8.10.7', '9.0.1', '9.2.2' ]
12+ steps:
13+ - uses: actions/checkout@v3
14+ - uses: haskell/actions/setup@v1
15+ with:
16+ ghc-version: ${{ matrix.ghc }}
17+ cabal-version: '3.4'
18+
19+ - name: cabal Cache
20+ uses: actions/cache@v1
21+ env:
22+ cache-name: cache-cabal
23+ with:
24+ path: ~/.cabal
25+ key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ hashFiles('**/*.cabal') }}-${{ hashFiles('**/cabal.project') }}
26+ restore-keys: |
27+ ${{ runner.os }}-build-${{ env.cache-name }}-
28+ ${{ runner.os }}-build-
29+ ${{ runner.os }}-
30+ - name: Install dependencies
31+ run: |
32+ cabal update
33+ cabal build --only-dependencies --enable-tests --enable-benchmarks all
34+ - name: Build
35+ run: cabal build --enable-tests --enable-benchmarks all
36+ - name: Run tests
37+ run: cabal test --enable-tests --enable-benchmarks all
.gitignore+1
@@ -1,3 +1,4 @@
1+dist-newstyle
1#################
2#################
2## Eclipse
3## Eclipse
3#################
4#################
CHANGELOG.mdnew file+10
@@ -0,0 +1,10 @@
1+## 0.3.1
2+
3+* Supported GHC 9
4+
5+## 0.3 (2021/01)
6+
7+* Deprecated `el`
8+* Removed `./` and `entire`
9+
10+
README.md+14−8
@@ -6,18 +6,20 @@ Lenses and traversals for [xml-conduit](http://hackage.haskell.org/package/xml-c
6Example
6Example
7========
7========
8
8
9+```haskell
9− > doc <- Text.XML.readFile def "examples/books.xml"
10+> doc <- Text.XML.readFile def "examples/books.xml"
10
11
11− > doc ^.. root . el "books" ./ el "book" . attributeIs "category" "Textbooks" ./ el "title" . text
12+> doc ^.. root . named "books" ... named "book" . attributeIs "category" "Textbooks" ... named "title" . text
12− ["Learn You a Haskell for Great Good!","Programming in Haskell","Real World Haskell"]
13+["Learn You a Haskell for Great Good!","Programming in Haskell","Real World Haskell"]
13
14
14− > lengthOf ?? doc $ root . el "books" ./ el "book"
15+> lengthOf ?? doc $ root . named "books" ... named "book"
15− 7
16+7
16
17
17− > doc ^? root . el "books" ./ attributeIs "category" "Joke" ./ el "title" . text
18+> doc ^? root . named "books" ... attributeIs "category" "Joke" ... named "title" . text
18− Just "Functional Ikamusume"
19+Just "Functional Ikamusume"
19
20
20− > doc & root . el "books" ./ el "book" ./ el "pages" . text <>~ " pages" & renderLBS def & BL.putStrLn
21+> doc & root . named "books" ... named "book" ... named "pages" . text <>~ " pages" & renderLBS def & BL.putStrLn
22+```
21
23
22```xml
24```xml
23<?xml version="1.0" encoding="ISO-8859-1"?>
25<?xml version="1.0" encoding="ISO-8859-1"?>
@@ -41,4 +43,8 @@ Example
41…
43…
42```
44```
43
45
46+Tips
47+----
44
48
49+* Use [cosmos](http://hackage.haskell.org/package/lens-4.19.2/docs/Control-Lens-Plated.html) to traverse over all children
50+* You can do or-composition of traversals with [failing](http://hackage.haskell.org/package/lens-4.19.2/docs/Control-Lens-Traversal.html#v:failing)
Text/XML/Lens.hs+73−27
@@ -1,24 +1,31 @@
1{-# LANGUAGE Rank2Types, FlexibleContexts #-}
1{-# LANGUAGE Rank2Types, FlexibleContexts #-}
2+{-# OPTIONS_GHC -fno-warn-orphans #-}
2-----------------------------------------------------------------------------
3-----------------------------------------------------------------------------
3-- |
4-- |
4-- Module : Text.XML.Lens
5-- Module : Text.XML.Lens
5−-- Copyright : (C) 2013 Fumiaki Kinoshita
6+-- Copyright : (C) 2015 Fumiaki Kinoshita
6-- License : BSD-style (see the file LICENSE)
7-- License : BSD-style (see the file LICENSE)
7--
8--
8-- Maintainer : Fumiaki Kinoshita <fumiexcel@gmail.com>
9-- Maintainer : Fumiaki Kinoshita <fumiexcel@gmail.com>
9−-- Stability : experimental
10+-- Stability : provisional
10-- Portability : non-portable
11-- Portability : non-portable
11−-- Useful traversals inspired by XPath
12+--
12----------------------------------------------------------------------------
13----------------------------------------------------------------------------
13module Text.XML.Lens (
14module Text.XML.Lens (
14 -- * Lenses, traversals for 'Element'
15 -- * Lenses, traversals for 'Element'
16+ Element(..)
15− (./)
17+ , (...)
16 -- ** Names
18 -- ** Names
17 , name
19 , name
20+ , localName
18 , el
21 , el
22+ , ell
23+ , named
19 -- ** Attributes
24 -- ** Attributes
20 , attributeIs
25 , attributeIs
21 , attributeSatisfies
26 , attributeSatisfies
27+ , attributeSatisfies'
28+ , withoutAttribute
22 , attr
29 , attr
23 , attribute
30 , attribute
24 , attrs
31 , attrs
@@ -26,23 +33,26 @@ module Text.XML.Lens (
26 , text
33 , text
27 , comment
34 , comment
28 -- ** Children
35 -- ** Children
29− , entire
30 , nodes
36 , nodes
31 -- * Prisms for 'Node'
37 -- * Prisms for 'Node'
38+ , Node(..)
32 , _Element
39 , _Element
33 , _Content
40 , _Content
34 , AsInstruction(..)
41 , AsInstruction(..)
35 , AsComment(..)
42 , AsComment(..)
36 -- * Lenses for 'Document'
43 -- * Lenses for 'Document'
44+ , Document(..)
37 , root
45 , root
38 , prologue
46 , prologue
39 , epilogue
47 , epilogue
40 , doctype
48 , doctype
41 -- * Lenses for 'Name'
49 -- * Lenses for 'Name'
50+ , Name(..)
42 , _nameLocalName
51 , _nameLocalName
43 , _nameNamespace
52 , _nameNamespace
44 , _namePrefix
53 , _namePrefix
45 -- * Lenses for 'Instruction'
54 -- * Lenses for 'Instruction'
55+ , Instruction(..)
46 , _instructionTarget
56 , _instructionTarget
47 , _instructionData
57 , _instructionData
48 ) where
58 ) where
@@ -50,41 +60,48 @@ import Text.XML
50import Control.Lens
60import Control.Lens
51import Data.Text (Text)
61import Data.Text (Text)
52import Data.Map (Map)
62import Data.Map (Map)
53−import Control.Applicative
63+import qualified Data.CaseInsensitive as CI
54−
64+import Data.Maybe (isNothing)
55−infixr 9 ./
56
65
57prologue :: Lens' Document Prologue
66prologue :: Lens' Document Prologue
58prologue f doc = fmap (\p -> doc { documentPrologue = p} ) $ f $ documentPrologue doc
67prologue f doc = fmap (\p -> doc { documentPrologue = p} ) $ f $ documentPrologue doc
68+{-# INLINE prologue #-}
59
69
60-- | The root element of the document.
70-- | The root element of the document.
61root :: Lens' Document Element
71root :: Lens' Document Element
62root f doc = fmap (\p -> doc { documentRoot = p} ) $ f $ documentRoot doc
72root f doc = fmap (\p -> doc { documentRoot = p} ) $ f $ documentRoot doc
73+{-# INLINE root #-}
63
74
64epilogue :: Lens' Document [Miscellaneous]
75epilogue :: Lens' Document [Miscellaneous]
65epilogue f doc = fmap (\p -> doc { documentEpilogue = p} ) $ f $ documentEpilogue doc
76epilogue f doc = fmap (\p -> doc { documentEpilogue = p} ) $ f $ documentEpilogue doc
77+{-# INLINE epilogue #-}
66
78
67doctype :: Lens' Prologue (Maybe Doctype)
79doctype :: Lens' Prologue (Maybe Doctype)
68doctype f doc = fmap (\p -> doc { prologueDoctype = p} ) $ f $ prologueDoctype doc
80doctype f doc = fmap (\p -> doc { prologueDoctype = p} ) $ f $ prologueDoctype doc
81+{-# INLINE doctype #-}
69
82
70class AsInstruction t where
83class AsInstruction t where
71 _Instruction :: Prism' t Instruction
84 _Instruction :: Prism' t Instruction
72
85
73_instructionTarget :: Lens' Instruction Text
86_instructionTarget :: Lens' Instruction Text
74_instructionTarget f (Instruction t d) = f t <&> \t' -> Instruction t' d
87_instructionTarget f (Instruction t d) = f t <&> \t' -> Instruction t' d
88+{-# INLINE _instructionTarget #-}
75
89
76_instructionData :: Lens' Instruction Text
90_instructionData :: Lens' Instruction Text
77_instructionData f (Instruction t d) = f d <&> \d' -> Instruction t d'
91_instructionData f (Instruction t d) = f d <&> \d' -> Instruction t d'
92+{-# INLINE _instructionData #-}
78
93
79instance AsInstruction Node where
94instance AsInstruction Node where
80 _Instruction = prism' NodeInstruction $ \s -> case s of
95 _Instruction = prism' NodeInstruction $ \s -> case s of
81 NodeInstruction e -> Just e
96 NodeInstruction e -> Just e
82 _ -> Nothing
97 _ -> Nothing
98+ {-# INLINE _Instruction #-}
83
99
84instance AsInstruction Miscellaneous where
100instance AsInstruction Miscellaneous where
85 _Instruction = prism' MiscInstruction $ \s -> case s of
101 _Instruction = prism' MiscInstruction $ \s -> case s of
86 MiscInstruction e -> Just e
102 MiscInstruction e -> Just e
87 _ -> Nothing
103 _ -> Nothing
104+ {-# INLINE _Instruction #-}
88
105
89class AsComment t where
106class AsComment t where
90 _Comment :: Prism' t Text
107 _Comment :: Prism' t Text
@@ -93,78 +110,107 @@ instance AsComment Node where
93 _Comment = prism' NodeComment $ \s -> case s of
110 _Comment = prism' NodeComment $ \s -> case s of
94 NodeComment e -> Just e
111 NodeComment e -> Just e
95 _ -> Nothing
112 _ -> Nothing
113+ {-# INLINE _Comment #-}
96
114
97instance AsComment Miscellaneous where
115instance AsComment Miscellaneous where
98 _Comment = prism' MiscComment $ \s -> case s of
116 _Comment = prism' MiscComment $ \s -> case s of
99 MiscComment e -> Just e
117 MiscComment e -> Just e
100 _ -> Nothing
118 _ -> Nothing
119+ {-# INLINE _Comment #-}
101
120
102_nameLocalName :: Lens' Name Text
121_nameLocalName :: Lens' Name Text
103−_nameLocalName f n = fmap (\x -> n { nameLocalName = x }) $ f $ nameLocalName n
122+_nameLocalName f n = f (nameLocalName n) <&> \x -> n { nameLocalName = x }
123+{-# INLINE _nameLocalName #-}
104
124
105_nameNamespace :: Lens' Name (Maybe Text)
125_nameNamespace :: Lens' Name (Maybe Text)
106−_nameNamespace f n = fmap (\x -> n { nameNamespace = x }) $ f $ nameNamespace n
126+_nameNamespace f n = f (nameNamespace n) <&> \x -> n { nameNamespace = x }
127+{-# INLINE _nameNamespace #-}
107
128
108_namePrefix :: Lens' Name (Maybe Text)
129_namePrefix :: Lens' Name (Maybe Text)
109−_namePrefix f n = fmap (\x -> n { namePrefix = x }) $ f $ namePrefix n
130+_namePrefix f n = f (namePrefix n) <&> \x -> n { namePrefix = x }
131+{-# INLINE _namePrefix #-}
110
132
111_Element :: Prism' Node Element
133_Element :: Prism' Node Element
112_Element = prism' NodeElement $ \s -> case s of
134_Element = prism' NodeElement $ \s -> case s of
113 NodeElement e -> Just e
135 NodeElement e -> Just e
114 _ -> Nothing
136 _ -> Nothing
137+{-# INLINE _Element #-}
115
138
116_Content :: Prism' Node Text
139_Content :: Prism' Node Text
117_Content = prism' NodeContent $ \s -> case s of
140_Content = prism' NodeContent $ \s -> case s of
118 NodeContent e -> Just e
141 NodeContent e -> Just e
119 _ -> Nothing
142 _ -> Nothing
143+{-# INLINE _Content #-}
120
144
121name :: Lens' Element Name
145name :: Lens' Element Name
122−name f e = fmap (\x -> e { elementName = x }) $ f $ elementName e
146+name f e = f (elementName e) <&> \x -> e { elementName = x }
147+{-# INLINE name #-}
148+
149+localName :: Lens' Element Text
150+localName = name . _nameLocalName
151+{-# INLINE localName #-}
123
152
124attrs :: Lens' Element (Map Name Text)
153attrs :: Lens' Element (Map Name Text)
125attrs f e = fmap (\x -> e { elementAttributes = x }) $ f $ elementAttributes e
154attrs f e = fmap (\x -> e { elementAttributes = x }) $ f $ elementAttributes e
155+{-# INLINE attrs #-}
126
156
127nodes :: Lens' Element [Node]
157nodes :: Lens' Element [Node]
128nodes f e = fmap (\x -> e { elementNodes = x }) $ f $ elementNodes e
158nodes f e = fmap (\x -> e { elementNodes = x }) $ f $ elementNodes e
159+{-# INLINE nodes #-}
129
160
130−attr :: Name -> IndexedTraversal' Name Element Text
161+attr :: Name -> Traversal' Element Text
131attr n = attrs . ix n
162attr n = attrs . ix n
163+{-# INLINE attr #-}
132
164
133−attribute :: Name -> IndexedLens' Name Element (Maybe Text)
165+attribute :: Name -> Lens' Element (Maybe Text)
134attribute n = attrs . at n
166attribute n = attrs . at n
167+{-# INLINE attribute #-}
168+
169+-- | Traverse elements which has the specified *local* name (case-insensitive).
170+named :: CI.CI Text -> Traversal' Element Element
171+named n f s
172+ | CI.mk (nameLocalName (elementName s)) == n = f s
173+ | otherwise = pure s
174+{-# INLINE named #-}
135
175
136−-- | Traverse itself with its all children.
176+-- | Old name for 'named'
137−entire :: Traversal' Element Element
177+ell :: Text -> Traversal' Element Element
138−entire f e@(Element _ _ ns) = com <$> f e <*> traverse (_Element (entire f)) ns where
178+ell n = named $ CI.mk n
139− com (Element n a _) ns = Element n a ns
140
179
141-- | Traverse elements which has the specified name.
180-- | Traverse elements which has the specified name.
142el :: Name -> Traversal' Element Element
181el :: Name -> Traversal' Element Element
143el n f s
182el n f s
144 | elementName s == n = f s
183 | elementName s == n = f s
145 | otherwise = pure s
184 | otherwise = pure s
185+{-# DEPRECATED el "Use named instead" #-}
146
186
147attributeSatisfies :: Name -> (Text -> Bool) -> Traversal' Element Element
187attributeSatisfies :: Name -> (Text -> Bool) -> Traversal' Element Element
148−attributeSatisfies n p = filtered (maybe False p . preview (attrs . ix n))
188+attributeSatisfies n p = attributeSatisfies' n (maybe False p)
189+{-# INLINE attributeSatisfies #-}
190+
191+attributeSatisfies' :: Name -> (Maybe Text -> Bool) -> Traversal' Element Element
192+attributeSatisfies' n p = filtered (p . preview (attrs . ix n))
193+{-# INLINE attributeSatisfies' #-}
194+
195+withoutAttribute :: Name -> Traversal' Element Element
196+withoutAttribute n = attributeSatisfies' n isNothing
197+{-# INLINE withoutAttribute #-}
149
198
150attributeIs :: Name -> Text -> Traversal' Element Element
199attributeIs :: Name -> Text -> Traversal' Element Element
151attributeIs n v = attributeSatisfies n (==v)
200attributeIs n v = attributeSatisfies n (==v)
201+{-# INLINE attributeIs #-}
152
202
153-- | Traverse all contents of the element.
203-- | Traverse all contents of the element.
154text :: Traversal' Element Text
204text :: Traversal' Element Text
155text = nodes . traverse . _Content
205text = nodes . traverse . _Content
206+{-# INLINE text #-}
156
207
157-- | Traverse all comments of the element.
208-- | Traverse all comments of the element.
158comment :: Traversal' Element Text
209comment :: Traversal' Element Text
159comment = nodes . traverse . _Comment
210comment = nodes . traverse . _Comment
211+{-# INLINE comment #-}
160
212
213+-- | 'plate' traverses over its sub-elements.
161instance Plated Element where
214instance Plated Element where
162 plate = nodes . traverse . _Element
215 plate = nodes . traverse . _Element
163−
164−-- | Combine two 'Traversal's just like XPath's slash.
165−--
166−-- @
167−-- l ./ m ≡ l . 'plate' . m
168−-- @
169−(./) :: Plated a => Traversal s t a a -> Traversal a a u v -> Traversal s t u v
170−l ./ m = l . plate . m
216+ {-# INLINE plate #-}
cabal.projectnew file+1
@@ -0,0 +1 @@
1+packages: ./
stack.yamlnew file+4
@@ -0,0 +1,4 @@
1+resolver: lts-21.3
2+
3+packages:
4+- .
xml-lens.cabal+17−9
@@ -1,16 +1,20 @@
1+cabal-version: 2.4
1name: xml-lens
2name: xml-lens
2−version: 0.1.1
3+version: 0.3.1
3−synopsis: Lenses, traversals, prisms for xml-conduit
4+synopsis: Lenses, traversals, and prisms for xml-conduit
4−description:
5+description: This package provides DOM selectors based on lenses and prisms. See README.md for examples
5homepage: https://github.com/fumieval/xml-lens
6homepage: https://github.com/fumieval/xml-lens
7+bug-reports: https://github.com/fumieval/xml-lens/issues
6−license: BSD3
8+license: BSD-3-Clause
7license-file: LICENSE
9license-file: LICENSE
8author: Fumiaki Kinoshita
10author: Fumiaki Kinoshita
9maintainer: Fumiaki Kinoshita <fumiexcel@gmail.com>
11maintainer: Fumiaki Kinoshita <fumiexcel@gmail.com>
10−copyright: Copyright (C) 2013 Fumiaki Kinoshita
12+copyright: Copyright (C) 2021 Fumiaki Kinoshita
11−category: Control
13+category: XML
12build-type: Simple
14build-type: Simple
13−cabal-version: >=1.10
15+extra-source-files:
16+ README.md
17+ CHANGELOG.md
14
18
15source-repository head
19source-repository head
16 type: git
20 type: git
@@ -20,5 +24,9 @@ library
20 default-language: Haskell2010
24 default-language: Haskell2010
21 ghc-options: -Wall
25 ghc-options: -Wall
22 exposed-modules: Text.XML.Lens
26 exposed-modules: Text.XML.Lens
23− -- other-modules:
24− build-depends: base ==4.*, lens >= 3.8 && < 4.2, containers >= 0.4.0 && < 0.6, text == 0.11.*, xml-conduit >= 1.1
27+ build-depends: base ==4.*
28+ , lens >= 4.0 && < 5.4
29+ , containers >= 0.4.0 && < 0.7
30+ , text >= 0.7 && < 1.3 || >= 2 && < 2.2
31+ , xml-conduit >= 1.1 && < 1.10
32+ , case-insensitive