packages feed

tigerbeetle-hs-0.1.0.0: src/Database/TigerBeetle/Internal/FFI/Account.hsc

{-# LANGUAGE ForeignFunctionInterface #-}
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE OverloadedRecordDot #-}
{-# LANGUAGE StrictData #-}
{-# LANGUAGE TypeApplications #-}
{-# LANGUAGE RecordWildCards #-}

module Database.TigerBeetle.Internal.FFI.Account where

import Data.Binary
import Data.Binary.Put
import Data.Binary.Get
import Data.WideWord
import Foreign.Ptr
import Foreign.Storable
import Data.Vector (Vector)
import Data.Vector qualified as V
import Data.Set (Set)
import Database.TigerBeetle.Internal.FFI.BitFlag (flagsToBitmask, bitmaskToFlags)

#include "tb_client.h"

data TBAccountFlags =
      Linked
    | DebitsMustNotExceedCredits
    | CreditsMustNotExceedDebits
    | History
    | Imported
    | Closed
    deriving (Enum, Eq, Ord, Show)

marshallTBAccountFlags :: Set TBAccountFlags -> Word16
marshallTBAccountFlags = flagsToBitmask

unmarshallTBAccountFlags :: Word16 -> Set TBAccountFlags
unmarshallTBAccountFlags = bitmaskToFlags

data TBAccount
  = TBAccount
  { tbAccountId             :: Word128
  , tbAccountDebitsPending  :: Word128
  , tbAccountDebitsPosted   :: Word128
  , tbAccountCreditsPending :: Word128
  , tbAccountCreditsPosted  :: Word128
  , tbAccountUserData128    :: Word128
  , tbAccountUserData64     :: Word64
  , tbAccountUserData32     :: Word32
  , tbAccountReserved       :: Word32
  , tbAccountLedger         :: Word32
  , tbAccountCode           :: Word16
  , tbAccountFlags          :: Set TBAccountFlags
  , tbAccountTimestamp      :: Word64
  }
  deriving (Eq, Show)

instance Storable TBAccount where
    sizeOf _ = #{size tb_account_t}

    alignment _ = #{alignment tb_account_t}

    peek ptr = do
      tbAccountId             <- #{peek tb_account_t, id} ptr
      tbAccountDebitsPending  <- #{peek tb_account_t, debits_pending} ptr
      tbAccountDebitsPosted   <- #{peek tb_account_t, debits_posted} ptr
      tbAccountCreditsPending <- #{peek tb_account_t, credits_pending} ptr
      tbAccountCreditsPosted  <- #{peek tb_account_t, credits_posted} ptr
      tbAccountUserData128    <- #{peek tb_account_t, user_data_128} ptr
      tbAccountUserData64     <- #{peek tb_account_t, user_data_64} ptr
      tbAccountUserData32     <- #{peek tb_account_t, user_data_32} ptr
      tbAccountReserved       <- #{peek tb_account_t, reserved} ptr
      tbAccountLedger         <- #{peek tb_account_t, ledger} ptr
      tbAccountCode           <- #{peek tb_account_t, code} ptr
      tbAccountFlags          <- unmarshallTBAccountFlags <$> #{peek tb_account_t, flags} ptr
      tbAccountTimestamp      <- #{peek tb_account_t, timestamp} ptr
      pure TBAccount{..}

    poke ptr account = do
        #{poke tb_account_t, id} ptr account.tbAccountId
        #{poke tb_account_t, debits_pending} ptr account.tbAccountDebitsPending
        #{poke tb_account_t, debits_posted} ptr account.tbAccountDebitsPosted
        #{poke tb_account_t, credits_pending} ptr account.tbAccountCreditsPending
        #{poke tb_account_t, credits_posted} ptr account.tbAccountCreditsPosted
        #{poke tb_account_t, user_data_128} ptr account.tbAccountUserData128
        #{poke tb_account_t, user_data_64} ptr account.tbAccountUserData64
        #{poke tb_account_t, user_data_32} ptr account.tbAccountUserData32
        #{poke tb_account_t, reserved} ptr account.tbAccountReserved
        #{poke tb_account_t, ledger} ptr account.tbAccountLedger
        #{poke tb_account_t, code} ptr account.tbAccountCode
        #{poke tb_account_t, flags} ptr (marshallTBAccountFlags account.tbAccountFlags)
        #{poke tb_account_t, timestamp} ptr account.tbAccountTimestamp

instance Binary TBAccount where
  put account = do
    put account.tbAccountId
    put account.tbAccountDebitsPending
    put account.tbAccountDebitsPosted
    put account.tbAccountCreditsPending
    put account.tbAccountCreditsPosted
    put account.tbAccountUserData128
    put account.tbAccountUserData64
    putWord32le account.tbAccountUserData32
    putWord32le account.tbAccountReserved
    putWord32le account.tbAccountLedger
    putWord16le account.tbAccountCode
    putWord16le . marshallTBAccountFlags $ account.tbAccountFlags
    putWord64le account.tbAccountTimestamp

  get = do
    tbAccountId <- get
    tbAccountDebitsPending <- get
    tbAccountDebitsPosted <- get
    tbAccountCreditsPending <- get
    tbAccountCreditsPosted <- get
    tbAccountUserData128 <- get
    tbAccountUserData64 <- get
    tbAccountUserData32 <- getWord32le
    tbAccountReserved <- getWord32le
    tbAccountLedger <- getWord32le
    tbAccountCode <- getWord16le
    tbAccountFlags <- unmarshallTBAccountFlags <$> getWord16le
    tbAccountTimestamp <- getWord64le
    return TBAccount{..}

data TBCreateAccountResult =
      Ok
    | LinkedEventFailed
    | LinkedEventChainOpen
    | ImportedEventExpected
    | ImportedEventNotExpected
    | TimestampMustBeZero
    | ImportedEventTimestampOutOfRange
    | ImportedEventTimestampMustNotAdvance
    | ReservedField
    | ReservedFlag
    | IdMustNotBeZero
    | IdMustNotBeIntMax
    | ExistsWithDifferentFlags
    | ExistsWithDifferentUserData128
    | ExistsWithDifferentUserData64
    | ExistsWithDifferentUserData32
    | ExistsWithDifferentLedger
    | ExistsWithDifferentCode
    | Exists
    | FlagsAreMutuallyExclusive
    | DebitsPendingMustBeZero
    | DebitsPostedMustBeZero
    | CreditsPendingMustBeZero
    | CreditsPostedMustBeZero
    | LedgerMustNotBeZero
    | CodeMustNotBeZero
    | ImportedEventTimestampMustNotRegress
    deriving (Eq, Ord, Show)

instance Enum TBCreateAccountResult where
    fromEnum Ok                                   = #const TB_CREATE_ACCOUNT_OK
    fromEnum LinkedEventFailed                    = #const TB_CREATE_ACCOUNT_LINKED_EVENT_FAILED
    fromEnum LinkedEventChainOpen                 = #const TB_CREATE_ACCOUNT_LINKED_EVENT_CHAIN_OPEN
    fromEnum ImportedEventExpected                = #const TB_CREATE_ACCOUNT_IMPORTED_EVENT_EXPECTED
    fromEnum ImportedEventNotExpected             = #const TB_CREATE_ACCOUNT_IMPORTED_EVENT_NOT_EXPECTED
    fromEnum TimestampMustBeZero                  = #const TB_CREATE_ACCOUNT_TIMESTAMP_MUST_BE_ZERO
    fromEnum ImportedEventTimestampOutOfRange     = #const TB_CREATE_ACCOUNT_IMPORTED_EVENT_TIMESTAMP_OUT_OF_RANGE
    fromEnum ImportedEventTimestampMustNotAdvance = #const TB_CREATE_ACCOUNT_IMPORTED_EVENT_TIMESTAMP_MUST_NOT_ADVANCE
    fromEnum ReservedField                        = #const TB_CREATE_ACCOUNT_RESERVED_FIELD
    fromEnum ReservedFlag                         = #const TB_CREATE_ACCOUNT_RESERVED_FLAG
    fromEnum IdMustNotBeZero                      = #const TB_CREATE_ACCOUNT_ID_MUST_NOT_BE_ZERO
    fromEnum IdMustNotBeIntMax                    = #const TB_CREATE_ACCOUNT_ID_MUST_NOT_BE_INT_MAX
    fromEnum ExistsWithDifferentFlags             = #const TB_CREATE_ACCOUNT_EXISTS_WITH_DIFFERENT_FLAGS
    fromEnum ExistsWithDifferentUserData128       = #const TB_CREATE_ACCOUNT_EXISTS_WITH_DIFFERENT_USER_DATA_128
    fromEnum ExistsWithDifferentUserData64        = #const TB_CREATE_ACCOUNT_EXISTS_WITH_DIFFERENT_USER_DATA_64
    fromEnum ExistsWithDifferentUserData32        = #const TB_CREATE_ACCOUNT_EXISTS_WITH_DIFFERENT_USER_DATA_32
    fromEnum ExistsWithDifferentLedger            = #const TB_CREATE_ACCOUNT_EXISTS_WITH_DIFFERENT_LEDGER
    fromEnum ExistsWithDifferentCode              = #const TB_CREATE_ACCOUNT_EXISTS_WITH_DIFFERENT_CODE
    fromEnum Exists                               = #const TB_CREATE_ACCOUNT_EXISTS
    fromEnum FlagsAreMutuallyExclusive            = #const TB_CREATE_ACCOUNT_FLAGS_ARE_MUTUALLY_EXCLUSIVE
    fromEnum DebitsPendingMustBeZero              = #const TB_CREATE_ACCOUNT_DEBITS_PENDING_MUST_BE_ZERO
    fromEnum DebitsPostedMustBeZero               = #const TB_CREATE_ACCOUNT_DEBITS_POSTED_MUST_BE_ZERO
    fromEnum CreditsPendingMustBeZero             = #const TB_CREATE_ACCOUNT_CREDITS_PENDING_MUST_BE_ZERO
    fromEnum CreditsPostedMustBeZero              = #const TB_CREATE_ACCOUNT_CREDITS_POSTED_MUST_BE_ZERO
    fromEnum LedgerMustNotBeZero                  = #const TB_CREATE_ACCOUNT_LEDGER_MUST_NOT_BE_ZERO
    fromEnum CodeMustNotBeZero                    = #const TB_CREATE_ACCOUNT_CODE_MUST_NOT_BE_ZERO
    fromEnum ImportedEventTimestampMustNotRegress = #const TB_CREATE_ACCOUNT_IMPORTED_EVENT_TIMESTAMP_MUST_NOT_REGRESS

    toEnum (#const TB_CREATE_ACCOUNT_OK)                                        = Ok
    toEnum (#const TB_CREATE_ACCOUNT_LINKED_EVENT_FAILED)                       = LinkedEventFailed
    toEnum (#const TB_CREATE_ACCOUNT_LINKED_EVENT_CHAIN_OPEN)                   = LinkedEventChainOpen
    toEnum (#const TB_CREATE_ACCOUNT_IMPORTED_EVENT_EXPECTED)                   = ImportedEventExpected
    toEnum (#const TB_CREATE_ACCOUNT_IMPORTED_EVENT_NOT_EXPECTED)               = ImportedEventNotExpected
    toEnum (#const TB_CREATE_ACCOUNT_TIMESTAMP_MUST_BE_ZERO)                    = TimestampMustBeZero
    toEnum (#const TB_CREATE_ACCOUNT_IMPORTED_EVENT_TIMESTAMP_OUT_OF_RANGE)     = ImportedEventTimestampOutOfRange
    toEnum (#const TB_CREATE_ACCOUNT_IMPORTED_EVENT_TIMESTAMP_MUST_NOT_ADVANCE) = ImportedEventTimestampMustNotAdvance
    toEnum (#const TB_CREATE_ACCOUNT_RESERVED_FIELD)                            = ReservedField
    toEnum (#const TB_CREATE_ACCOUNT_RESERVED_FLAG)                             = ReservedFlag
    toEnum (#const TB_CREATE_ACCOUNT_ID_MUST_NOT_BE_ZERO)                       = IdMustNotBeZero
    toEnum (#const TB_CREATE_ACCOUNT_ID_MUST_NOT_BE_INT_MAX)                    = IdMustNotBeIntMax
    toEnum (#const TB_CREATE_ACCOUNT_EXISTS_WITH_DIFFERENT_FLAGS)               = ExistsWithDifferentFlags
    toEnum (#const TB_CREATE_ACCOUNT_EXISTS_WITH_DIFFERENT_USER_DATA_128)       = ExistsWithDifferentUserData128
    toEnum (#const TB_CREATE_ACCOUNT_EXISTS_WITH_DIFFERENT_USER_DATA_64)        = ExistsWithDifferentUserData64
    toEnum (#const TB_CREATE_ACCOUNT_EXISTS_WITH_DIFFERENT_USER_DATA_32)        = ExistsWithDifferentUserData32
    toEnum (#const TB_CREATE_ACCOUNT_EXISTS_WITH_DIFFERENT_LEDGER)              = ExistsWithDifferentLedger
    toEnum (#const TB_CREATE_ACCOUNT_EXISTS_WITH_DIFFERENT_CODE)                = ExistsWithDifferentCode
    toEnum (#const TB_CREATE_ACCOUNT_EXISTS)                                    = Exists
    toEnum (#const TB_CREATE_ACCOUNT_FLAGS_ARE_MUTUALLY_EXCLUSIVE)              = FlagsAreMutuallyExclusive
    toEnum (#const TB_CREATE_ACCOUNT_DEBITS_PENDING_MUST_BE_ZERO)               = DebitsPendingMustBeZero
    toEnum (#const TB_CREATE_ACCOUNT_DEBITS_POSTED_MUST_BE_ZERO)                = DebitsPostedMustBeZero
    toEnum (#const TB_CREATE_ACCOUNT_CREDITS_PENDING_MUST_BE_ZERO)              = CreditsPendingMustBeZero
    toEnum (#const TB_CREATE_ACCOUNT_CREDITS_POSTED_MUST_BE_ZERO)               = CreditsPostedMustBeZero
    toEnum (#const TB_CREATE_ACCOUNT_LEDGER_MUST_NOT_BE_ZERO)                   = LedgerMustNotBeZero
    toEnum (#const TB_CREATE_ACCOUNT_CODE_MUST_NOT_BE_ZERO)                     = CodeMustNotBeZero
    toEnum (#const TB_CREATE_ACCOUNT_IMPORTED_EVENT_TIMESTAMP_MUST_NOT_REGRESS) = ImportedEventTimestampMustNotRegress
    toEnum unmatched                                                            = error $ "CreateAccountsResult.toEnum: Cannot match " ++ show unmatched

instance Binary TBCreateAccountResult where
  put = putWord32le . marshallTBCreateAccountResult
  get = unmarshallTBCreateAccountResult <$> getWord32le

marshallTBCreateAccountResult :: TBCreateAccountResult -> Word32
marshallTBCreateAccountResult = fromIntegral . fromEnum

unmarshallTBCreateAccountResult :: Word32 -> TBCreateAccountResult
unmarshallTBCreateAccountResult = toEnum . fromIntegral

data TBCreateAccountsResult = TBCreateAccountsResult
    { tbCreateAccountsResultIndex :: Word32
    , tbCreateAccountsResultResult :: TBCreateAccountResult
    }
    deriving (Show, Eq)

instance Storable TBCreateAccountsResult  where
    sizeOf _ = #{size tb_create_accounts_result_t}

    alignment _ = #{alignment tb_create_accounts_result_t}

    peek ptr = do
      tbCreateAccountsResultIndex  <- #{peek tb_create_accounts_result_t, index} ptr
      tbCreateAccountsResultResult <- unmarshallTBCreateAccountResult <$> #{peek tb_create_accounts_result_t, result} ptr
      pure TBCreateAccountsResult{..}

    poke ptr createAccountsResult = do
        #{poke tb_create_accounts_result_t, index} ptr createAccountsResult.tbCreateAccountsResultIndex
        #{poke tb_create_accounts_result_t, result} ptr (marshallTBCreateAccountResult $ createAccountsResult.tbCreateAccountsResultResult)

data TBAccountFilterFlags =
      Debits
    | Credits
    | Reversed
    deriving (Eq, Ord, Show)

instance Enum TBAccountFilterFlags where
    fromEnum Debits   = #const TB_ACCOUNT_FILTER_DEBITS
    fromEnum Credits  = #const TB_ACCOUNT_FILTER_CREDITS
    fromEnum Reversed = #const TB_ACCOUNT_FILTER_REVERSED

    toEnum (#const TB_ACCOUNT_FILTER_DEBITS)   = Debits
    toEnum (#const TB_ACCOUNT_FILTER_CREDITS)  = Credits
    toEnum (#const TB_ACCOUNT_FILTER_REVERSED) = Reversed
    toEnum unmatched                           = error $ "AccountFilterFlags.toEnum: Cannot match " ++ show unmatched

marshallTBAccountFilterFlags :: Set TBAccountFilterFlags -> Word32
marshallTBAccountFilterFlags = flagsToBitmask

unmarshallTBAccountFilterFlags :: Word32 -> Set TBAccountFilterFlags
unmarshallTBAccountFilterFlags = bitmaskToFlags

data TBAccountFilter = TBAccountFilter
    { tbAccountFilterAccountId :: Word128
    , tbAccountFilterUserData128 :: Word128
    , tbAccountFilterUserData64 :: Word64
    , tbAccountFilterUserData32 :: Word32
    , tbAccountFilterCode :: Word16
    , tbAccountFilterReserved :: Vector Word8
    , tbAccountFilterTimestampMin :: Word64
    , tbAccountFilterTimestampMax :: Word64
    , tbAccountFilterLimit :: Word32
    , tbAccountFilterFlags :: Set TBAccountFilterFlags
    }
    deriving (Eq, Show)

instance Storable TBAccountFilter where
    sizeOf _ = #{size tb_account_filter_t}

    alignment _ = #{alignment tb_account_filter_t}

    peek ptr = do
      let reservedPtr = #{ptr tb_account_filter_t, reserved} ptr
      tbAccountFilterAccountId <- #{peek tb_account_filter_t, account_id} ptr
      tbAccountFilterUserData128 <- #{peek tb_account_filter_t, user_data_128} ptr
      tbAccountFilterUserData64 <- #{peek tb_account_filter_t, user_data_64} ptr
      tbAccountFilterUserData32 <- #{peek tb_account_filter_t, user_data_32} ptr
      tbAccountFilterCode <- #{peek tb_account_filter_t, code} ptr
      tbAccountFilterReserved <- V.generateM 58 (peekByteOff reservedPtr)
      tbAccountFilterTimestampMin <- #{peek tb_account_filter_t, timestamp_min} ptr
      tbAccountFilterTimestampMax <- #{peek tb_account_filter_t, timestamp_max} ptr
      tbAccountFilterLimit <- #{peek tb_account_filter_t, limit} ptr
      tbAccountFilterFlags <- unmarshallTBAccountFilterFlags <$> #{peek tb_account_filter_t, flags} ptr
      pure TBAccountFilter{..}

    poke ptr accountFilter = do
      #{poke tb_account_filter_t, account_id} ptr accountFilter.tbAccountFilterAccountId
      #{poke tb_account_filter_t, user_data_128} ptr accountFilter.tbAccountFilterUserData128
      #{poke tb_account_filter_t, user_data_64} ptr accountFilter.tbAccountFilterUserData64
      #{poke tb_account_filter_t, user_data_32} ptr accountFilter.tbAccountFilterUserData32
      #{poke tb_account_filter_t, code} ptr accountFilter.tbAccountFilterCode
      #{poke tb_account_filter_t, timestamp_min} ptr accountFilter.tbAccountFilterTimestampMin
      #{poke tb_account_filter_t, timestamp_max} ptr accountFilter.tbAccountFilterTimestampMax
      #{poke tb_account_filter_t, limit} ptr accountFilter.tbAccountFilterLimit
      #{poke tb_account_filter_t, flags} ptr (marshallTBAccountFilterFlags accountFilter.tbAccountFilterFlags)
      let reservedPtr = #{ptr tb_account_filter_t, reserved} ptr
      V.iforM_ accountFilter.tbAccountFilterReserved (pokeByteOff reservedPtr)

instance Binary TBAccountFilter where
  put accountFilter = do
    put $ tbAccountFilterAccountId accountFilter
    put $ tbAccountFilterUserData128 accountFilter
    put $ tbAccountFilterUserData64 accountFilter
    putWord32le $ tbAccountFilterUserData32 accountFilter
    putWord16le $ tbAccountFilterCode accountFilter
    V.mapM_ putWord8 $ tbAccountFilterReserved accountFilter
    putWord64le $ tbAccountFilterTimestampMin accountFilter
    putWord64le $ tbAccountFilterTimestampMax accountFilter
    putWord32le $ tbAccountFilterLimit accountFilter
    putWord32le . marshallTBAccountFilterFlags $ tbAccountFilterFlags accountFilter

  get = do
    tbAccountFilterAccountId <- get
    tbAccountFilterUserData128 <- get
    tbAccountFilterUserData64 <- get
    tbAccountFilterUserData32 <- getWord32le
    tbAccountFilterCode <- getWord16le
    tbAccountFilterReserved <- V.replicateM 58 getWord8
    tbAccountFilterTimestampMin <- getWord64le
    tbAccountFilterTimestampMax <- getWord64le
    tbAccountFilterLimit <- getWord32le
    tbAccountFilterFlags <- unmarshallTBAccountFilterFlags <$> getWord32le
    return TBAccountFilter{..}

data TBAccountBalance = TBAccountBalance
    { tbAccountBalanceDebitsPending  :: Word128
    , tbAccountBalanceDebitsPosted   :: Word128
    , tbAccountBalanceCreditsPending :: Word128
    , tbAccountBalanceCreditsPosted  :: Word128
    , tbAccountBalanceTimestamp      :: Word64
    , tbAccountBalanceReserved       :: Vector Word8
    }
    deriving (Show, Eq)

instance Storable TBAccountBalance where
    sizeOf _ = #{size tb_account_balance_t}

    alignment _ = #{alignment tb_account_balance_t}

    peek ptr = do
      let reservedPtr = #{ptr tb_account_balance_t, reserved} ptr
      tbAccountBalanceDebitsPending  <- #{peek tb_account_balance_t, debits_pending} ptr
      tbAccountBalanceDebitsPosted   <- #{peek tb_account_balance_t, debits_posted} ptr
      tbAccountBalanceCreditsPending <- #{peek tb_account_balance_t, credits_pending} ptr
      tbAccountBalanceCreditsPosted  <- #{peek tb_account_balance_t, credits_posted} ptr
      tbAccountBalanceTimestamp      <- #{peek tb_account_balance_t, timestamp} ptr
      tbAccountBalanceReserved       <- V.generateM 56 (\i -> peekByteOff reservedPtr i)
      pure TBAccountBalance{..}

    poke ptr accountBalance = do
        #{poke tb_account_balance_t, debits_pending} ptr accountBalance.tbAccountBalanceDebitsPending
        #{poke tb_account_balance_t, debits_posted} ptr accountBalance.tbAccountBalanceDebitsPosted
        #{poke tb_account_balance_t, credits_pending} ptr accountBalance.tbAccountBalanceCreditsPending
        #{poke tb_account_balance_t, credits_posted} ptr accountBalance.tbAccountBalanceCreditsPosted
        #{poke tb_account_balance_t, timestamp} ptr accountBalance.tbAccountBalanceTimestamp
        let reservedPtr = #{ptr tb_account_balance_t, reserved} ptr
        V.iforM_ accountBalance.tbAccountBalanceReserved $ \i val -> pokeByteOff reservedPtr i val

instance Binary TBAccountBalance where
  put balance = do
    put $ tbAccountBalanceDebitsPending balance
    put $ tbAccountBalanceDebitsPosted balance
    put $ tbAccountBalanceCreditsPending balance
    put $ tbAccountBalanceCreditsPosted balance
    putWord64le $ tbAccountBalanceTimestamp balance
    V.mapM_ putWord8 $ tbAccountBalanceReserved balance

  get = do
    tbAccountBalanceDebitsPending <- get
    tbAccountBalanceDebitsPosted <- get
    tbAccountBalanceCreditsPending <- get
    tbAccountBalanceCreditsPosted <- get
    tbAccountBalanceTimestamp <- getWord64le
    tbAccountBalanceReserved <- V.replicateM 56 getWord8
    return TBAccountBalance{..}