-- | Backend-neutral parameters as Hasql encoders.
module Arbiter.Hasql.Encode
  ( buildEncoder
  , buildStatementRowCount
  , encodeSomeParam
  , colEncoder
  ) where

import Arbiter.Core.Codec (Col (..), ParamType (..), Params, SomeParam (..))
import Arbiter.Core.Sql.Query (numberPlaceholders)
import Data.Functor.Contravariant (contramap)
import Data.Int (Int64)
import Data.Text (Text)
import Hasql.Decoders qualified as D
import Hasql.Encoders qualified as E
import Hasql.Statement qualified as S

-- | Unprepared statement over numbered placeholders, returning rows affected.
buildStatementRowCount :: Text -> Params -> S.Statement () Int64
buildStatementRowCount :: Text -> Params -> Statement () Int64
buildStatementRowCount Text
sql Params
params =
  Text -> Params () -> Result Int64 -> Statement () Int64
forall params result.
Text -> Params params -> Result result -> Statement params result
S.unpreparable (Text -> Text
numberPlaceholders Text
sql) (Params -> Params ()
buildEncoder Params
params) Result Int64
D.rowsAffected

-- | A parameter list as one positional encoder.
buildEncoder :: Params -> E.Params ()
buildEncoder :: Params -> Params ()
buildEncoder = [Params ()] -> Params ()
forall a. Monoid a => [a] -> a
mconcat ([Params ()] -> Params ())
-> (Params -> [Params ()]) -> Params -> Params ()
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (SomeParam -> Params ()) -> Params -> [Params ()]
forall a b. (a -> b) -> [a] -> [b]
map SomeParam -> Params ()
encodeSomeParam

-- | Encode one parameter at its declared nullability.
encodeSomeParam :: SomeParam -> E.Params ()
encodeSomeParam :: SomeParam -> Params ()
encodeSomeParam (SomeParam ParamType a
paramType a
value) = case ParamType a
paramType of
  PScalar Col a
col -> (() -> a) -> Params a -> Params ()
forall a' a. (a' -> a) -> Params a -> Params a'
forall (f :: * -> *) a' a.
Contravariant f =>
(a' -> a) -> f a -> f a'
contramap (a -> () -> a
forall a b. a -> b -> a
const a
value) (Params a -> Params ()) -> Params a -> Params ()
forall a b. (a -> b) -> a -> b
$ NullableOrNot Value a -> Params a
forall a. NullableOrNot Value a -> Params a
E.param (Value a -> NullableOrNot Value a
forall (encoder :: * -> *) a. encoder a -> NullableOrNot encoder a
E.nonNullable (Col a -> Value a
forall a. Col a -> Value a
colEncoder Col a
col))
  PNullable Col a1
col -> (() -> a) -> Params a -> Params ()
forall a' a. (a' -> a) -> Params a -> Params a'
forall (f :: * -> *) a' a.
Contravariant f =>
(a' -> a) -> f a -> f a'
contramap (a -> () -> a
forall a b. a -> b -> a
const a
value) (Params a -> Params ()) -> Params a -> Params ()
forall a b. (a -> b) -> a -> b
$ NullableOrNot Value a -> Params a
forall a. NullableOrNot Value a -> Params a
E.param (Value a1 -> NullableOrNot Value (Maybe a1)
forall (encoder :: * -> *) a.
encoder a -> NullableOrNot encoder (Maybe a)
E.nullable (Col a1 -> Value a1
forall a. Col a -> Value a
colEncoder Col a1
col))
  PArray Col a1
col ->
    (() -> a) -> Params a -> Params ()
forall a' a. (a' -> a) -> Params a -> Params a'
forall (f :: * -> *) a' a.
Contravariant f =>
(a' -> a) -> f a -> f a'
contramap (a -> () -> a
forall a b. a -> b -> a
const a
value) (Params a -> Params ()) -> Params a -> Params ()
forall a b. (a -> b) -> a -> b
$
      NullableOrNot Value a -> Params a
forall a. NullableOrNot Value a -> Params a
E.param (Value a -> NullableOrNot Value a
forall (encoder :: * -> *) a. encoder a -> NullableOrNot encoder a
E.nonNullable (Array a -> Value a
forall a. Array a -> Value a
E.array ((forall a. (a -> a1 -> a) -> a -> a -> a) -> Array a1 -> Array a
forall b c.
(forall a. (a -> b -> a) -> a -> c -> a) -> Array b -> Array c
E.dimension (a -> a1 -> a) -> a -> a -> a
(a -> a1 -> a) -> a -> [a1] -> a
forall a. (a -> a1 -> a) -> a -> a -> a
forall b a. (b -> a -> b) -> b -> [a] -> b
forall (t :: * -> *) b a.
Foldable t =>
(b -> a -> b) -> b -> t a -> b
foldl' (NullableOrNot Value a1 -> Array a1
forall a. NullableOrNot Value a -> Array a
E.element (Value a1 -> NullableOrNot Value a1
forall (encoder :: * -> *) a. encoder a -> NullableOrNot encoder a
E.nonNullable (Col a1 -> Value a1
forall a. Col a -> Value a
colEncoder Col a1
col))))))
  PNullArray Col a1
col ->
    (() -> a) -> Params a -> Params ()
forall a' a. (a' -> a) -> Params a -> Params a'
forall (f :: * -> *) a' a.
Contravariant f =>
(a' -> a) -> f a -> f a'
contramap (a -> () -> a
forall a b. a -> b -> a
const a
value) (Params a -> Params ()) -> Params a -> Params ()
forall a b. (a -> b) -> a -> b
$
      NullableOrNot Value a -> Params a
forall a. NullableOrNot Value a -> Params a
E.param (Value a -> NullableOrNot Value a
forall (encoder :: * -> *) a. encoder a -> NullableOrNot encoder a
E.nonNullable (Array a -> Value a
forall a. Array a -> Value a
E.array ((forall a. (a -> Maybe a1 -> a) -> a -> a -> a)
-> Array (Maybe a1) -> Array a
forall b c.
(forall a. (a -> b -> a) -> a -> c -> a) -> Array b -> Array c
E.dimension (a -> Maybe a1 -> a) -> a -> a -> a
(a -> Maybe a1 -> a) -> a -> [Maybe a1] -> a
forall a. (a -> Maybe a1 -> a) -> a -> a -> a
forall b a. (b -> a -> b) -> b -> [a] -> b
forall (t :: * -> *) b a.
Foldable t =>
(b -> a -> b) -> b -> t a -> b
foldl' (NullableOrNot Value (Maybe a1) -> Array (Maybe a1)
forall a. NullableOrNot Value a -> Array a
E.element (Value a1 -> NullableOrNot Value (Maybe a1)
forall (encoder :: * -> *) a.
encoder a -> NullableOrNot encoder (Maybe a)
E.nullable (Col a1 -> Value a1
forall a. Col a -> Value a
colEncoder Col a1
col))))))

-- | Encoder for a column type.
colEncoder :: Col a -> E.Value a
colEncoder :: forall a. Col a -> Value a
colEncoder Col a
CInt4 = Value a
Value Int32
E.int4
colEncoder Col a
CInt8 = Value a
Value Int64
E.int8
colEncoder Col a
CText = Value a
Value Text
E.text
colEncoder Col a
CBool = Value a
Value Bool
E.bool
colEncoder Col a
CTimestamptz = Value a
Value UTCTime
E.timestamptz
colEncoder Col a
CJsonb = Value a
Value Value
E.jsonb
colEncoder Col a
CFloat8 = Value a
Value Double
E.float8
colEncoder Col a
CUuid = Value a
Value UUID
E.uuid