Revision 05ac718f

b/htools/Ganeti/HTools/QC.hs
590 590

  
591 591
-- | Custom 'Qlang.Filter' generator (top-level), which enforces a
592 592
-- (sane) limit on the depth of the generated filters.
593
genFilter :: Gen Qlang.Filter
593
genFilter :: Gen (Qlang.Filter Qlang.FilterField)
594 594
genFilter = choose (0, 10) >>= genFilter'
595 595

  
596 596
-- | Custom generator for filters that correctly halves the state of
597 597
-- the generators at each recursive step, per the QuickCheck
598 598
-- documentation, in order not to run out of memory.
599
genFilter' :: Int -> Gen Qlang.Filter
599
genFilter' :: Int -> Gen (Qlang.Filter Qlang.FilterField)
600 600
genFilter' 0 =
601 601
  oneof [ return Qlang.EmptyFilter
602 602
        , Qlang.TrueFilter     <$> getName
b/htools/Ganeti/Luxi.hs
112 112
  [ (luxiReqQuery,
113 113
    [ ("what",    [t| Qlang.ItemType |])
114 114
    , ("fields",  [t| [String]  |])
115
    , ("qfilter", [t| Qlang.Filter |])
115
    , ("qfilter", [t| Qlang.Filter Qlang.FilterField |])
116 116
    ])
117 117
  , (luxiReqQueryFields,
118 118
    [ ("what",    [t| Qlang.ItemType |])
b/htools/Ganeti/Qlang.hs
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"

Also available in: Unified diff