packages feed

zoovisitor-0.1.0.0: src/ZooKeeper/Internal/Types.hsc

{-# LANGUAGE CPP #-}

module ZooKeeper.Internal.Types where

import           Control.Exception (bracket_)
import           Control.Monad     (forM)
import           Data.Int
import           Data.Proxy        (Proxy (..))
import           Foreign
import           Foreign.C
import           Numeric           (showHex)
import           Z.Data.CBytes     (CBytes)
import qualified Z.Data.CBytes     as CBytes
import qualified Z.Data.Text       as Text
import           Z.Data.Vector     (Bytes)
import qualified Z.Foreign         as Z

#include "hs_zk.h"

-------------------------------------------------------------------------------

newtype ZHandle = ZHandle { unZHandle :: Ptr () }
  deriving (Show, Eq)

newtype ClientID = ClientID { unClientID :: Ptr () }
  deriving (Show, Eq)

newtype ZooLogLevel = ZooLogLevel CInt
  deriving (Eq, Storable)

instance Show ZooLogLevel where
  show ZooLogError = "ERROR"
  show ZooLogWarn  = "WARN"
  show ZooLogInfo  = "INFO"
  show ZooLogDebug = "DEBUG"
  show (ZooLogLevel x) = "ZooLogLevel " ++ show x

pattern ZooLogError, ZooLogWarn, ZooLogInfo, ZooLogDebug :: ZooLogLevel
pattern ZooLogError = ZooLogLevel (#const ZOO_LOG_LEVEL_ERROR)
pattern ZooLogWarn  = ZooLogLevel (#const ZOO_LOG_LEVEL_WARN)
pattern ZooLogInfo  = ZooLogLevel (#const ZOO_LOG_LEVEL_INFO)
pattern ZooLogDebug = ZooLogLevel (#const ZOO_LOG_LEVEL_DEBUG)

-------------------------------------------------------------------------------

-- | ACL permissions.
newtype ZooPerm = ZooPerm { unAcl :: CInt }
  deriving Eq

instance Show ZooPerm where
  show ZooPermRead   = "ZooPermRead"
  show ZooPermWrite  = "ZooPermWrite"
  show ZooPermCreate = "ZooPermCreate"
  show ZooPermDelete = "ZooPermDelete"
  show ZooPermAdmin  = "ZooPermAdmin"
  show ZooPermAll    = "ZooPermAll"
  show (ZooPerm x)   = "ZooPerm: 0x" ++ showHex x ""

pattern ZooPermRead :: ZooPerm
pattern ZooPermRead = ZooPerm (#const ZOO_PERM_READ)

pattern ZooPermWrite :: ZooPerm
pattern ZooPermWrite = ZooPerm (#const ZOO_PERM_WRITE)

pattern ZooPermCreate :: ZooPerm
pattern ZooPermCreate = ZooPerm (#const ZOO_PERM_CREATE)

pattern ZooPermDelete :: ZooPerm
pattern ZooPermDelete = ZooPerm (#const ZOO_PERM_DELETE)

pattern ZooPermAdmin :: ZooPerm
pattern ZooPermAdmin = ZooPerm (#const ZOO_PERM_ADMIN)

pattern ZooPermAll :: ZooPerm
pattern ZooPermAll = ZooPerm (#const ZOO_PERM_ALL)

newtype AclVector = AclVector { unAclVector :: Ptr () }
  deriving (Show, Eq)

-- | This is a completely open ACL
foreign import ccall unsafe "hs_zk.h &ZOO_OPEN_ACL_UNSAFE"
  zooOpenAclUnsafe :: AclVector

-- | This ACL gives the world the ability to read.
foreign import ccall unsafe "hs_zk.h &ZOO_READ_ACL_UNSAFE"
  zooReadAclUnsafe :: AclVector

-- | This ACL gives the creators authentication id's all permissions.
foreign import ccall unsafe "hs_zk.h &ZOO_CREATOR_ALL_ACL"
  zooCreatorAllAcl :: AclVector

-------------------------------------------------------------------------------

-- | Interest Consts
--
-- These constants are used to express interest in an event and to
-- indicate to zookeeper which events have occurred. They can
-- be ORed together to express multiple interests. These flags are
-- used in the interest and event parameters of
-- zookeeper_interest and zookeeper_process.
newtype ZooInterest = ZooInterest CInt
  deriving (Eq, Storable)

instance Show ZooInterest where
  show ZookeeperWrite  = "ZookeeperWrite"
  show ZookeeperRead   = "ZookeeperRead"
  show (ZooInterest x) = "ZooInterest: " <> show x

pattern ZookeeperWrite :: ZooInterest
pattern ZookeeperWrite = ZooInterest (#const ZOOKEEPER_WRITE)

pattern ZookeeperRead :: ZooInterest
pattern ZookeeperRead = ZooInterest (#const ZOOKEEPER_READ)

-------------------------------------------------------------------------------

-- | State Consts
--
-- These constants represent the states of a zookeeper connection. They are
-- possible parameters of the watcher callback.
newtype ZooState = ZooState CInt
  deriving (Eq, Storable)
  deriving newtype (Text.Print)

instance Show ZooState where
  show ZooExpiredSession   = "ExpiredSession"
  show ZooAuthFailed       = "AuthFailed"
  show ZooConnectingState  = "ConnectingState"
  show ZooAssociatingState = "AssociatingState"
  show ZooConnectedState   = "ConnectedState"
  show (ZooState x)        = "ZooState " <> show x

pattern
    ZooExpiredSession, ZooAuthFailed
  , ZooConnectingState, ZooAssociatingState, ZooConnectedState :: ZooState
pattern ZooExpiredSession   = ZooState (#const ZOO_EXPIRED_SESSION_STATE)
pattern ZooAuthFailed       = ZooState (#const ZOO_AUTH_FAILED_STATE)
pattern ZooConnectingState  = ZooState (#const ZOO_CONNECTING_STATE)
pattern ZooAssociatingState = ZooState (#const ZOO_ASSOCIATING_STATE)
pattern ZooConnectedState   = ZooState (#const ZOO_CONNECTED_STATE)

-- TODO
-- pattern ZOO_READONLY_STATE :: ZooState
-- pattern ZOO_READONLY_STATE = ZooState (#const ZOO_READONLY_STATE)
-- pattern ZOO_NOTCONNECTED_STATE :: ZooState
-- pattern ZOO_NOTCONNECTED_STATE = ZooState (#const ZOO_NOTCONNECTED_STATE)

-------------------------------------------------------------------------------

-- | Watch Types
--
-- These constants indicate the event that caused the watch event. They are
-- possible values of the first parameter of the watcher callback.
newtype ZooEvent = ZooEvent CInt
  deriving (Eq, Storable)

instance Show ZooEvent where
  show ZooCreateEvent     = "CreateEvent"
  show ZooDeleteEvent     = "DeleteEvent"
  show ZooChangedEvent    = "ChangedEvent"
  show ZooChildEvent      = "ChildEvent"
  show ZooSessionEvent    = "SessionEvent"
  show ZooNoWatchingEvent = "NoWatchingEvent"
  show (ZooEvent x)       = "ZooEvent " <> show x

-- | A node has been created.
--
-- This is only generated by watches on non-existent nodes. These watches
-- are set using 'ZooKeeper.zooWatchExists'.
pattern ZooCreateEvent :: ZooEvent
pattern ZooCreateEvent = ZooEvent (#const ZOO_CREATED_EVENT)

-- | A node has been deleted.
--
-- This is only generated by watches on nodes. These watches
-- are set using 'ZooKeeper.zooWatchExists' and 'ZooKeeper.zooWatchGet'.
pattern ZooDeleteEvent :: ZooEvent
pattern ZooDeleteEvent = ZooEvent (#const ZOO_DELETED_EVENT)

-- | A node has changed.
--
-- This is only generated by watches on nodes. These watches
-- are set using 'ZooKeeper.zooWatchExists' and 'ZooKeeper.zooWatchGet'.
pattern ZooChangedEvent :: ZooEvent
pattern ZooChangedEvent = ZooEvent (#const ZOO_CHANGED_EVENT)

-- A change as occurred in the list of children.
--
-- This is only generated by watches on the child list of a node. These watches
-- are set using 'ZooKeeper.zooWatchGetChildren' or 'ZooKeeper.zooWatchGetChildren2'.
pattern ZooChildEvent :: ZooEvent
pattern ZooChildEvent = ZooEvent (#const ZOO_CHILD_EVENT)

-- | A session has been lost.
--
-- This is generated when a client loses contact or reconnects with a server.
pattern ZooSessionEvent :: ZooEvent
pattern ZooSessionEvent = ZooEvent (#const ZOO_SESSION_EVENT)

-- | A watch has been removed.
--
-- This is generated when the server for some reason, probably a resource
-- constraint, will no longer watch a node for a client.
pattern ZooNoWatchingEvent :: ZooEvent
pattern ZooNoWatchingEvent = ZooEvent (#const ZOO_NOTWATCHING_EVENT)

-------------------------------------------------------------------------------

-- | These modes are used by zoo_create to affect node create.
newtype CreateMode = CreateMode { unCreateMode :: CInt }
  deriving (Show, Eq, Storable)

pattern ZooPersistent :: CreateMode
pattern ZooPersistent = CreateMode 0

-- | The znode will be deleted upon the client's disconnect.
pattern ZooEphemeral :: CreateMode
pattern ZooEphemeral = CreateMode (#const ZOO_EPHEMERAL)

pattern ZooSequence :: CreateMode
pattern ZooSequence = CreateMode (#const ZOO_SEQUENCE)

-- TODO
--pattern ZooPersistent :: CreateMode
--pattern ZooPersistent = CreateMode (#const ZOO_PERSISTENT)
--
--pattern ZooPersistentSequential :: CreateMode
--pattern ZooPersistentSequential = CreateMode (#const ZOO_PERSISTENT_SEQUENTIAL)
--
--pattern ZooEphemeralSequential :: CreateMode
--pattern ZooEphemeralSequential = CreateMode (#const ZOO_EPHEMERAL_SEQUENTIAL)
--
--pattern ZooContainer :: CreateMode
--pattern ZooContainer = CreateMode (#const ZOO_CONTAINER)
--
--pattern ZooPersistentWithTTL :: CreateMode
--pattern ZooPersistentWithTTL = CreateMode (#const ZOO_PERSISTENT_WITH_TTL)
--
--pattern ZooPersistentSequentialWithTTL :: CreateMode
--pattern ZooPersistentSequentialWithTTL = CreateMode (#const ZOO_PERSISTENT_SEQUENTIAL_WITH_TTL)

data Stat = Stat
  { statCzxid          :: Int64
  , statMzxid          :: Int64
  , statCtime          :: Int64
  , statMtime          :: Int64
  , statVersion        :: Int32
  , statCversion       :: Int32
  , statAversion       :: Int32
  , statEphemeralOwner :: Int64
  , statDataLength     :: Int32
  , statNumChildren    :: Int32
  , statPzxid          :: Int64
  } deriving (Show, Eq)

statSize :: Int
statSize = (#size stat_t)

peekStat' :: Ptr Stat -> IO Stat
peekStat' ptr = Stat
  <$> (#peek stat_t, czxid) ptr
  <*> (#peek stat_t, mzxid) ptr
  <*> (#peek stat_t, ctime) ptr
  <*> (#peek stat_t, mtime) ptr
  <*> (#peek stat_t, version) ptr
  <*> (#peek stat_t, cversion) ptr
  <*> (#peek stat_t, aversion) ptr
  <*> (#peek stat_t, ephemeralOwner) ptr
  <*> (#peek stat_t, dataLength) ptr
  <*> (#peek stat_t, numChildren) ptr
  <*> (#peek stat_t, pzxid) ptr

peekStat :: Ptr Stat -> IO Stat
peekStat ptr = peekStat' ptr <* free ptr

newtype StringVector = StringVector { unStrVec :: [CBytes] }
  deriving Show

peekStringVector :: Ptr StringVector -> IO StringVector
peekStringVector ptr = bracket_ (return ()) (free ptr) $ do
  -- NOTE: Int32 is necessary, since count is int32_t in c
  count <- fromIntegral @Int32 <$> (#peek string_vector_t, count) ptr
  StringVector <$> forM [0..count-1] (peekStringVectorIdx ptr)

peekStringVectorIdx :: Ptr StringVector -> Int -> IO CBytes
peekStringVectorIdx ptr offset = do
  ptr' <- (#peek string_vector_t, data) ptr
  data_ptr <- peek $ ptr' `plusPtr` (offset * (sizeOf ptr'))
  CBytes.fromCString data_ptr <* free data_ptr

-------------------------------------------------------------------------------
-- Callback datas

data HsWatcherCtx = HsWatcherCtx
  { watcherCtxZHandle :: ZHandle
  , watcherCtxType    :: ZooEvent
  , watcherCtxState   :: ZooState
  , watcherCtxPath    :: Maybe CBytes
  } deriving Show

hsWatcherCtxSize :: Int
hsWatcherCtxSize = (#size hs_watcher_ctx_t)

peekHsWatcherCtx :: Ptr HsWatcherCtx -> IO HsWatcherCtx
peekHsWatcherCtx ptr = do
  zh_ptr <- (#peek hs_watcher_ctx_t, zh) ptr
  event_type <-(#peek hs_watcher_ctx_t, type) ptr
  connect_state <- (#peek hs_watcher_ctx_t, state) ptr
  path_ptr <- (#peek hs_watcher_ctx_t, path) ptr
  path <- if path_ptr == nullPtr
             then return Nothing
             else Just <$> CBytes.fromCString path_ptr <* free path_ptr
  return $ HsWatcherCtx (ZHandle zh_ptr) event_type connect_state path

class Completion a where
  {-# MINIMAL csize, peekRet, peekData #-}
  csize :: Proxy a -> Int
  peekRet :: Ptr a -> IO CInt
  peekData :: Ptr a -> IO a

newtype StringCompletion = StringCompletion { strCompletionValue :: CBytes }
  deriving Show

instance Completion StringCompletion where
  csize _ = (#size hs_string_completion_t)
  peekRet ptr = (#peek hs_string_completion_t, rc) ptr
  peekData ptr = do
    value_ptr <- (#peek hs_string_completion_t, value) ptr
    value <- CBytes.fromCString value_ptr <* free value_ptr
    return $ StringCompletion value

data DataCompletion = DataCompletion
  { dataCompletionValue :: Maybe Bytes
  , dataCompletionStat  :: Stat
  } deriving (Show, Eq)

instance Completion DataCompletion where
  csize _ = (#size hs_data_completion_t)
  peekRet ptr = (#peek hs_data_completion_t, rc) ptr
  peekData ptr = do
    val_ptr <- (#peek hs_data_completion_t, value) ptr
    val_len :: CInt <- (#peek hs_data_completion_t, value_len) ptr
    val <- if val_len >= 0
              then Just <$> Z.fromPtr val_ptr (fromIntegral val_len) <* free val_ptr
              else return Nothing
    stat_ptr <- (#peek hs_data_completion_t, stat) ptr
    stat <- peekStat stat_ptr
    return $ DataCompletion val stat

newtype StatCompletion = StatCompletion { statCompletionStat :: Stat }
  deriving (Show, Eq)

instance Completion StatCompletion where
  csize _ = (#size hs_stat_completion_t)
  peekRet ptr = (#peek hs_stat_completion_t, rc) ptr
  peekData ptr = do
    stat_ptr <- (#peek hs_stat_completion_t, stat) ptr
    stat <- peekStat stat_ptr
    return $ StatCompletion stat

newtype VoidCompletion = VoidCompletion ()

instance Completion VoidCompletion where
  csize _ = (#size hs_void_completion_t)
  peekRet ptr = (#peek hs_stat_completion_t, rc) ptr
  peekData _ = return $ VoidCompletion ()

newtype StringsCompletion = StringsCompletion
  { strsCompletionValues :: StringVector }
  deriving Show

instance Completion StringsCompletion where
  csize _ = (#size hs_strings_completion_t)
  peekRet ptr = (#peek hs_strings_completion_t, rc) ptr
  peekData ptr = do
    strs_ptr <- (#peek hs_strings_completion_t, strings) ptr
    vals <- peekStringVector strs_ptr
    return $ StringsCompletion vals

data StringsStatCompletion = StringsStatCompletion
  { strsStatCompletionStrs :: StringVector
  , strsStatCompletionStat :: Stat
  } deriving Show

instance Completion StringsStatCompletion where
  csize _ = (#size hs_strings_stat_completion_t)
  peekRet ptr = (#peek hs_strings_stat_completion_t, rc) ptr
  peekData ptr = do
    strs_ptr <- (#peek hs_strings_stat_completion_t, strings) ptr
    vals <- peekStringVector strs_ptr
    stat_ptr <- (#peek hs_strings_stat_completion_t, stat) ptr
    stat <- peekStat stat_ptr
    return $ StringsStatCompletion vals stat

-------------------------------------------------------------------------------

data CZooOp
data CZooOpResult

zooOpSize :: Int
zooOpSize = (#size zoo_op_t)

zooOpResultSize :: Int
zooOpResultSize = (#size zoo_op_result_t)

-- only safe on /pinned/ byte array
type ResultBytes = Z.MutableByteArray Z.RealWorld
type TouchListBytes = [Z.MutableByteArray Z.RealWorld]

-- | This structure holds all the arguments necessary for one op as part of a
-- containing multi_op via 'ZooKeeper.zooMulti'.
data ZooOp
  = ZooCreateOp (Ptr CZooOp -> IO (ResultBytes, TouchListBytes))
  | ZooDeleteOp (Ptr CZooOp -> IO ((), TouchListBytes))
  | ZooSetOp    (Ptr CZooOp -> IO (ResultBytes, TouchListBytes))
  | ZooCheckOp  (Ptr CZooOp -> IO ((), TouchListBytes))

data ZooOpResult
  = ZooCreateOpResult CInt CBytes
  | ZooDeleteOpResult CInt
  | ZooSetOpResult    CInt Stat
  | ZooCheckOpResult  CInt
  deriving (Show, Eq)

peekZooCreateOpResult :: ResultBytes -> Ptr CZooOpResult -> IO ZooOpResult
peekZooCreateOpResult (Z.MutableByteArray ba##) ptr = do
  ret <- (#peek zoo_op_result_t, err) ptr
  value <- CBytes.fromMutablePrimArray $ Z.MutablePrimArray ba##
  return $ ZooCreateOpResult ret value

peekZooDeleteOpResult :: Ptr CZooOpResult -> IO ZooOpResult
peekZooDeleteOpResult ptr = ZooDeleteOpResult <$> (#peek zoo_op_result_t, err) ptr

peekZooSetOpResult :: ResultBytes -> Ptr CZooOpResult -> IO ZooOpResult
peekZooSetOpResult mba ptr = do
  ret <- (#peek zoo_op_result_t, err) ptr
  ba <- Z.unsafeFreezeByteArray mba
  stat <- peekStat' $ castPtr $ Z.byteArrayContents ba
  return $ ZooSetOpResult ret stat

peekZooCheckOpResult :: Ptr CZooOpResult -> IO ZooOpResult
peekZooCheckOpResult ptr = ZooCheckOpResult <$> (#peek zoo_op_result_t, err) ptr