module Web.Bugzilla.Internal.Search
( FieldType
, SearchTerm (..)
, SearchExpression (..)
, evalSearchExpr
) where
import Data.List
import qualified Data.Text as T
import Data.Time.Clock (UTCTime(..))
import Data.Time.ISO8601 (formatISO8601)
import Web.Bugzilla.Internal.Network
import Web.Bugzilla.Internal.Types
class FieldType a where fvAsText :: a -> T.Text
instance FieldType T.Text where fvAsText = id
instance FieldType Int where fvAsText = T.pack . show
instance FieldType UTCTime where fvAsText = T.pack . formatISO8601
instance FieldType Bool where
fvAsText True = "true"
fvAsText False = "false"
instance FieldType a => FieldType [a] where
fvAsText = T.intercalate "," . map fvAsText
data SearchTerm where
UnaryOp :: FieldType a => T.Text -> Field a -> SearchTerm
BinaryOp :: (FieldType a, FieldType b) => T.Text -> Field a -> b -> SearchTerm
data SearchExpression
= And [SearchExpression]
| Or [SearchExpression]
| Not SearchExpression
| Term SearchTerm
taggedQueryPart :: Int -> Char -> T.Text -> QueryPart
taggedQueryPart t k v = (T.cons k . T.pack . show $ t, Just v)
termQuery :: FieldType b => Int -> Field a -> T.Text -> b -> [QueryPart]
termQuery t f o v = [taggedQueryPart t 'f' (searchFieldName f),
taggedQueryPart t 'o' o,
taggedQueryPart t 'v' (fvAsText v)]
evalSearchTerm :: Int -> SearchTerm -> [QueryPart]
evalSearchTerm t (UnaryOp op field) = termQuery t field op ("" :: T.Text)
evalSearchTerm t (BinaryOp op field val) = termQuery t field op val
evalSearchExpr :: SearchExpression -> [QueryPart]
evalSearchExpr e = snd $ evalSearchExpr' 1 e
where
evalExprGroup :: Int -> [SearchExpression] -> (Int, [QueryPart])
evalExprGroup t es =
let (subExprT, subExprQs) = foldl' evalSubExpr (t + 1, []) es
qs = (taggedQueryPart t 'f' "OP") :
(taggedQueryPart subExprT 'f' "CP") :
subExprQs
in (subExprT + 1, qs)
evalSubExpr :: (Int, [QueryPart]) -> SearchExpression -> (Int, [QueryPart])
evalSubExpr (t, qs) expr = let (nextT, qs') = evalSearchExpr' t expr
in (nextT, qs ++ qs')
evalSearchExpr' :: Int -> SearchExpression -> (Int, [QueryPart])
evalSearchExpr' t (And es) = evalExprGroup t es
evalSearchExpr' t (Or es) =
let (groupT, groupQs) = evalExprGroup t es
qs = (taggedQueryPart t 'j' "OR") : groupQs
in (groupT + 1, qs)
evalSearchExpr' t (Not es) =
let (groupT, groupQs) = evalSearchExpr' t es
qs = (taggedQueryPart t 'n' "1") : groupQs
in (groupT + 1, qs)
evalSearchExpr' t (Term term) = (t + 1, evalSearchTerm t term)