Revision e8a25d62 htools/Ganeti/Query2.hs
b/htools/Ganeti/Query2.hs | ||
---|---|---|
27 | 27 |
|
28 | 28 |
module Ganeti.Query2 |
29 | 29 |
( Filter(..) |
30 |
, FilterValue(..) |
|
30 | 31 |
, Query(..) |
31 | 32 |
, QueryResult(..) |
32 | 33 |
, QueryFields(..) |
... | ... | |
35 | 36 |
, ResultEntry(..) |
36 | 37 |
) where |
37 | 38 |
|
38 |
|
|
39 |
import Control.Applicative |
|
40 |
import Data.Ratio (numerator, denominator) |
|
41 |
import Text.JSON.Pretty (pp_value) |
|
39 | 42 |
import Text.JSON.Types |
40 | 43 |
import Text.JSON |
41 | 44 |
|
... | ... | |
101 | 104 |
|
102 | 105 |
-- | Query2 filter expression. |
103 | 106 |
data Filter |
104 |
= AndFilter [ Filter ] -- ^ & [<expression>, ...] |
|
105 |
| OrFilter [ Filter ] -- ^ | [<expression>, ...] |
|
106 |
| NotFilter Filter -- ^ ! <expression> |
|
107 |
| TrueFilter FilterField -- ^ ? <field> |
|
108 |
| EqualFilter FilterField FilterValue -- ^ (=|!=) <field> <value> |
|
109 |
| LessThanFilter FilterField FilterValue -- ^ < <field> <value> |
|
110 |
| GreaterThanFilter FilterField FilterValue -- ^ > <field> <value> |
|
111 |
| LEThanFilter FilterField FilterValue -- ^ <= <field> <value> |
|
112 |
| GEThanFilter FilterField FilterValue -- ^ >= <field> <value> |
|
113 |
| RegexpFilter FilterField FilterRegexp -- ^ =~ <field> <regexp> |
|
114 |
| ContainsFilter FilterField FilterValue -- ^ =[] <list-field> <value> |
|
107 |
= EmptyFilter -- ^ No filter at all |
|
108 |
| AndFilter [ Filter ] -- ^ & [<expression>, ...] |
|
109 |
| OrFilter [ Filter ] -- ^ | [<expression>, ...] |
|
110 |
| NotFilter Filter -- ^ ! <expression> |
|
111 |
| TrueFilter FilterField -- ^ ? <field> |
|
112 |
| EQFilter FilterField FilterValue -- ^ (=|!=) <field> <value> |
|
113 |
| LTFilter FilterField FilterValue -- ^ < <field> <value> |
|
114 |
| GTFilter FilterField FilterValue -- ^ > <field> <value> |
|
115 |
| LEFilter FilterField FilterValue -- ^ <= <field> <value> |
|
116 |
| GEFilter FilterField FilterValue -- ^ >= <field> <value> |
|
117 |
| RegexpFilter FilterField FilterRegexp -- ^ =~ <field> <regexp> |
|
118 |
| ContainsFilter FilterField FilterValue -- ^ =[] <list-field> <value> |
|
119 |
deriving (Show, Read, Eq) |
|
120 |
|
|
121 |
-- | Serialiser for the 'Filter' data type. |
|
122 |
showFilter :: Filter -> JSValue |
|
123 |
showFilter (EmptyFilter) = JSNull |
|
124 |
showFilter (AndFilter exprs) = |
|
125 |
JSArray $ (showJSON C.qlangOpAnd):(map showJSON exprs) |
|
126 |
showFilter (OrFilter exprs) = |
|
127 |
JSArray $ (showJSON C.qlangOpOr):(map showJSON exprs) |
|
128 |
showFilter (NotFilter flt) = |
|
129 |
JSArray [showJSON C.qlangOpNot, showJSON flt] |
|
130 |
showFilter (TrueFilter field) = |
|
131 |
JSArray [showJSON C.qlangOpTrue, showJSON field] |
|
132 |
showFilter (EQFilter field value) = |
|
133 |
JSArray [showJSON C.qlangOpEqual, showJSON field, showJSON value] |
|
134 |
showFilter (LTFilter field value) = |
|
135 |
JSArray [showJSON C.qlangOpLt, showJSON field, showJSON value] |
|
136 |
showFilter (GTFilter field value) = |
|
137 |
JSArray [showJSON C.qlangOpGt, showJSON field, showJSON value] |
|
138 |
showFilter (LEFilter field value) = |
|
139 |
JSArray [showJSON C.qlangOpLe, showJSON field, showJSON value] |
|
140 |
showFilter (GEFilter field value) = |
|
141 |
JSArray [showJSON C.qlangOpGe, showJSON field, showJSON value] |
|
142 |
showFilter (RegexpFilter field regexp) = |
|
143 |
JSArray [showJSON C.qlangOpRegexp, showJSON field, showJSON regexp] |
|
144 |
showFilter (ContainsFilter field value) = |
|
145 |
JSArray [showJSON C.qlangOpContains, showJSON field, showJSON value] |
|
146 |
|
|
147 |
-- | Deserializer for the 'Filter' data type. |
|
148 |
readFilter :: JSValue -> Result Filter |
|
149 |
readFilter JSNull = Ok EmptyFilter |
|
150 |
readFilter (JSArray (JSString op:args)) = |
|
151 |
readFilterArray (fromJSString op) args |
|
152 |
readFilter v = |
|
153 |
Error $ "Cannot deserialise filter: expected array [string, args], got " ++ |
|
154 |
show (pp_value v) |
|
155 |
|
|
156 |
-- | Helper to deserialise an array corresponding to a single filter |
|
157 |
-- and return the built filter. Note this looks generic but is (at |
|
158 |
-- least currently) only used for the NotFilter. |
|
159 |
readFilterArg :: (Filter -> Filter) -- ^ Constructor |
|
160 |
-> [JSValue] -- ^ Single argument |
|
161 |
-> Result Filter |
|
162 |
readFilterArg constr [flt] = constr <$> readJSON flt |
|
163 |
readFilterArg _ v = Error $ "Cannot deserialise field, expected [filter]\ |
|
164 |
\ but got " ++ show (pp_value (showJSON v)) |
|
165 |
|
|
166 |
-- | Helper to deserialise an array corresponding to a single field |
|
167 |
-- and return the built filter. |
|
168 |
readFilterField :: (FilterField -> Filter) -- ^ Constructor |
|
169 |
-> [JSValue] -- ^ Single argument |
|
170 |
-> Result Filter |
|
171 |
readFilterField constr [field] = constr <$> readJSON field |
|
172 |
readFilterField _ v = Error $ "Cannot deserialise field, expected [fieldname]\ |
|
173 |
\ but got " ++ show (pp_value (showJSON v)) |
|
174 |
|
|
175 |
-- | Helper to deserialise an array corresponding to a field and |
|
176 |
-- value, returning the built filter. |
|
177 |
readFilterFieldValue :: (JSON a) => |
|
178 |
(FilterField -> a -> Filter) -- ^ Constructor |
|
179 |
-> [JSValue] -- ^ Arguments array |
|
180 |
-> Result Filter |
|
181 |
readFilterFieldValue constr [field, value] = |
|
182 |
constr <$> readJSON field <*> readJSON value |
|
183 |
readFilterFieldValue _ v = |
|
184 |
Error $ "Cannot deserialise field/value pair, expected [fieldname, value]\ |
|
185 |
\ but got " ++ show (pp_value (showJSON v)) |
|
186 |
|
|
187 |
-- | Inner deserialiser for 'Filter'. |
|
188 |
readFilterArray :: String -> [JSValue] -> Result Filter |
|
189 |
readFilterArray op args |
|
190 |
| op == C.qlangOpAnd = AndFilter <$> mapM readJSON args |
|
191 |
| op == C.qlangOpOr = OrFilter <$> mapM readJSON args |
|
192 |
| op == C.qlangOpNot = readFilterArg NotFilter args |
|
193 |
| op == C.qlangOpTrue = readFilterField TrueFilter args |
|
194 |
| op == C.qlangOpEqual = readFilterFieldValue EQFilter args |
|
195 |
| op == C.qlangOpLt = readFilterFieldValue LTFilter args |
|
196 |
| op == C.qlangOpGt = readFilterFieldValue GTFilter args |
|
197 |
| op == C.qlangOpLe = readFilterFieldValue LEFilter args |
|
198 |
| op == C.qlangOpGe = readFilterFieldValue GEFilter args |
|
199 |
| op == C.qlangOpRegexp = readFilterFieldValue RegexpFilter args |
|
200 |
| op == C.qlangOpContains = readFilterFieldValue ContainsFilter args |
|
201 |
| otherwise = Error $ "Unknown filter operand '" ++ op ++ "'" |
|
202 |
|
|
203 |
instance JSON Filter where |
|
204 |
showJSON = showFilter |
|
205 |
readJSON = readFilter |
|
115 | 206 |
|
116 | 207 |
-- | Field name to filter on. |
117 | 208 |
type FilterField = String |
118 | 209 |
|
119 | 210 |
-- | Value to compare the field value to, for filtering purposes. |
120 |
type FilterValue = String |
|
211 |
data FilterValue = QuotedString String |
|
212 |
| NumericValue Integer |
|
213 |
deriving (Read, Show, Eq) |
|
214 |
|
|
215 |
-- | Serialiser for 'FilterValue'. The Python code just sends this to |
|
216 |
-- JSON as-is, so we'll do the same. |
|
217 |
showFilterValue :: FilterValue -> JSValue |
|
218 |
showFilterValue (QuotedString str) = showJSON str |
|
219 |
showFilterValue (NumericValue val) = showJSON val |
|
220 |
|
|
221 |
-- | Decoder for 'FilterValue'. We have to see what it contains, since |
|
222 |
-- the context doesn't give us hints on what to expect. |
|
223 |
readFilterValue :: JSValue -> Result FilterValue |
|
224 |
readFilterValue (JSString a) = Ok . QuotedString $ fromJSString a |
|
225 |
readFilterValue (JSRational _ x) = |
|
226 |
if denominator x /= 1 |
|
227 |
then Error $ "Cannot deserialise numeric filter value,\ |
|
228 |
\ expecting integral but\ |
|
229 |
\ got a fractional value: " ++ show x |
|
230 |
else Ok . NumericValue $ numerator x |
|
231 |
readFilterValue v = Error $ "Cannot deserialise filter value, expecting\ |
|
232 |
\ string or integer, got " ++ show (pp_value v) |
|
233 |
|
|
234 |
instance JSON FilterValue where |
|
235 |
showJSON = showFilterValue |
|
236 |
readJSON = readFilterValue |
|
121 | 237 |
|
122 | 238 |
-- | Regexp to apply to the filter value, for filteriong purposes. |
123 | 239 |
type FilterRegexp = String |
Also available in: Unified diff