-- |
-- A DSL for declaration of query parameter encoders.
module Hasql.Private.Encoders where

import qualified Data.Aeson as Aeson
import qualified Data.ByteString.Lazy as LazyByteString
import qualified Hasql.Private.Encoders.Array as Array
import qualified Hasql.Private.Encoders.Params as Params
import qualified Hasql.Private.Encoders.Value as Value
import qualified Hasql.Private.PTI as PTI
import Hasql.Private.Prelude hiding (bool)
import qualified Hasql.Private.Prelude as Prelude
import qualified Network.IP.Addr as NetworkIp
import qualified PostgreSQL.Binary.Encoding as A
import qualified Text.Builder as C

-- * Parameters Product Encoder

-- |
-- Encoder of some representation of a parameters product.
--
-- Has instances of 'Contravariant', 'Divisible' and 'Monoid',
-- which you can use to compose multiple parameters together.
-- E.g.,
--
-- @
-- someParamsEncoder :: 'Params' (Int64, Maybe Text)
-- someParamsEncoder =
--   ('fst' '>$<' 'param' ('nonNullable' 'int8')) '<>'
--   ('snd' '>$<' 'param' ('nullable' 'text'))
-- @
--
-- As a general solution for tuples of any arity, instead of 'fst' and 'snd',
-- consider the functions of the @contrazip@ family
-- from the \"contravariant-extras\" package.
-- E.g., here's how you can achieve the same as the above:
--
-- @
-- someParamsEncoder :: 'Params' (Int64, Maybe Text)
-- someParamsEncoder =
--   'contrazip2' ('param' ('nonNullable' 'int8')) ('param' ('nullable' 'text'))
-- @
--
-- Here's how you can implement encoders for custom composite types:
--
-- @
-- data Person = Person { name :: Text, gender :: Gender, age :: Int }
--
-- data Gender = Male | Female
--
-- personParams :: 'Params' Person
-- personParams =
--   (name '>$<' 'param' ('nonNullable' 'text')) '<>'
--   (gender '>$<' 'param' ('nonNullable' genderValue)) '<>'
--   ('fromIntegral' . age '>$<' 'param' ('nonNullable' 'int8'))
--
-- genderValue :: 'Value' Gender
-- genderValue = 'enum' genderText 'text' where
--   genderText gender = case gender of
--     Male -> "male"
--     Female -> "female"
-- @
newtype Params a = Params (Params.Params a)
  deriving (forall b a. b -> Params b -> Params a
forall a' a. (a' -> a) -> Params a -> Params a'
forall (f :: * -> *).
(forall a' a. (a' -> a) -> f a -> f a')
-> (forall b a. b -> f b -> f a) -> Contravariant f
>$ :: forall b a. b -> Params b -> Params a
$c>$ :: forall b a. b -> Params b -> Params a
contramap :: forall a' a. (a' -> a) -> Params a -> Params a'
$ccontramap :: forall a' a. (a' -> a) -> Params a -> Params a'
Contravariant, Contravariant Params
forall a. Params a
forall a b c. (a -> (b, c)) -> Params b -> Params c -> Params a
forall (f :: * -> *).
Contravariant f
-> (forall a b c. (a -> (b, c)) -> f b -> f c -> f a)
-> (forall a. f a)
-> Divisible f
conquer :: forall a. Params a
$cconquer :: forall a. Params a
divide :: forall a b c. (a -> (b, c)) -> Params b -> Params c -> Params a
$cdivide :: forall a b c. (a -> (b, c)) -> Params b -> Params c -> Params a
Divisible, Divisible Params
forall a. (a -> Void) -> Params a
forall a b c. (a -> Either b c) -> Params b -> Params c -> Params a
forall (f :: * -> *).
Divisible f
-> (forall a. (a -> Void) -> f a)
-> (forall a b c. (a -> Either b c) -> f b -> f c -> f a)
-> Decidable f
choose :: forall a b c. (a -> Either b c) -> Params b -> Params c -> Params a
$cchoose :: forall a b c. (a -> Either b c) -> Params b -> Params c -> Params a
lose :: forall a. (a -> Void) -> Params a
$close :: forall a. (a -> Void) -> Params a
Decidable, Params a
[Params a] -> Params a
Params a -> Params a -> Params a
forall {a}. Semigroup (Params a)
forall a. Params a
forall a.
Semigroup a -> a -> (a -> a -> a) -> ([a] -> a) -> Monoid a
forall a. [Params a] -> Params a
forall a. Params a -> Params a -> Params a
mconcat :: [Params a] -> Params a
$cmconcat :: forall a. [Params a] -> Params a
mappend :: Params a -> Params a -> Params a
$cmappend :: forall a. Params a -> Params a -> Params a
mempty :: Params a
$cmempty :: forall a. Params a
Monoid, NonEmpty (Params a) -> Params a
Params a -> Params a -> Params a
forall b. Integral b => b -> Params a -> Params a
forall a. NonEmpty (Params a) -> Params a
forall a. Params a -> Params a -> Params a
forall a.
(a -> a -> a)
-> (NonEmpty a -> a)
-> (forall b. Integral b => b -> a -> a)
-> Semigroup a
forall a b. Integral b => b -> Params a -> Params a
stimes :: forall b. Integral b => b -> Params a -> Params a
$cstimes :: forall a b. Integral b => b -> Params a -> Params a
sconcat :: NonEmpty (Params a) -> Params a
$csconcat :: forall a. NonEmpty (Params a) -> Params a
<> :: Params a -> Params a -> Params a
$c<> :: forall a. Params a -> Params a -> Params a
Semigroup)

