{-# language BangPatterns #-}
{-# language DataKinds #-}
{-# language DeriveFunctor #-}
{-# language DerivingStrategies #-}
{-# language DuplicateRecordFields #-}
{-# language FlexibleContexts #-}
{-# language GeneralizedNewtypeDeriving #-}
{-# language MultiParamTypeClasses #-}
{-# language NamedFieldPuns #-}
{-# language OverloadedStrings #-}
{-# language PolyKinds #-}
{-# language RankNTypes #-}
{-# language TypeFamilies #-}
{-# language UnboxedTuples #-}
{-# language UndecidableInstances #-}

module Kafka.Message.Request.V2
  ( Request(..)
  , Header(..)
  , toChunks
  ) where

import Data.Int
import Data.Text (Text)
import Data.Bytes.Chunks (Chunks(ChunksCons))
import Kafka.ApiKey (ApiKey)
import Kafka.Builder (Builder)

import qualified Kafka.Builder as Builder
import qualified Kafka.Builder.Bounded as Bounded
import qualified Data.Bytes.Chunks as Chunks
import qualified Arithmetic.Nat as Nat
import qualified Data.Bytes.Encode.BigEndian as Encode.BigEndian

data Header = Header
  { Header -> ApiKey
apiKey :: !ApiKey
  , Header -> Int16
apiVersion :: !Int16
  , Header -> Int32
correlationId :: !Int32
  , Header -> Maybe Text
clientId :: !(Maybe Text)
  } deriving stock (Int -> Header -> ShowS
[Header] -> ShowS
Header -> String
(Int -> Header -> ShowS)
-> (Header -> String) -> ([Header] -> ShowS) -> Show Header
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
$cshowsPrec :: Int -> Header -> ShowS
showsPrec :: Int -> Header -> ShowS
$cshow :: Header -> String
show :: Header -> String
$cshowList :: [Header] -> ShowS
showList :: [Header] -> ShowS
Show)

-- | A Kafka request. This data type does not enforce the agreement of
-- the @apiKey@ and the serialized @body@. That resposibility is left
-- to the user.
data Request = Request
  { Request -> Header
header :: !Header
  , Request -> Chunks
body :: !Chunks
    -- ^ Pre-encoded body
  }

toChunks :: Request -> Chunks
toChunks :: Request -> Chunks
toChunks Request{Header
$sel:header:Request :: Request -> Header
header :: Header
header,Chunks
$sel:body:Request :: Request -> Chunks
body :: Chunks
body} =
  let payload :: Chunks
payload = Int -> Builder -> Chunks -> Chunks
Builder.runOnto Int
64 (Header -> Builder
builderHeader Header
header) Chunks
body
      len :: Int
len = Chunks -> Int
Chunks.length Chunks
payload
   in Bytes -> Chunks -> Chunks
ChunksCons (Word32 -> Bytes
Encode.BigEndian.word32 (Int -> Word32
forall a b. (Integral a, Num b) => a -> b
fromIntegral Int
len)) Chunks
payload

builderHeader :: Header -> Builder
builderHeader :: Header -> Builder
builderHeader Header{ApiKey
$sel:apiKey:Header :: Header -> ApiKey
apiKey :: ApiKey
apiKey,Int16
$sel:apiVersion:Header :: Header -> Int16
apiVersion :: Int16
apiVersion,Int32
$sel:correlationId:Header :: Header -> Int32
correlationId :: Int32
correlationId,Maybe Text
$sel:clientId:Header :: Header -> Maybe Text
clientId :: Maybe Text
clientId} =
  Nat (2 + 6) -> Builder (2 + 6) -> Builder
forall (n :: Nat). Nat n -> Builder n -> Builder
Builder.fromBounded Nat (2 + 6)
forall (n :: Nat). KnownNat n => Nat n
Nat.constant
    ( ApiKey -> Builder 2
Bounded.apiKey ApiKey
apiKey
      Builder 2 -> Builder 6 -> Builder (2 + 6)
forall (m :: Nat) (n :: Nat).
Builder m -> Builder n -> Builder (m + n)
`Bounded.append`
      Int16 -> Builder 2
Bounded.int16 Int16
apiVersion
      Builder 2 -> Builder 4 -> Builder (2 + 4)
forall (m :: Nat) (n :: Nat).
Builder m -> Builder n -> Builder (m + n)
`Bounded.append`
      Int32 -> Builder 4
Bounded.int32 Int32
correlationId
    )
  Builder -> Builder -> Builder
forall a. Semigroup a => a -> a -> a
<>
  Maybe Text -> Builder
Builder.nullableString Maybe Text
clientId
  Builder -> Builder -> Builder
forall a. Semigroup a => a -> a -> a
<>
  -- Zero tagged fields. There are currently no tagged fields that
  -- can appear in the header, so we do not bother allowing the user
  -- to set these.
  Word8 -> Builder
Builder.word8 Word8
0