module Database.RethinkDB.Types where
import Control.Applicative
import Control.Monad.State (State, gets, modify, evalState)
import Data.Word
import Data.String
import Data.Text (Text)
import Data.Time
import Data.Time.Clock.POSIX
import Data.Aeson (FromJSON, parseJSON, toJSON)
import Data.Aeson.Types (Parser, Value)
import qualified Data.Aeson as A
import Data.Vector (Vector)
import qualified Data.Vector as V
import Data.HashMap.Strict (HashMap)
import qualified Data.HashMap.Strict as HMS
import Data.Set (Set)
import qualified Data.Set as S
import Database.RethinkDB.Types.Datum
import Prelude
class Term a where
toTerm :: a -> State Context A.Value
instance Term A.Value where
toTerm = pure
data Context = Context
{ varCounter :: !Int
, defaultDatabase :: !(Exp Database)
}
compileTerm :: Exp Database -> State Context A.Value -> A.Value
compileTerm db e = evalState e (Context 0 db)
newVar :: State Context Int
newVar = do
ix <- gets varCounter
modify $ \s -> s { varCounter = ix + 1 }
pure ix
class IsDatum a
instance IsDatum Datum
instance Term Datum where
toTerm (Null ) = pure $ A.Null
toTerm (Bool x) = toTerm x
toTerm (Number x) = toTerm x
toTerm (String x) = toTerm x
toTerm (Array x) = toTerm x
toTerm (Object x) = toTerm x
toTerm (Time x) = toTerm x
instance FromResponse Datum where
parseResponse = responseAtomParser
instance FromResponse (Maybe Datum) where
parseResponse r = case (responseType r, V.toList (responseResult r)) of
(SuccessAtom, [a]) -> do
res0 <- parseWire a
case res0 of
Null -> pure Nothing
res -> pure $ Just res
_ -> fail $ "responseAtomParser: Not a single-element vector " ++ show (responseResult r)
instance IsDatum Bool
instance FromResponse Bool where
parseResponse = responseAtomParser
instance Term Bool where
toTerm = pure . A.Bool
instance IsDatum Double
instance FromResponse Double where
parseResponse = responseAtomParser
instance Term Double where
toTerm = pure . toJSON
instance FromResponse Int where
parseResponse = responseAtomParser
instance FromResponse Char where
parseResponse = responseAtomParser
instance FromResponse [Char] where
parseResponse = responseAtomParser
instance IsDatum Text
instance FromResponse Text where
parseResponse = responseAtomParser
instance Term Text where
toTerm = pure . toJSON
instance (IsDatum a) => IsDatum (Array a)
instance (IsDatum a) => IsSequence (Array a)
instance (FromDatum a) => FromResponse (Array a) where
parseResponse = responseAtomParser
instance (Term a) => Term (Array a) where
toTerm v = do
vals <- mapM toTerm (V.toList v)
options <- toTerm emptyOptions
pure $ A.Array $ V.fromList $
[ A.Number 2
, toJSON vals
, toJSON $ options
]
class (IsDatum a) => IsObject a
instance IsDatum Object
instance IsObject Object
instance FromResponse Object where
parseResponse = responseAtomParser
instance Term Object where
toTerm x = do
items <- mapM (\(k, v) -> (,) <$> pure k <*> toTerm v) $ HMS.toList x
pure $ A.Object $ HMS.fromList $ items
instance IsDatum ZonedTime
instance IsObject ZonedTime
instance FromResponse ZonedTime where
parseResponse = responseAtomParser
instance Term ZonedTime where
toTerm x = pure $ A.object
[ "$reql_type$" A..= ("TIME" :: Text)
, "timezone" A..= (timeZoneOffsetString $ zonedTimeZone x)
, "epoch_time" A..= (realToFrac $ utcTimeToPOSIXSeconds $ zonedTimeToUTC x :: Double)
]
instance IsDatum UTCTime
instance IsObject UTCTime
instance FromResponse UTCTime where
parseResponse = responseAtomParser
instance Term UTCTime where
toTerm = toTerm . utcToZonedTime utc
data Table = MkTable
instance IsSequence Table
data SingleSelection = SingleSelection
deriving (Show)
instance IsDatum SingleSelection
instance IsObject SingleSelection
data Database = MkDatabase
data Bound = Open !Datum | Closed !Datum
boundDatum :: Bound -> Datum
boundDatum (Open x) = x
boundDatum (Closed x) = x
boundString :: Bound -> Text
boundString (Open _) = "open"
boundString (Closed _) = "closed"
data ConflictResolutionStrategy
= CRError
| CRReplace
| CRUpdate
instance ToDatum ConflictResolutionStrategy where
toDatum CRError = String "error"
toDatum CRReplace = String "replace"
toDatum CRUpdate = String "update"
data Order = Ascending !Text | Descending !Text
instance Term Order where
toTerm (Ascending key) = simpleTerm 73 [SomeExp $ lift key]
toTerm (Descending key) = simpleTerm 74 [SomeExp $ lift key]
data Sequence a
= Done !(Vector a)
| Partial !Token !(Vector a)
class IsSequence a
instance Show (Sequence a) where
show (Done v) = "Done " ++ (show $ V.length v)
show (Partial _ v) = "Partial " ++ (show $ V.length v)
instance (FromDatum a) => FromResponse (Sequence a) where
parseResponse = responseSequenceParser
instance IsSequence (Sequence a)
instance (FromDatum a) => FromDatum (Sequence a) where
parseDatum (Array x) = Done <$> V.mapM parseDatum x
parseDatum _ = fail "Sequence"
data Exp a where
Constant :: (ToDatum a) => a -> Exp a
MkArray :: [Exp a] -> Exp (Array a)
ListDatabases :: Exp (Array Text)
CreateDatabase :: Exp Text -> Exp Object
DropDatabase :: Exp Text -> Exp Object
WaitDatabase :: Exp Database -> Exp Object
ListTables :: Exp Database -> Exp (Array Text)
CreateTable :: Exp Database -> Exp Text -> Exp Object
DropTable :: Exp Database -> Exp Text -> Exp Object
WaitTable :: Exp Table -> Exp Object
ListIndices :: Exp Table -> Exp (Array Text)
CreateIndex :: (IsDatum a) => Exp Table -> Exp Text -> (Exp Object -> Exp a) -> Exp Object
DropIndex :: Exp Table -> Exp Text -> Exp Object
IndexStatus :: Exp Table -> [Exp Text] -> Exp (Array Object)
WaitIndex :: Exp Table -> [Exp Text] -> Exp (Array Object)
Database :: Exp Text -> Exp Database
Table :: Maybe (Exp Database) -> Exp Text -> Exp Table
Coerce :: Exp a -> Exp Text -> Exp b
Eq :: (IsDatum a, IsDatum b) => Exp a -> Exp b -> Exp Bool
Ne :: (IsDatum a, IsDatum b) => Exp a -> Exp b -> Exp Bool
Not :: Exp Bool -> Exp Bool
Match :: Exp Text -> Exp Text -> Exp Datum
Get :: Exp Table -> Exp Text -> Exp SingleSelection
GetAll :: (IsDatum a) => Exp Table -> [Exp a] -> Exp (Array Datum)
GetAllIndexed :: (IsDatum a) => Exp Table -> [Exp a] -> Text -> Exp (Sequence Datum)
Add :: (Num a) => [Exp a] -> Exp a
Sub :: (Num a) => [Exp a] -> Exp a
Multiply :: (Num a) => [Exp a] -> Exp a
All :: [Exp Bool] -> Exp Bool
Any :: [Exp Bool] -> Exp Bool
GetField :: (IsObject a, IsDatum r) => Exp Text -> Exp a -> Exp r
HasFields :: (IsObject a) => [Text] -> Exp a -> Exp Bool
Take :: (IsSequence s) => Exp Double -> Exp s -> Exp s
Append :: (IsDatum a) => Exp (Array a) -> Exp a -> Exp (Array a)
Prepend :: (IsDatum a) => Exp (Array a) -> Exp a -> Exp (Array a)
IsEmpty :: (IsSequence a) => Exp a -> Exp Bool
Delete :: Exp a -> Exp Object
InsertObject :: ConflictResolutionStrategy -> Exp Table -> Object -> Exp Object
InsertSequence :: (IsSequence s) => Exp Table -> Exp s -> Exp Object
Filter :: (IsSequence s) => (Exp a -> Exp Bool) -> Exp s -> Exp s
Map :: (IsSequence s) => (Exp a -> Exp b) -> Exp s -> Exp (Sequence b)
Between :: (IsSequence s) => (Bound, Bound) -> Exp s -> Exp s
BetweenIndexed :: (IsSequence s) => Text -> (Bound, Bound) -> Exp s -> Exp s
OrderBy :: (IsSequence s) => [Order] -> Exp s -> Exp s
OrderByIndexed :: (IsSequence s) => Order -> Exp s -> Exp s
Keys :: (IsObject a) => Exp a -> Exp (Array Text)
Var :: Int -> Exp a
Function :: State Context ([Int], Exp a) -> Exp f
Call :: Exp f -> [SomeExp] -> Exp r
Limit :: (IsSequence s) => Double -> Exp s -> Exp s
Nth :: (IsSequence s, IsDatum r) => Double -> Exp s -> Exp r
UUID :: Exp Text
Now :: Exp ZonedTime
Timezone :: Exp ZonedTime -> Exp Text
RandomInteger :: Exp Int -> Exp Int -> Exp Int
RandomFloat :: Exp Double -> Exp Double -> Exp Double
Info :: Exp a -> Exp Object
Default :: Exp a -> Exp a -> Exp a
Error :: Exp Text -> Exp a
SequenceChanges :: (IsSequence s) => Exp s -> Exp (Sequence ChangeNotification)
SingleSelectionChanges :: (IsDatum a) => Exp a -> Exp (Sequence ChangeNotification)
instance Term (Exp a) where
toTerm (Constant datum) =
toTerm $ toDatum datum
toTerm (MkArray xs) =
simpleTerm 2 (map SomeExp xs)
toTerm ListDatabases =
noargTerm 59
toTerm (CreateDatabase name) =
simpleTerm 57 [SomeExp name]
toTerm (DropDatabase name) =
simpleTerm 58 [SomeExp name]
toTerm (WaitDatabase db) =
simpleTerm 177 [SomeExp db]
toTerm (ListTables db) =
simpleTerm 62 [SomeExp db]
toTerm (CreateTable db name) =
simpleTerm 60 [SomeExp db, SomeExp name]
toTerm (DropTable db name) =
simpleTerm 61 [SomeExp db, SomeExp name]
toTerm (WaitTable table) =
simpleTerm 177 [SomeExp table]
toTerm (ListIndices table) =
simpleTerm 77 [SomeExp table]
toTerm (CreateIndex table name f) =
simpleTerm 75 [SomeExp table, SomeExp name, SomeExp (lift f)]
toTerm (DropIndex table name) =
simpleTerm 76 [SomeExp table, SomeExp name]
toTerm (IndexStatus table indices) =
simpleTerm 139 ([SomeExp table] ++ map SomeExp indices)
toTerm (WaitIndex table indices) =
simpleTerm 140 ([SomeExp table] ++ map SomeExp indices)
toTerm (Database name) =
simpleTerm 14 [SomeExp name]
toTerm (Table mbDatabase name) = do
db <- maybe (gets defaultDatabase) pure mbDatabase
simpleTerm 15 [SomeExp db, SomeExp name]
toTerm (Filter f s) =
simpleTerm 39 [SomeExp s, SomeExp (lift f)]
toTerm (Map f s) =
simpleTerm 38 [SomeExp s, SomeExp (lift f)]
toTerm (Between (l, u) s) =
termWithOptions 36 [SomeExp s, SomeExp $ lift (boundDatum l), SomeExp $ lift (boundDatum u)] $
HMS.fromList
[ ("left_bound", toJSON $ String (boundString l))
, ("right_bound", toJSON $ String (boundString u))
]
toTerm (BetweenIndexed index (l, u) s) =
termWithOptions 36 [SomeExp s, SomeExp $ lift (boundDatum l), SomeExp $ lift (boundDatum u)] $
HMS.fromList
[ ("left_bound", toJSON $ String (boundString l))
, ("right_bound", toJSON $ String (boundString u))
, ("index", toJSON $ String index)
]
toTerm (OrderBy spec s) = do
s' <- toTerm s
spec' <- mapM toTerm spec
simpleTerm 41 ([s'] ++ spec')
toTerm (OrderByIndexed spec s) = do
s' <- toTerm s
spec' <- toTerm spec
termWithOptions 41 [s'] $ HMS.singleton "index" spec'
toTerm (InsertObject crs table obj) =
termWithOptions 56 [SomeExp table, SomeExp (lift obj)] $
HMS.singleton "conflict" (toJSON $ toDatum crs)
toTerm (InsertSequence table s) =
termWithOptions 56 [SomeExp table, SomeExp s] HMS.empty
toTerm (Delete selection) =
simpleTerm 54 [SomeExp selection]
toTerm (GetField field obj) =
simpleTerm 31 [SomeExp obj, SomeExp field]
toTerm (HasFields fields obj) =
simpleTerm 32 ([SomeExp obj] ++ map (SomeExp . lift) fields)
toTerm (Coerce value typeName) =
simpleTerm 51 [SomeExp value, SomeExp typeName]
toTerm (Add values) =
simpleTerm 24 (map SomeExp values)
toTerm (Sub values) =
simpleTerm 25 (map SomeExp values)
toTerm (Multiply values) =
simpleTerm 26 (map SomeExp values)
toTerm (All values) =
simpleTerm 67 (map SomeExp values)
toTerm (Any values) =
simpleTerm 66 (map SomeExp values)
toTerm (Eq a b) =
simpleTerm 17 [SomeExp a, SomeExp b]
toTerm (Ne a b) =
simpleTerm 18 [SomeExp a, SomeExp b]
toTerm (Not e) =
simpleTerm 23 [SomeExp e]
toTerm (Match str re) =
simpleTerm 97 [SomeExp str, SomeExp re]
toTerm (Get table key) =
simpleTerm 16 [SomeExp table, SomeExp key]
toTerm (GetAll table keys) =
simpleTerm 78 ([SomeExp table] ++ map SomeExp keys)
toTerm (GetAllIndexed table keys index) =
termWithOptions 78 ([SomeExp table] ++ map SomeExp keys)
(HMS.singleton "index" (toJSON $ String index))
toTerm (Take n s) =
simpleTerm 71 [SomeExp s, SomeExp n]
toTerm (Append array value) =
simpleTerm 29 [SomeExp array, SomeExp value]
toTerm (Prepend array value) =
simpleTerm 80 [SomeExp array, SomeExp value]
toTerm (IsEmpty s) =
simpleTerm 86 [SomeExp s]
toTerm (Keys a) =
simpleTerm 94 [SomeExp a]
toTerm (Var a) =
simpleTerm 10 [SomeExp $ lift $ (fromIntegral a :: Double)]
toTerm (Function a) = do
(vars, f) <- a
simpleTerm 69 [SomeExp $ Constant $ V.fromList $ map (Number . fromIntegral) vars, SomeExp f]
toTerm (Call f args) =
simpleTerm 64 ([SomeExp f] ++ args)
toTerm (Limit n s) =
simpleTerm 71 [SomeExp s, SomeExp (lift n)]
toTerm (Nth n s) =
simpleTerm 45 [SomeExp s, SomeExp (lift n)]
toTerm UUID =
noargTerm 169
toTerm Now =
noargTerm 103
toTerm (Timezone time) =
simpleTerm 127 [SomeExp time]
toTerm (RandomInteger lo hi) =
simpleTerm 151 [SomeExp lo, SomeExp hi]
toTerm (RandomFloat lo hi) =
termWithOptions 151 [SomeExp lo, SomeExp hi] $
HMS.singleton "float" (toJSON $ Bool True)
toTerm (Info a) =
simpleTerm 79 [SomeExp a]
toTerm (Default action def) =
simpleTerm 92 [SomeExp action, SomeExp def]
toTerm (Error message) =
simpleTerm 12 [SomeExp message]
toTerm (SequenceChanges stream) =
simpleTerm 152 [SomeExp stream]
toTerm (SingleSelectionChanges stream) =
simpleTerm 152 [SomeExp stream]
noargTerm :: Int -> State Context A.Value
noargTerm termType = pure $ A.Array $ V.fromList [toJSON termType]
simpleTerm :: (Term a) => Int -> [a] -> State Context A.Value
simpleTerm termType args = do
args' <- mapM toTerm args
pure $ A.Array $ V.fromList [toJSON termType, toJSON args']
termWithOptions :: (Term a) => Int -> [a] -> HashMap Text Value -> State Context A.Value
termWithOptions termType args options = do
args' <- mapM toTerm args
pure $ A.Array $ V.fromList [toJSON termType, toJSON args', toJSON options]
instance IsString (Exp Text) where
fromString = lift . (fromString :: String -> Text)
instance Num (Exp Double) where
fromInteger = Constant . fromInteger
a + b = Add [a, b]
a * b = Multiply [a, b]
abs _ = error "Num (Exp a): abs not implemented"
signum _ = error "Num (Exp a): signum not implemented"
negate _ = error "Num (Exp a): negate not implemented"
class Lift c e where
type Simplified e
lift :: e -> c (Simplified e)
instance Lift Exp Bool where
type Simplified Bool = Bool
lift = Constant
instance Lift Exp Int where
type Simplified Int = Int
lift = Constant
instance Lift Exp Double where
type Simplified Double = Double
lift = Constant
instance Lift Exp Char where
type Simplified Char = Char
lift = Constant
instance Lift Exp String where
type Simplified String = String
lift = Constant
instance Lift Exp Text where
type Simplified Text = Text
lift = Constant
instance Lift Exp Object where
type Simplified Object = Object
lift = Constant
instance Lift Exp Datum where
type Simplified Datum = Datum
lift = Constant
instance Lift Exp ZonedTime where
type Simplified ZonedTime = ZonedTime
lift = Constant
instance Lift Exp UTCTime where
type Simplified UTCTime = ZonedTime
lift = Constant . utcToZonedTime utc
instance Lift Exp (Array Datum) where
type Simplified (Array Datum) = (Array Datum)
lift = Constant
instance Lift Exp [Exp a] where
type Simplified [Exp a] = Array a
lift = MkArray
instance Lift Exp (Exp a -> Exp r) where
type Simplified (Exp a -> Exp r) = Exp r
lift f = Function $ do
v1 <- newVar
pure $ ([v1], f (Var v1))
instance Lift Exp (Exp a -> Exp b -> Exp r) where
type Simplified (Exp a -> Exp b -> Exp r) = Exp r
lift f = Function $ do
v1 <- newVar
v2 <- newVar
pure $ ([v1, v2], f (Var v1) (Var v2))
call1 :: (Exp a -> Exp r) -> Exp a -> Exp r
call1 f a = Call (lift f) [SomeExp a]
call2 :: (Exp a -> Exp b -> Exp r) -> Exp a -> Exp b -> Exp r
call2 f a b = Call (lift f) [SomeExp a, SomeExp b]
emptyOptions :: Object
emptyOptions = HMS.empty
data SomeExp where
SomeExp :: Exp a -> SomeExp
instance Term SomeExp where
toTerm (SomeExp e) = toTerm e
type family Result a
type instance Result Text = Text
type instance Result Double = Double
type instance Result Int = Int
type instance Result Char = Char
type instance Result String = String
type instance Result Bool = Bool
type instance Result ZonedTime = ZonedTime
type instance Result Table = Sequence Datum
type instance Result Datum = Datum
type instance Result Object = Object
type instance Result (Array a) = Array a
type instance Result SingleSelection = Maybe Datum
type instance Result (Sequence a) = Sequence a
type Res a = Either Error (Result a)
class FromResponse a where
parseResponse :: Response -> Parser a
responseAtomParser :: (FromDatum a) => Response -> Parser a
responseAtomParser r = case (responseType r, V.toList (responseResult r)) of
(SuccessAtom, [a]) -> parseWire a >>= parseDatum
_ -> fail $ "responseAtomParser: Not a single-element vector " ++ show (responseResult r)
responseSequenceParser :: (FromDatum a) => Response -> Parser (Sequence a)
responseSequenceParser r = case responseType r of
SuccessAtom -> Done <$> responseAtomParser r
SuccessSequence -> Done <$> values
SuccessPartial -> Partial <$> pure (responseToken r) <*> values
rt -> fail $ "responseSequenceParser: Unexpected type " ++ show rt
where
values = V.mapM (\x -> parseWire x >>= parseDatum) (responseResult r)
type Token = Word64
data ResponseType
= SuccessAtom
| SuccessSequence
| SuccessPartial
| WaitComplete
| RTServerInfo
| ClientErrorType
| CompileErrorType
| RuntimeErrorType
deriving (Show, Eq)
instance FromJSON ResponseType where
parseJSON (A.Number 1) = pure SuccessAtom
parseJSON (A.Number 2) = pure SuccessSequence
parseJSON (A.Number 3) = pure SuccessPartial
parseJSON (A.Number 4) = pure WaitComplete
parseJSON (A.Number 5) = pure RTServerInfo
parseJSON (A.Number 16) = pure ClientErrorType
parseJSON (A.Number 17) = pure CompileErrorType
parseJSON (A.Number 18) = pure RuntimeErrorType
parseJSON _ = fail "ResponseType"
data ResponseNote
= SequenceFeed
| AtomFeed
| OrderByLimitFeed
| UnionedFeed
| IncludesStates
deriving (Show, Eq, Ord)
instance FromJSON ResponseNote where
parseJSON (A.Number 1) = pure SequenceFeed
parseJSON (A.Number 2) = pure AtomFeed
parseJSON (A.Number 3) = pure OrderByLimitFeed
parseJSON (A.Number 4) = pure UnionedFeed
parseJSON (A.Number 5) = pure IncludesStates
parseJSON _ = fail "ResponseNote"
data Response = Response
{ responseToken :: !Token
, responseType :: !ResponseType
, responseResult :: !(Vector Value)
, responseNotes :: !(Set ResponseNote)
} deriving (Show, Eq)
responseParser :: Token -> Value -> Parser Response
responseParser token (A.Object o) = Response
<$> pure token
<*> o A..: "t"
<*> o A..: "r"
<*> (S.fromList <$> o A..:? "n" A..!= [])
responseParser _ _ =
fail "responseParser: Unexpected JSON value"
data Error
= ProtocolError !Text
| ClientError !Text
| CompileError !Text
| RuntimeError !Text
deriving (Eq, Show)
data ServerInfo = ServerInfo
{ siId :: !Text
, siName :: !Text
} deriving (Show)
instance FromResponse ServerInfo where
parseResponse r = case (responseType r, V.toList (responseResult r)) of
(RTServerInfo, [a]) -> parseWire a >>= \datum -> case datum of
(Object o) -> ServerInfo <$> o .: "id" <*> o .: "name"
_ -> fail "ServerInfo"
_ -> fail $ "ServerInfo: Bad response" ++ show (responseResult r)
data ChangeNotification = ChangeNotification
{ cnOldValue :: !Datum
, cnNewValue :: !Datum
} deriving (Show)
instance FromDatum ChangeNotification where
parseDatum (Object o) = ChangeNotification <$> o .: "old_val" <*> o .: "new_val"
parseDatum _ = fail "ChangeNotification"