-- |
-- No parameters. Same as `mempty` and `conquered`.
noParams :: Params ()
noParams :: Params ()
noParams = forall a. Monoid a => a
mempty

-- |
-- Lift a single parameter encoder, with its nullability specified,
-- associating it with a single placeholder.
param :: NullableOrNot Value a -> Params a
param :: forall a. NullableOrNot Value a -> Params a
param = \case
  NonNullable (Value Value a
valueEnc) -> forall a. Params a -> Params a
Params (forall a. Value a -> Params a
Params.value Value a
valueEnc)
  Nullable (Value Value a
valueEnc) -> forall a. Params a -> Params a
Params (forall a. Value a -> Params (Maybe a)
Params.nullableValue Value a
valueEnc)

-- * Nullability

-- |
-- Extensional specification of nullability over a generic encoder.
data NullableOrNot encoder a where
  NonNullable :: encoder a -> NullableOrNot encoder a
  Nullable :: encoder a -> NullableOrNot encoder (Maybe a)

-- |
-- Specify that an encoder produces a non-nullable value.
nonNullable :: encoder a -> NullableOrNot encoder a
nonNullable :: forall (encoder :: * -> *) a. encoder a -> NullableOrNot encoder a
nonNullable = forall (encoder :: * -> *) a. encoder a -> NullableOrNot encoder a
NonNullable

-- |
-- Specify that an encoder produces a nullable value.
nullable :: encoder a -> NullableOrNot encoder (Maybe a)
nullable :: forall (encoder :: * -> *) a.
encoder a -> NullableOrNot encoder (Maybe a)
nullable = forall (encoder :: * -> *) a.
encoder a -> NullableOrNot encoder (Maybe a)
Nullable

-- * Value

-- |
-- Value encoder.
newtype Value a = Value (Value.Value a)
  deriving (forall b a. b -> Value b -> Value a
forall a' a. (a' -> a) -> Value a -> Value a'
forall (f :: * -> *).
(forall a' a. (a' -> a) -> f a -> f a')
-> (forall b a. b -> f b -> f a) -> Contravariant f
>$ :: forall b a. b -> Value b -> Value a
$c>$ :: forall b a. b -> Value b -> Value a
contramap :: forall a' a. (a' -> a) -> Value a -> Value a'
$ccontramap :: forall a' a. (a' -> a) -> Value a -> Value a'
Contravariant)

