module Database.CQL.Protocol.Tuple.TH where
import Control.Applicative
import Control.Monad
import Data.Functor.Identity
import Data.Serialize
import Data.Word
import Database.CQL.Protocol.Class
import Database.CQL.Protocol.Codec (putValue, getValue)
import Database.CQL.Protocol.Types
import Language.Haskell.TH
import Prelude
class PrivateTuple a where
count :: Tagged a Int
check :: Tagged a ([ColumnType] -> [ColumnType])
tuple :: Version -> Get a
store :: Version -> Putter a
class PrivateTuple a => Tuple a
instance PrivateTuple () where
count = Tagged 0
check = Tagged $ const []
tuple _ = return ()
store _ = const $ return ()
instance Tuple ()
instance Cql a => PrivateTuple (Identity a) where
count = Tagged 1
check = Tagged $ typecheck [untag (ctype :: Tagged a ColumnType)]
tuple v = Identity <$> element v ctype
store v (Identity a) = do
put (1 :: Word16)
putValue v (toCql a)
instance Cql a => Tuple (Identity a)
genInstances :: Int -> Q [Dec]
genInstances n = join <$> mapM tupleInstance [2 .. n]
tupleInstance :: Int -> Q [Dec]
tupleInstance n = do
let cql = mkName "Cql"
vnames <- replicateM n (newName "a")
let vtypes = map VarT vnames
let tupleType = foldl1 ($:) (TupleT n : vtypes)
#if MIN_VERSION_template_haskell(2,10,0)
let ctx = map (AppT (ConT cql)) vtypes
#else
let ctx = map (\t -> ClassP cql [t]) vtypes
#endif
td <- tupleDecl n
sd <- storeDecl n
return
[ InstanceD ctx (tcon "PrivateTuple" $: tupleType)
[ FunD (mkName "count") [countDecl n]
, FunD (mkName "check") [checkDecl vnames]
, FunD (mkName "tuple") [td]
, FunD (mkName "store") [sd]
]
, InstanceD ctx (tcon "Tuple" $: tupleType) []
]
countDecl :: Int -> Clause
countDecl n = Clause [] (NormalB body) []
where
body = con "Tagged" $$ litInt n
checkDecl :: [Name] -> Clause
checkDecl names = Clause [] (NormalB body) []
where
body = con "Tagged" $$ (var "typecheck" $$ ListE (map fn names))
fn n = var "untag" $$ SigE (var "ctype") (tty n)
tty n = tcon "Tagged" $: VarT n $: tcon "ColumnType"
tupleDecl :: Int -> Q Clause
tupleDecl n = do
let v = mkName "v"
Clause [VarP v] (NormalB $ body v) <$> comb
where
body v = UInfixE (var "combine") (var "<$>") (foldl1 star (elts v))
elts v = replicate n (var "element" $$ VarE v $$ var "ctype")
star = flip UInfixE (var "<*>")
comb = do
names <- replicateM n (newName "x")
let f = NormalB $ TupE (map VarE names)
return [ FunD (mkName "combine") [Clause (map VarP names) f []] ]
storeDecl :: Int -> Q Clause
storeDecl n = do
let v = mkName "v"
names <- replicateM n (newName "k")
return $ Clause [VarP v, TupP (map VarP names)] (NormalB $ body v names) []
where
body x names = DoE (NoBindS size : map (NoBindS . value x) names)
size = var "put" $$ SigE (litInt n) (tcon "Word16")
value x v = var "putValue" $$ VarE x $$ (var "toCql" $$ VarE v)
litInt :: Integral i => i -> Exp
litInt = LitE . IntegerL . fromIntegral
var, con :: String -> Exp
var = VarE . mkName
con = ConE . mkName
tcon :: String -> Type
tcon = ConT . mkName
($$) :: Exp -> Exp -> Exp
($$) = AppE
($:) :: Type -> Type -> Type
($:) = AppT
element :: Cql a => Version -> Tagged a ColumnType -> Get a
element v t = getValue v (untag t) >>= either fail return . fromCql
typecheck :: [ColumnType] -> [ColumnType] -> [ColumnType]
typecheck rr cc = if and (zipWith (===) rr cc) then [] else rr
where
(MaybeColumn a) === b = a === b
(ListColumn a) === (ListColumn b) = a === b
(SetColumn a) === (SetColumn b) = a === b
(MapColumn a b) === (MapColumn c d) = a === c && b === d
TextColumn === VarCharColumn = True
VarCharColumn === TextColumn = True
a === b = a == b