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