-- |
-- Encoder of @BOOL@ values.
{-# INLINEABLE bool #-}
bool :: Value Bool
bool :: Value Bool
bool = forall a. Value a -> Value a
Value (forall a. Show a => PTI -> (Bool -> a -> Encoding) -> Value a
Value.unsafePTIWithShow PTI
PTI.bool (forall a b. a -> b -> a
const Bool -> Encoding
A.bool))

-- |
-- Encoder of @INT2@ values.
{-# INLINEABLE int2 #-}
int2 :: Value Int16
int2 :: Value Int16
int2 = forall a. Value a -> Value a
Value (forall a. Show a => PTI -> (Bool -> a -> Encoding) -> Value a
Value.unsafePTIWithShow PTI
PTI.int2 (forall a b. a -> b -> a
const Int16 -> Encoding
A.int2_int16))

-- |
-- Encoder of @INT4@ values.
{-# INLINEABLE int4 #-}
int4 :: Value Int32
int4 :: Value Int32
int4 = forall a. Value a -> Value a
Value (forall a. Show a => PTI -> (Bool -> a -> Encoding) -> Value a
Value.unsafePTIWithShow PTI
PTI.int4 (forall a b. a -> b -> a
const Int32 -> Encoding
A.int4_int32))

-- |
-- Encoder of @INT8@ values.
{-# INLINEABLE int8 #-}
int8 :: Value Int64
int8 :: Value Int64
int8 = forall a. Value a -> Value a
Value (forall a. Show a => PTI -> (Bool -> a -> Encoding) -> Value a
Value.unsafePTIWithShow PTI
PTI.int8 (forall a b. a -> b -> a
const Int64 -> Encoding
A.int8_int64))

-- |
-- Encoder of @FLOAT4@ values.
{-# INLINEABLE float4 #-}
float4 :: Value Float
float4 :: Value Float
float4 = forall a. Value a -> Value a
Value (forall a. Show a => PTI -> (Bool -> a -> Encoding) -> Value a
Value.unsafePTIWithShow PTI
PTI.float4 (forall a b. a -> b -> a
const Float -> Encoding
A.float4))

-- |
-- Encoder of @FLOAT8@ values.
{-# INLINEABLE float8 #-}
float8 :: Value Double
float8 :: Value Double
float8 = forall a. Value a -> Value a
Value (forall a. Show a => PTI -> (Bool -> a -> Encoding) -> Value a
Value.unsafePTIWithShow PTI
PTI.float8 (forall a b. a -> b -> a
const Double -> Encoding
A.float8))

-- |
-- Encoder of @NUMERIC@ values.
{-# INLINEABLE numeric #-}
numeric :: Value Scientific
numeric :: Value Scientific
numeric = forall a. Value a -> Value a
Value (forall a. Show a => PTI -> (Bool -> a -> Encoding) -> Value a
Value.unsafePTIWithShow PTI
PTI.numeric (forall a b. a -> b -> a
const Scientific -> Encoding
A.numeric))

-- |
-- Encoder of @CHAR@ values.
--
-- Note that it supports Unicode values and
-- identifies itself under the @TEXT@ OID because of that.
{-# INLINEABLE char #-}
char :: Value Char
char :: Value Char
char = forall a. Value a -> Value a
Value (forall a. Show a => PTI -> (Bool -> a -> Encoding) -> Value a
Value.unsafePTIWithShow PTI
PTI.text (forall a b. a -> b -> a
const Char -> Encoding
A.char_utf8))

-- |
-- Encoder of @TEXT@ values.
{-# INLINEABLE text #-}
text :: Value Text
text :: Value Text
text = forall a. Value a -> Value a
Value (forall a. Show a => PTI -> (Bool -> a -> Encoding) -> Value a
Value.unsafePTIWithShow PTI
PTI.text (forall a b. a -> b -> a
const Text -> Encoding
A.text_strict))

-- |
-- Encoder of @BYTEA@ values.
{-# INLINEABLE bytea #-}
bytea :: Value ByteString
bytea :: Value ByteString
bytea = forall a. Value a -> Value a
Value (forall a. Show a => PTI -> (Bool -> a -> Encoding) -> Value a
Value.unsafePTIWithShow PTI
PTI.bytea (forall a b. a -> b -> a
const ByteString -> Encoding
A.bytea_strict))

-- |
-- Encoder of @DATE@ values.
{-# INLINEABLE date #-}
date :: Value Day
date :: Value Day
date = forall a. Value a -> Value a
Value (forall a. Show a => PTI -> (Bool -> a -> Encoding) -> Value a
Value.unsafePTIWithShow PTI
PTI.date (forall a b. a -> b -> a
const Day -> Encoding
A.date))

-- |
-- Encoder of @TIMESTAMP@ values.
{-# INLINEABLE timestamp #-}
timestamp :: Value LocalTime
timestamp :: Value LocalTime
timestamp = forall a. Value a -> Value a
Value (forall a. Show a => PTI -> (Bool -> a -> Encoding) -> Value a
Value.unsafePTIWithShow PTI
PTI.timestamp (forall a. a -> a -> Bool -> a
Prelude.bool LocalTime -> Encoding
A.timestamp_float LocalTime -> Encoding
A.timestamp_int))

-- |
-- Encoder of @TIMESTAMPTZ@ values.
{-# INLINEABLE timestamptz #-}
timestamptz :: Value UTCTime
timestamptz :: Value UTCTime
timestamptz = forall a. Value a -> Value a
Value (forall a. Show a => PTI -> (Bool -> a -> Encoding) -> Value a
Value.unsafePTIWithShow PTI
PTI.timestamptz (forall a. a -> a -> Bool -> a
Prelude.bool UTCTime -> Encoding
A.timestamptz_float UTCTime -> Encoding
A.timestamptz_int))

-- |
-- Encoder of @TIME@ values.
{-# INLINEABLE time #-}
time :: Value TimeOfDay
time :: Value TimeOfDay
time = forall a. Value a -> Value a
Value (forall a. Show a => PTI -> (Bool -> a -> Encoding) -> Value a
Value.unsafePTIWithShow PTI
PTI.time (forall a. a -> a -> Bool -> a
Prelude.bool TimeOfDay -> Encoding
A.time_float TimeOfDay -> Encoding
A.time_int))

-- |
-- Encoder of @TIMETZ@ values.
{-# INLINEABLE timetz #-}
timetz :: Value (TimeOfDay, TimeZone)
timetz :: Value (TimeOfDay, TimeZone)
timetz = forall a. Value a -> Value a
Value (forall a. Show a => PTI -> (Bool -> a -> Encoding) -> Value a
Value.unsafePTIWithShow PTI
PTI.timetz (forall a. a -> a -> Bool -> a
Prelude.bool (TimeOfDay, TimeZone) -> Encoding
A.timetz_float (TimeOfDay, TimeZone) -> Encoding
A.timetz_int))

-- |
-- Encoder of @INTERVAL@ values.
{-# INLINEABLE interval #-}
interval :: Value DiffTime
interval :: Value DiffTime
interval = forall a. Value a -> Value a
Value (forall a. Show a => PTI -> (Bool -> a -> Encoding) -> Value a
Value.unsafePTIWithShow PTI
PTI.interval (forall a. a -> a -> Bool -> a
Prelude.bool DiffTime -> Encoding
A.interval_float DiffTime -> Encoding
A.interval_int))

-- |
-- Encoder of @UUID@ values.
{-# INLINEABLE uuid #-}
uuid :: Value UUID
uuid :: Value UUID
uuid = forall a. Value a -> Value a
Value (forall a. Show a => PTI -> (Bool -> a -> Encoding) -> Value a
Value.unsafePTIWithShow PTI
PTI.uuid (forall a b. a -> b -> a
const UUID -> Encoding
A.uuid))

-- |
-- Encoder of @INET@ values.
{-# INLINEABLE inet #-}
inet :: Value (NetworkIp.NetAddr NetworkIp.IP)
inet :: Value (NetAddr IP)
inet = forall a. Value a -> Value a
Value (forall a. Show a => PTI -> (Bool -> a -> Encoding) -> Value a
Value.unsafePTIWithShow PTI
PTI.inet (forall a b. a -> b -> a
const NetAddr IP -> Encoding
A.inet))

-- |
-- Encoder of @JSON@ values from JSON AST.
{-# INLINEABLE json #-}
json :: Value Aeson.Value
json :: Value Value
json = forall a. Value a -> Value a
Value (forall a. Show a => PTI -> (Bool -> a -> Encoding) -> Value a
Value.unsafePTIWithShow PTI
PTI.json (forall a b. a -> b -> a
const Value -> Encoding
A.json_ast))

-- |
-- Encoder of @JSON@ values from raw JSON.
{-# INLINEABLE jsonBytes #-}
jsonBytes :: Value ByteString
jsonBytes :: Value ByteString
jsonBytes = forall a. Value a -> Value a
Value (forall a. Show a => PTI -> (Bool -> a -> Encoding) -> Value a
Value.unsafePTIWithShow PTI
PTI.json (forall a b. a -> b -> a
const ByteString -> Encoding
A.json_bytes))

-- |
-- Encoder of @JSON@ values from raw JSON as lazy ByteString.
{-# INLINEABLE jsonLazyBytes #-}
jsonLazyBytes :: Value LazyByteString.ByteString
jsonLazyBytes :: Value ByteString
jsonLazyBytes = forall a. Value a -> Value a
Value (forall a. Show a => PTI -> (Bool -> a -> Encoding) -> Value a
Value.unsafePTIWithShow PTI
PTI.json (forall a b. a -> b -> a
const ByteString -> Encoding
A.json_bytes_lazy))

-- |
-- Encoder of @JSONB@ values from JSON AST.
{-# INLINEABLE jsonb #-}
jsonb :: Value Aeson.Value
jsonb :: Value Value
jsonb = forall a. Value a -> Value a
Value (forall a. Show a => PTI -> (Bool -> a -> Encoding) -> Value a
Value.unsafePTIWithShow PTI
PTI.jsonb (forall a b. a -> b -> a
const Value -> Encoding
A.jsonb_ast))

-- |
-- Encoder of @JSONB@ values from raw JSON.
{-# INLINEABLE jsonbBytes #-}
jsonbBytes :: Value ByteString
jsonbBytes :: Value ByteString
jsonbBytes = forall a. Value a -> Value a
Value (forall a. Show a => PTI -> (Bool -> a -> Encoding) -> Value a
Value.unsafePTIWithShow PTI
PTI.jsonb (forall a b. a -> b -> a
const ByteString -> Encoding
A.jsonb_bytes))

-- |
-- Encoder of @JSONB@ values from raw JSON as lazy ByteString.
{-# INLINEABLE jsonbLazyBytes #-}
jsonbLazyBytes :: Value LazyByteString.ByteString
jsonbLazyBytes :: Value ByteString
jsonbLazyBytes = forall a. Value a -> Value a
Value (forall a. Show a => PTI -> (Bool -> a -> Encoding) -> Value a
Value.unsafePTIWithShow PTI
PTI.jsonb (forall a b. a -> b -> a
const ByteString -> Encoding
A.jsonb_bytes_lazy))

-- |
-- Encoder of @OID@ values.
{-# INLINEABLE oid #-}
oid :: Value Int32
oid :: Value Int32
oid = forall a. Value a -> Value a
Value (forall a. Show a => PTI -> (Bool -> a -> Encoding) -> Value a
Value.unsafePTIWithShow PTI
PTI.oid (forall a b. a -> b -> a
const Int32 -> Encoding
A.int4_int32))

-- |
-- Encoder of @NAME@ values.
{-# INLINEABLE name #-}
name :: Value Text
name :: Value Text
name = forall a. Value a -> Value a
Value (forall a. Show a => PTI -> (Bool -> a -> Encoding) -> Value a
Value.unsafePTIWithShow PTI
PTI.name (forall a b. a -> b -> a
const Text -> Encoding
A.text_strict))

-- |
-- Given a function,
-- which maps a value into a textual enum label used on the DB side,
-- produces an encoder of that value.
{-# INLINEABLE enum #-}
enum :: (a -> Text) -> Value a
enum :: forall a. (a -> Text) -> Value a
enum a -> Text
mapping = forall a. Value a -> Value a
Value (forall a.
PTI -> (Bool -> a -> Encoding) -> (a -> Builder) -> Value a
Value.unsafePTI PTI
PTI.text (forall a b. a -> b -> a
const (Text -> Encoding
A.text_strict forall {k} (cat :: k -> k -> *) (b :: k) (c :: k) (a :: k).
Category cat =>
cat b c -> cat a b -> cat a c
. a -> Text
mapping)) (Text -> Builder
C.text forall {k} (cat :: k -> k -> *) (b :: k) (c :: k) (a :: k).
Category cat =>
cat b c -> cat a b -> cat a c
. a -> Text
mapping))

-- |
-- Variation of 'enum' with unknown OID.
-- This function does not identify the type to Postgres,
-- so Postgres must be able to derive the type from context.
-- When you find yourself in such situation just provide an explicit type in the query
-- using the :: operator.
{-# INLINEABLE unknownEnum #-}
unknownEnum :: (a -> Text) -> Value a
unknownEnum :: forall a. (a -> Text) -> Value a
unknownEnum a -> Text
mapping = forall a. Value a -> Value a
Value (forall a.
PTI -> (Bool -> a -> Encoding) -> (a -> Builder) -> Value a
Value.unsafePTI PTI
PTI.binaryUnknown (forall a b. a -> b -> a
const (Text -> Encoding
A.text_strict forall {k} (cat :: k -> k -> *) (b :: k) (c :: k) (a :: k).
Category cat =>
cat b c -> cat a b -> cat a c
. a -> Text
mapping)) (Text -> Builder
C.text forall {k} (cat :: k -> k -> *) (b :: k) (c :: k) (a :: k).
Category cat =>
cat b c -> cat a b -> cat a c
. a -> Text
mapping))

-- |
-- Identifies the value with the PostgreSQL's \"unknown\" type,
-- thus leaving it up to Postgres to infer the actual type of the value.
--
-- The value transimitted is any value encoded in the Postgres' Text data format.
-- For reference, see the
-- <https://www.postgresql.org/docs/10/static/protocol-overview.html#protocol-format-codes Formats and Format Codes>
-- section of the Postgres' documentation.
--
-- __Warning:__ Do not use this as part of composite encoders like 'array' since
-- it is the only encoder that doesn't use the binary format.
{-# INLINEABLE unknown #-}
unknown :: Value ByteString
unknown :: Value ByteString
unknown = forall a. Value a -> Value a
Value (forall a. Show a => PTI -> (Bool -> a -> Encoding) -> Value a
Value.unsafePTIWithShow PTI
PTI.textUnknown (forall a b. a -> b -> a
const ByteString -> Encoding
A.bytea_strict))

-- |
-- Lift an array encoder into a value encoder.
array :: Array a -> Value a
array :: forall a. Array a -> Value a
array (Array (Array.Array OID
valueOID OID
arrayOID Bool -> a -> Array
arrayEncoder a -> Builder
renderer)) =
  let encoder :: Bool -> a -> Encoding
encoder Bool
env a
input = Word32 -> Array -> Encoding
A.array (OID -> Word32
PTI.oidWord32 OID
valueOID) (Bool -> a -> Array
arrayEncoder Bool
env a
input)
   in forall a. Value a -> Value a
Value (forall a.
OID -> OID -> (Bool -> a -> Encoding) -> (a -> Builder) -> Value a
Value.Value OID
arrayOID OID
arrayOID Bool -> a -> Encoding
encoder a -> Builder
renderer)

-- |
-- Lift a composite encoder into a value encoder.
composite :: Composite a -> Value a
composite :: forall a. Composite a -> Value a
composite (Composite a -> Bool -> Composite
encode a -> [Builder]
print) =
  forall a. Value a -> Value a
Value (forall a.
PTI -> (Bool -> a -> Encoding) -> (a -> Builder) -> Value a
Value.unsafePTI PTI
PTI.binaryUnknown Bool -> a -> Encoding
encodeValue a -> Builder
printValue)
  where
    encodeValue :: Bool -> a -> Encoding
encodeValue Bool
idt a
val =
      Composite -> Encoding
A.composite forall a b. (a -> b) -> a -> b
$ a -> Bool -> Composite
encode a
val Bool
idt
    printValue :: a -> Builder
printValue a
val =
      Builder
"ROW (" forall a. Semigroup a => a -> a -> a
<> forall (foldable :: * -> *).
Foldable foldable =>
Builder -> foldable Builder -> Builder
C.intercalate Builder
", " (a -> [Builder]
print a
val) forall a. Semigroup a => a -> a -> a
<> Builder
")"

-- |
-- Lift a value encoder of element into a unidimensional array encoder of a foldable value.
--
-- This function is merely a shortcut to the following expression:
--
-- @
-- ('array' . 'dimension' 'foldl'' . 'element')
-- @
--
-- You can use it like this:
--
-- @
-- vectorOfInts :: Value (Vector Int64)
-- vectorOfInts = 'foldableArray' ('nonNullable' 'int8')
-- @
--
-- Please notice that in case of multidimensional arrays nesting 'foldableArray' encoder
-- won't work. You have to explicitly construct the array encoder using 'array'.
{-# INLINE foldableArray #-}
foldableArray :: Foldable foldable => NullableOrNot Value element -> Value (foldable element)
foldableArray :: forall (foldable :: * -> *) element.
Foldable foldable =>
NullableOrNot Value element -> Value (foldable element)
foldableArray = forall a. Array a -> Value a
array forall {k} (cat :: k -> k -> *) (b :: k) (c :: k) (a :: k).
Category cat =>
cat b c -> cat a b -> cat a c
. forall b c.
(forall a. (a -> b -> a) -> a -> c -> a) -> Array b -> Array c
dimension forall (t :: * -> *) b a.
Foldable t =>
(b -> a -> b) -> b -> t a -> b
foldl' forall {k} (cat :: k -> k -> *) (b :: k) (c :: k) (a :: k).
Category cat =>
cat b c -> cat a b -> cat a c
. forall a. NullableOrNot Value a -> Array a
element

-- * Array

-- |
-- Generic array encoder.
--
-- Here's an example of its usage:
--
-- @
-- someParamsEncoder :: 'Params' [[Int64]]
-- someParamsEncoder = 'param' ('nonNullable' ('array' ('dimension' 'foldl'' ('dimension' 'foldl'' ('element' ('nonNullable' 'int8'))))))
-- @
--
-- Please note that the PostgreSQL @IN@ keyword does not accept an array, but rather a syntactical list of
-- values, thus this encoder is not suited for that. Use a @value = ANY($1)@ condition instead.
newtype Array a = Array (Array.Array a)
  deriving (forall b a. b -> Array b -> Array a
forall a' a. (a' -> a) -> Array a -> Array a'
forall (f :: * -> *).
(forall a' a. (a' -> a) -> f a -> f a')
-> (forall b a. b -> f b -> f a) -> Contravariant f
>$ :: forall b a. b -> Array b -> Array a
$c>$ :: forall b a. b -> Array b -> Array a
contramap :: forall a' a. (a' -> a) -> Array a -> Array a'
$ccontramap :: forall a' a. (a' -> a) -> Array a -> Array a'
Contravariant)

-- |
-- Lifts a 'Value' encoder into an 'Array' encoder.
element :: NullableOrNot Value a -> Array a
element :: forall a. NullableOrNot Value a -> Array a
element = \case
  NonNullable (Value (Value.Value OID
elementOID OID
arrayOID Bool -> a -> Encoding
encoder a -> Builder
renderer)) ->
    forall a. Array a -> Array a
Array (forall a.
OID -> OID -> (Bool -> a -> Encoding) -> (a -> Builder) -> Array a
Array.value OID
elementOID OID
arrayOID Bool -> a -> Encoding
encoder a -> Builder
renderer)
  Nullable (Value (Value.Value OID
elementOID OID
arrayOID Bool -> a -> Encoding
encoder a -> Builder
renderer)) ->
    forall a. Array a -> Array a
Array (forall a.
OID
-> OID
-> (Bool -> a -> Encoding)
-> (a -> Builder)
-> Array (Maybe a)
Array.nullableValue OID
elementOID OID
arrayOID Bool -> a -> Encoding
encoder a -> Builder
renderer)

-- |
-- Encoder of an array dimension,
-- which thus provides support for multidimensional arrays.
--
-- Accepts:
--
-- * An implementation of the left-fold operation,
-- such as @Data.Foldable.'foldl''@,
-- which determines the input value.
--
-- * A component encoder, which can be either another 'dimension' or 'element'.
{-# INLINEABLE dimension #-}
dimension :: (forall a. (a -> b -> a) -> a -> c -> a) -> Array b -> Array c
dimension :: forall b c.
(forall a. (a -> b -> a) -> a -> c -> a) -> Array b -> Array c
dimension forall a. (a -> b -> a) -> a -> c -> a
foldl (Array Array b
imp) = forall a. Array a -> Array a
Array (forall b c.
(forall a. (a -> b -> a) -> a -> c -> a) -> Array b -> Array c
Array.dimension forall a. (a -> b -> a) -> a -> c -> a
foldl Array b
imp)

-- * Composite

-- |
-- Composite or row-types encoder.
data Composite a
  = Composite
      (a -> Bool -> A.Composite)
      (a -> [C.Builder])

instance Contravariant Composite where
  contramap :: forall a' a. (a' -> a) -> Composite a -> Composite a'
contramap a' -> a
f (Composite a -> Bool -> Composite
encode a -> [Builder]
print) =
    forall a.
(a -> Bool -> Composite) -> (a -> [Builder]) -> Composite a
Composite (a -> Bool -> Composite
encode forall {k} (cat :: k -> k -> *) (b :: k) (c :: k) (a :: k).
Category cat =>
cat b c -> cat a b -> cat a c
. a' -> a
f) (a -> [Builder]
print forall {k} (cat :: k -> k -> *) (b :: k) (c :: k) (a :: k).
Category cat =>
cat b c -> cat a b -> cat a c
. a' -> a
f)

instance Divisible Composite where
  divide :: forall a b c.
(a -> (b, c)) -> Composite b -> Composite c -> Composite a
divide a -> (b, c)
f (Composite b -> Bool -> Composite
encodeL b -> [Builder]
printL) (Composite c -> Bool -> Composite
encodeR c -> [Builder]
printR) =
    forall a.
(a -> Bool -> Composite) -> (a -> [Builder]) -> Composite a
Composite
      (\a
val Bool
idt -> case a -> (b, c)
f a
val of (b
lVal, c
rVal) -> b -> Bool -> Composite
encodeL b
lVal Bool
idt forall a. Semigroup a => a -> a -> a
<> c -> Bool -> Composite
encodeR c
rVal Bool
idt)
      (\a
val -> case a -> (b, c)
f a
val of (b
lVal, c
rVal) -> b -> [Builder]
printL b
lVal forall a. Semigroup a => a -> a -> a
<> c -> [Builder]
printR c
rVal)
  conquer :: forall a. Composite a
conquer = forall a. Monoid a => a
mempty

instance Semigroup (Composite a) where
  Composite a -> Bool -> Composite
encodeL a -> [Builder]
printL <> :: Composite a -> Composite a -> Composite a
<> Composite a -> Bool -> Composite
encodeR a -> [Builder]
printR =
    forall a.
(a -> Bool -> Composite) -> (a -> [Builder]) -> Composite a
Composite
      (\a
val Bool
idt -> a -> Bool -> Composite
encodeL a
val Bool
idt forall a. Semigroup a => a -> a -> a
<> a -> Bool -> Composite
encodeR a
val Bool
idt)
      (\a
val -> a -> [Builder]
printL a
val forall a. Semigroup a => a -> a -> a
<> a -> [Builder]
printR a
val)

instance Monoid (Composite a) where
  mempty :: Composite a
mempty = forall a.
(a -> Bool -> Composite) -> (a -> [Builder]) -> Composite a
Composite forall a. Monoid a => a
mempty forall a. Monoid a => a
mempty

-- | Single field of a row-type.
field :: NullableOrNot Value a -> Composite a
field :: forall a. NullableOrNot Value a -> Composite a
field = \case
  NonNullable (Value (Value.Value OID
elementOID OID
arrayOID Bool -> a -> Encoding
encode a -> Builder
print)) ->
    forall a.
(a -> Bool -> Composite) -> (a -> [Builder]) -> Composite a
Composite
      (\a
val Bool
idt -> Word32 -> Encoding -> Composite
A.field (OID -> Word32
PTI.oidWord32 OID
elementOID) (Bool -> a -> Encoding
encode Bool
idt a
val))
      (\a
val -> [a -> Builder
print a
val])
  Nullable (Value (Value.Value OID
elementOID OID
arrayOID Bool -> a -> Encoding
encode a -> Builder
print)) ->
    forall a.
(a -> Bool -> Composite) -> (a -> [Builder]) -> Composite a
Composite
      ( \a
val Bool
idt -> case a
val of
          a
Maybe a
Nothing -> Word32 -> Composite
A.nullField (OID -> Word32
PTI.oidWord32 OID
elementOID)
          Just a
val -> Word32 -> Encoding -> Composite
A.field (OID -> Word32
PTI.oidWord32 OID
elementOID) (Bool -> a -> Encoding
encode Bool
idt a
val)
      )
      ( \a
val ->
          case a
val of
            a
Maybe a
Nothing -> [Builder
"NULL"]
            Just a
val -> [a -> Builder
print a
val]
      )