27 |
27 |
|
28 |
28 |
module Ganeti.Qlang
|
29 |
29 |
( Filter(..)
|
|
30 |
, FilterField
|
30 |
31 |
, FilterValue(..)
|
31 |
32 |
, Fields
|
32 |
33 |
, Query(..)
|
... | ... | |
46 |
47 |
) where
|
47 |
48 |
|
48 |
49 |
import Control.Applicative
|
|
50 |
import Data.Foldable
|
|
51 |
import Data.Traversable (Traversable, traverse, fmapDefault, foldMapDefault)
|
49 |
52 |
import Data.Ratio (numerator, denominator)
|
50 |
53 |
import Text.JSON.Pretty (pp_value)
|
51 |
54 |
import Text.JSON.Types
|
... | ... | |
106 |
109 |
-- | List of requested fields.
|
107 |
110 |
type Fields = [ String ]
|
108 |
111 |
|
109 |
|
-- | Query2 filter expression.
|
110 |
|
data Filter
|
111 |
|
= EmptyFilter -- ^ No filter at all
|
112 |
|
| AndFilter [ Filter ] -- ^ & [<expression>, ...]
|
113 |
|
| OrFilter [ Filter ] -- ^ | [<expression>, ...]
|
114 |
|
| NotFilter Filter -- ^ ! <expression>
|
115 |
|
| TrueFilter FilterField -- ^ ? <field>
|
116 |
|
| EQFilter FilterField FilterValue -- ^ (=|!=) <field> <value>
|
117 |
|
| LTFilter FilterField FilterValue -- ^ < <field> <value>
|
118 |
|
| GTFilter FilterField FilterValue -- ^ > <field> <value>
|
119 |
|
| LEFilter FilterField FilterValue -- ^ <= <field> <value>
|
120 |
|
| GEFilter FilterField FilterValue -- ^ >= <field> <value>
|
121 |
|
| RegexpFilter FilterField FilterRegexp -- ^ =~ <field> <regexp>
|
122 |
|
| ContainsFilter FilterField FilterValue -- ^ =[] <list-field> <value>
|
|
112 |
-- | Query2 filter expression. It's a parameteric type since we can
|
|
113 |
-- filter different \"things\"; e.g. field names, or actual field
|
|
114 |
-- getters, etc.
|
|
115 |
data Filter a
|
|
116 |
= EmptyFilter -- ^ No filter at all
|
|
117 |
| AndFilter [ Filter a ] -- ^ & [<expression>, ...]
|
|
118 |
| OrFilter [ Filter a ] -- ^ | [<expression>, ...]
|
|
119 |
| NotFilter (Filter a) -- ^ ! <expression>
|
|
120 |
| TrueFilter a -- ^ ? <field>
|
|
121 |
| EQFilter a FilterValue -- ^ (=|!=) <field> <value>
|
|
122 |
| LTFilter a FilterValue -- ^ < <field> <value>
|
|
123 |
| GTFilter a FilterValue -- ^ > <field> <value>
|
|
124 |
| LEFilter a FilterValue -- ^ <= <field> <value>
|
|
125 |
| GEFilter a FilterValue -- ^ >= <field> <value>
|
|
126 |
| RegexpFilter a FilterRegexp -- ^ =~ <field> <regexp>
|
|
127 |
| ContainsFilter a FilterValue -- ^ =[] <list-field> <value>
|
123 |
128 |
deriving (Show, Read, Eq)
|
124 |
129 |
|
125 |
130 |
-- | Serialiser for the 'Filter' data type.
|
126 |
|
showFilter :: Filter -> JSValue
|
|
131 |
showFilter :: (JSON a) => Filter a -> JSValue
|
127 |
132 |
showFilter (EmptyFilter) = JSNull
|
128 |
133 |
showFilter (AndFilter exprs) =
|
129 |
134 |
JSArray $ (showJSON C.qlangOpAnd):(map showJSON exprs)
|
... | ... | |
149 |
154 |
JSArray [showJSON C.qlangOpContains, showJSON field, showJSON value]
|
150 |
155 |
|
151 |
156 |
-- | Deserializer for the 'Filter' data type.
|
152 |
|
readFilter :: JSValue -> Result Filter
|
|
157 |
readFilter :: (JSON a) => JSValue -> Result (Filter a)
|
153 |
158 |
readFilter JSNull = Ok EmptyFilter
|
154 |
159 |
readFilter (JSArray (JSString op:args)) =
|
155 |
160 |
readFilterArray (fromJSString op) args
|
... | ... | |
160 |
165 |
-- | Helper to deserialise an array corresponding to a single filter
|
161 |
166 |
-- and return the built filter. Note this looks generic but is (at
|
162 |
167 |
-- least currently) only used for the NotFilter.
|
163 |
|
readFilterArg :: (Filter -> Filter) -- ^ Constructor
|
164 |
|
-> [JSValue] -- ^ Single argument
|
165 |
|
-> Result Filter
|
|
168 |
readFilterArg :: (JSON a) =>
|
|
169 |
(Filter a -> Filter a) -- ^ Constructor
|
|
170 |
-> [JSValue] -- ^ Single argument
|
|
171 |
-> Result (Filter a)
|
166 |
172 |
readFilterArg constr [flt] = constr <$> readJSON flt
|
167 |
173 |
readFilterArg _ v = Error $ "Cannot deserialise field, expected [filter]\
|
168 |
174 |
\ but got " ++ show (pp_value (showJSON v))
|
169 |
175 |
|
170 |
176 |
-- | Helper to deserialise an array corresponding to a single field
|
171 |
177 |
-- and return the built filter.
|
172 |
|
readFilterField :: (FilterField -> Filter) -- ^ Constructor
|
173 |
|
-> [JSValue] -- ^ Single argument
|
174 |
|
-> Result Filter
|
|
178 |
readFilterField :: (JSON a) =>
|
|
179 |
(a -> Filter a) -- ^ Constructor
|
|
180 |
-> [JSValue] -- ^ Single argument
|
|
181 |
-> Result (Filter a)
|
175 |
182 |
readFilterField constr [field] = constr <$> readJSON field
|
176 |
183 |
readFilterField _ v = Error $ "Cannot deserialise field, expected [fieldname]\
|
177 |
184 |
\ but got " ++ show (pp_value (showJSON v))
|
178 |
185 |
|
179 |
186 |
-- | Helper to deserialise an array corresponding to a field and
|
180 |
187 |
-- value, returning the built filter.
|
181 |
|
readFilterFieldValue :: (JSON a) =>
|
182 |
|
(FilterField -> a -> Filter) -- ^ Constructor
|
183 |
|
-> [JSValue] -- ^ Arguments array
|
184 |
|
-> Result Filter
|
|
188 |
readFilterFieldValue :: (JSON a, JSON b) =>
|
|
189 |
(a -> b -> Filter a) -- ^ Constructor
|
|
190 |
-> [JSValue] -- ^ Arguments array
|
|
191 |
-> Result (Filter a)
|
185 |
192 |
readFilterFieldValue constr [field, value] =
|
186 |
193 |
constr <$> readJSON field <*> readJSON value
|
187 |
194 |
readFilterFieldValue _ v =
|
... | ... | |
189 |
196 |
\ but got " ++ show (pp_value (showJSON v))
|
190 |
197 |
|
191 |
198 |
-- | Inner deserialiser for 'Filter'.
|
192 |
|
readFilterArray :: String -> [JSValue] -> Result Filter
|
|
199 |
readFilterArray :: (JSON a) => String -> [JSValue] -> Result (Filter a)
|
193 |
200 |
readFilterArray op args
|
194 |
201 |
| op == C.qlangOpAnd = AndFilter <$> mapM readJSON args
|
195 |
202 |
| op == C.qlangOpOr = OrFilter <$> mapM readJSON args
|
... | ... | |
204 |
211 |
| op == C.qlangOpContains = readFilterFieldValue ContainsFilter args
|
205 |
212 |
| otherwise = Error $ "Unknown filter operand '" ++ op ++ "'"
|
206 |
213 |
|
207 |
|
instance JSON Filter where
|
|
214 |
instance (JSON a) => JSON (Filter a) where
|
208 |
215 |
showJSON = showFilter
|
209 |
216 |
readJSON = readFilter
|
210 |
217 |
|
|
218 |
-- Traversable implementation for 'Filter'.
|
|
219 |
traverseFlt :: (Applicative f) => (a -> f b) -> Filter a -> f (Filter b)
|
|
220 |
traverseFlt _ EmptyFilter = pure EmptyFilter
|
|
221 |
traverseFlt f (AndFilter flts) = AndFilter <$> (traverse (traverseFlt f) flts)
|
|
222 |
traverseFlt f (OrFilter flts) = OrFilter <$> (traverse (traverseFlt f) flts)
|
|
223 |
traverseFlt f (NotFilter flt) = NotFilter <$> (traverseFlt f flt)
|
|
224 |
traverseFlt f (TrueFilter a) = TrueFilter <$> f a
|
|
225 |
traverseFlt f (EQFilter a fval) = EQFilter <$> f a <*> pure fval
|
|
226 |
traverseFlt f (LTFilter a fval) = LTFilter <$> f a <*> pure fval
|
|
227 |
traverseFlt f (GTFilter a fval) = GTFilter <$> f a <*> pure fval
|
|
228 |
traverseFlt f (LEFilter a fval) = LEFilter <$> f a <*> pure fval
|
|
229 |
traverseFlt f (GEFilter a fval) = GEFilter <$> f a <*> pure fval
|
|
230 |
traverseFlt f (RegexpFilter a re) = RegexpFilter <$> f a <*> pure re
|
|
231 |
traverseFlt f (ContainsFilter a fval) = ContainsFilter <$> f a <*> pure fval
|
|
232 |
|
|
233 |
instance Traversable Filter where
|
|
234 |
traverse = traverseFlt
|
|
235 |
|
|
236 |
instance Functor Filter where
|
|
237 |
fmap = fmapDefault
|
|
238 |
|
|
239 |
instance Foldable Filter where
|
|
240 |
foldMap = foldMapDefault
|
|
241 |
|
211 |
242 |
-- | Field name to filter on.
|
212 |
243 |
type FilterField = String
|
213 |
244 |
|
... | ... | |
281 |
312 |
-- * Main Qlang queries and responses.
|
282 |
313 |
|
283 |
314 |
-- | Query2 query.
|
284 |
|
data Query = Query ItemType Fields Filter
|
|
315 |
data Query = Query ItemType Fields (Filter FilterField)
|
285 |
316 |
|
286 |
317 |
-- | Query2 result.
|
287 |
318 |
$(buildObject "QueryResult" "qres"
|