module Hasql.Codecs.Encoders.Composite where

import Data.HashMap.Strict qualified as HashMap
import Data.HashSet qualified as HashSet
import Hasql.Codecs.Encoders.NullableOrNot qualified as NullableOrNot
import Hasql.Codecs.Encoders.Value qualified as Value
import Hasql.Platform.Prelude hiding (bool)
import PostgreSQL.Binary.Encoding qualified as Binary
import TextBuilder qualified

-- |
-- Composite or row-types encoder.
data Composite a
  = Composite
      -- | Names of types that are not known statically and must be looked up at runtime collected from the nested composite and array encoders.
      (HashSet (Maybe Text, Text))
      -- | Serialization function given the dictionary of resolved OIDs.
      (HashMap (Maybe Text, Text) (Word32, Word32) -> a -> Binary.Composite)
      -- | Render function for error messages.
      (a -> [TextBuilder.TextBuilder])

instance Contravariant Composite where
  contramap :: forall a' a. (a' -> a) -> Composite a -> Composite a'
contramap a' -> a
f (Composite HashSet (Maybe Text, Text)
unknownTypes HashMap (Maybe Text, Text) (Word32, Word32) -> a -> Composite
encode a -> [TextBuilder]
print) =
    HashSet (Maybe Text, Text)
-> (HashMap (Maybe Text, Text) (Word32, Word32) -> a' -> Composite)
-> (a' -> [TextBuilder])
-> Composite a'
forall a.
HashSet (Maybe Text, Text)
-> (HashMap (Maybe Text, Text) (Word32, Word32) -> a -> Composite)
-> (a -> [TextBuilder])
-> Composite a
Composite HashSet (Maybe Text, Text)
unknownTypes (\HashMap (Maybe Text, Text) (Word32, Word32)
oidCache -> HashMap (Maybe Text, Text) (Word32, Word32) -> a -> Composite
encode HashMap (Maybe Text, Text) (Word32, Word32)
oidCache (a -> Composite) -> (a' -> a) -> a' -> Composite
forall b c a. (b -> c) -> (a -> b) -> a -> c
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 -> [TextBuilder]
print (a -> [TextBuilder]) -> (a' -> a) -> a' -> [TextBuilder]
forall b c a. (b -> c) -> (a -> b) -> a -> c
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 HashSet (Maybe Text, Text)
unknownTypesL HashMap (Maybe Text, Text) (Word32, Word32) -> b -> Composite
encodeL b -> [TextBuilder]
printL) (Composite HashSet (Maybe Text, Text)
unknownTypesR HashMap (Maybe Text, Text) (Word32, Word32) -> c -> Composite
encodeR c -> [TextBuilder]
printR) =
    HashSet (Maybe Text, Text)
-> (HashMap (Maybe Text, Text) (Word32, Word32) -> a -> Composite)
-> (a -> [TextBuilder])
-> Composite a
forall a.
HashSet (Maybe Text, Text)
-> (HashMap (Maybe Text, Text) (Word32, Word32) -> a -> Composite)
-> (a -> [TextBuilder])
-> Composite a
Composite
      (HashSet (Maybe Text, Text)
unknownTypesL HashSet (Maybe Text, Text)
-> HashSet (Maybe Text, Text) -> HashSet (Maybe Text, Text)
forall a. Semigroup a => a -> a -> a
<> HashSet (Maybe Text, Text)
unknownTypesR)
      (\HashMap (Maybe Text, Text) (Word32, Word32)
oidCache a
val -> case a -> (b, c)
f a
val of (b
lVal, c
rVal) -> HashMap (Maybe Text, Text) (Word32, Word32) -> b -> Composite
encodeL HashMap (Maybe Text, Text) (Word32, Word32)
oidCache b
lVal Composite -> Composite -> Composite
forall a. Semigroup a => a -> a -> a
<> HashMap (Maybe Text, Text) (Word32, Word32) -> c -> Composite
encodeR HashMap (Maybe Text, Text) (Word32, Word32)
oidCache c
rVal)
      (\a
val -> case a -> (b, c)
f a
val of (b
lVal, c
rVal) -> b -> [TextBuilder]
printL b
lVal [TextBuilder] -> [TextBuilder] -> [TextBuilder]
forall a. Semigroup a => a -> a -> a
<> c -> [TextBuilder]
printR c
rVal)
  conquer :: forall a. Composite a
conquer = Composite a
forall a. Monoid a => a
mempty

instance Semigroup (Composite a) where
  Composite HashSet (Maybe Text, Text)
unknownTypesL HashMap (Maybe Text, Text) (Word32, Word32) -> a -> Composite
encodeL a -> [TextBuilder]
printL <> :: Composite a -> Composite a -> Composite a
<> Composite HashSet (Maybe Text, Text)
unknownTypesR HashMap (Maybe Text, Text) (Word32, Word32) -> a -> Composite
encodeR a -> [TextBuilder]
printR =
    HashSet (Maybe Text, Text)
-> (HashMap (Maybe Text, Text) (Word32, Word32) -> a -> Composite)
-> (a -> [TextBuilder])
-> Composite a
forall a.
HashSet (Maybe Text, Text)
-> (HashMap (Maybe Text, Text) (Word32, Word32) -> a -> Composite)
-> (a -> [TextBuilder])
-> Composite a
Composite
      (HashSet (Maybe Text, Text)
unknownTypesL HashSet (Maybe Text, Text)
-> HashSet (Maybe Text, Text) -> HashSet (Maybe Text, Text)
forall a. Semigroup a => a -> a -> a
<> HashSet (Maybe Text, Text)
unknownTypesR)
      (\HashMap (Maybe Text, Text) (Word32, Word32)
oidCache a
val -> HashMap (Maybe Text, Text) (Word32, Word32) -> a -> Composite
encodeL HashMap (Maybe Text, Text) (Word32, Word32)
oidCache a
val Composite -> Composite -> Composite
forall a. Semigroup a => a -> a -> a
<> HashMap (Maybe Text, Text) (Word32, Word32) -> a -> Composite
encodeR HashMap (Maybe Text, Text) (Word32, Word32)
oidCache a
val)
      (\a
val -> a -> [TextBuilder]
printL a
val [TextBuilder] -> [TextBuilder] -> [TextBuilder]
forall a. Semigroup a => a -> a -> a
<> a -> [TextBuilder]
printR a
val)

instance Monoid (Composite a) where
  mempty :: Composite a
mempty = HashSet (Maybe Text, Text)
-> (HashMap (Maybe Text, Text) (Word32, Word32) -> a -> Composite)
-> (a -> [TextBuilder])
-> Composite a
forall a.
HashSet (Maybe Text, Text)
-> (HashMap (Maybe Text, Text) (Word32, Word32) -> a -> Composite)
-> (a -> [TextBuilder])
-> Composite a
Composite HashSet (Maybe Text, Text)
forall a. Monoid a => a
mempty HashMap (Maybe Text, Text) (Word32, Word32) -> a -> Composite
forall a. Monoid a => a
mempty a -> [TextBuilder]
forall a. Monoid a => a
mempty

-- | Single field of a row-type.
field :: NullableOrNot.NullableOrNot Value.Value a -> Composite a
field :: forall a. NullableOrNot Value a -> Composite a
field = \case
  NullableOrNot.NonNullable (Value.Value Maybe Text
schemaName Text
typeName Maybe Word32
scalarOid Maybe Word32
arrayOid Word
dimensionality Bool
_ HashSet (Maybe Text, Text)
unknownTypes HashMap (Maybe Text, Text) (Word32, Word32) -> a -> Encoding
encode a -> TextBuilder
print) ->
    let staticOid :: Maybe Word32
staticOid = if Word
dimensionality Word -> Word -> Bool
forall a. Eq a => a -> a -> Bool
== Word
0 then Maybe Word32
scalarOid else Maybe Word32
arrayOid
     in case Maybe Word32
staticOid of
          Just Word32
oid ->
            HashSet (Maybe Text, Text)
-> (HashMap (Maybe Text, Text) (Word32, Word32) -> a -> Composite)
-> (a -> [TextBuilder])
-> Composite a
forall a.
HashSet (Maybe Text, Text)
-> (HashMap (Maybe Text, Text) (Word32, Word32) -> a -> Composite)
-> (a -> [TextBuilder])
-> Composite a
Composite
              HashSet (Maybe Text, Text)
unknownTypes
              (\HashMap (Maybe Text, Text) (Word32, Word32)
oidCache a
val -> Word32 -> Encoding -> Composite
Binary.field Word32
oid (HashMap (Maybe Text, Text) (Word32, Word32) -> a -> Encoding
encode HashMap (Maybe Text, Text) (Word32, Word32)
oidCache a
val))
              (\a
val -> [a -> TextBuilder
print a
val])
          Maybe Word32
Nothing ->
            HashSet (Maybe Text, Text)
-> (HashMap (Maybe Text, Text) (Word32, Word32) -> a -> Composite)
-> (a -> [TextBuilder])
-> Composite a
forall a.
HashSet (Maybe Text, Text)
-> (HashMap (Maybe Text, Text) (Word32, Word32) -> a -> Composite)
-> (a -> [TextBuilder])
-> Composite a
Composite
              ((Maybe Text, Text)
-> HashSet (Maybe Text, Text) -> HashSet (Maybe Text, Text)
forall a. (Eq a, Hashable a) => a -> HashSet a -> HashSet a
HashSet.insert (Maybe Text
schemaName, Text
typeName) HashSet (Maybe Text, Text)
unknownTypes)
              ( \HashMap (Maybe Text, Text) (Word32, Word32)
oidCache a
val ->
                  let oid :: Word32
oid = if Word
dimensionality Word -> Word -> Bool
forall a. Eq a => a -> a -> Bool
== Word
0 then Word32
-> ((Word32, Word32) -> Word32) -> Maybe (Word32, Word32) -> Word32
forall b a. b -> (a -> b) -> Maybe a -> b
maybe Word32
0 (Word32, Word32) -> Word32
forall a b. (a, b) -> a
fst ((Maybe Text, Text)
-> HashMap (Maybe Text, Text) (Word32, Word32)
-> Maybe (Word32, Word32)
forall k v. (Eq k, Hashable k) => k -> HashMap k v -> Maybe v
HashMap.lookup (Maybe Text
schemaName, Text
typeName) HashMap (Maybe Text, Text) (Word32, Word32)
oidCache) else Word32
-> ((Word32, Word32) -> Word32) -> Maybe (Word32, Word32) -> Word32
forall b a. b -> (a -> b) -> Maybe a -> b
maybe Word32
0 (Word32, Word32) -> Word32
forall a b. (a, b) -> b
snd ((Maybe Text, Text)
-> HashMap (Maybe Text, Text) (Word32, Word32)
-> Maybe (Word32, Word32)
forall k v. (Eq k, Hashable k) => k -> HashMap k v -> Maybe v
HashMap.lookup (Maybe Text
schemaName, Text
typeName) HashMap (Maybe Text, Text) (Word32, Word32)
oidCache)
                   in Word32 -> Encoding -> Composite
Binary.field Word32
oid (HashMap (Maybe Text, Text) (Word32, Word32) -> a -> Encoding
encode HashMap (Maybe Text, Text) (Word32, Word32)
oidCache a
val)
              )
              (\a
val -> [a -> TextBuilder
print a
val])
  NullableOrNot.Nullable (Value.Value Maybe Text
schemaName Text
typeName Maybe Word32
scalarOid Maybe Word32
arrayOid Word
dimensionality Bool
_ HashSet (Maybe Text, Text)
unknownTypes HashMap (Maybe Text, Text) (Word32, Word32) -> a1 -> Encoding
encode a1 -> TextBuilder
print) ->
    let staticOid :: Maybe Word32
staticOid = if Word
dimensionality Word -> Word -> Bool
forall a. Eq a => a -> a -> Bool
== Word
0 then Maybe Word32
scalarOid else Maybe Word32
arrayOid
     in case Maybe Word32
staticOid of
          Just Word32
oid ->
            HashSet (Maybe Text, Text)
-> (HashMap (Maybe Text, Text) (Word32, Word32) -> a -> Composite)
-> (a -> [TextBuilder])
-> Composite a
forall a.
HashSet (Maybe Text, Text)
-> (HashMap (Maybe Text, Text) (Word32, Word32) -> a -> Composite)
-> (a -> [TextBuilder])
-> Composite a
Composite
              HashSet (Maybe Text, Text)
unknownTypes
              ( \HashMap (Maybe Text, Text) (Word32, Word32)
oidCache -> \case
                  a
Maybe a1
Nothing -> Word32 -> Composite
Binary.nullField Word32
oid
                  Just a1
val -> Word32 -> Encoding -> Composite
Binary.field Word32
oid (HashMap (Maybe Text, Text) (Word32, Word32) -> a1 -> Encoding
encode HashMap (Maybe Text, Text) (Word32, Word32)
oidCache a1
val)
              )
              ( \case
                  a
Maybe a1
Nothing -> [TextBuilder
"NULL"]
                  Just a1
val -> [a1 -> TextBuilder
print a1
val]
              )
          Maybe Word32
Nothing ->
            HashSet (Maybe Text, Text)
-> (HashMap (Maybe Text, Text) (Word32, Word32) -> a -> Composite)
-> (a -> [TextBuilder])
-> Composite a
forall a.
HashSet (Maybe Text, Text)
-> (HashMap (Maybe Text, Text) (Word32, Word32) -> a -> Composite)
-> (a -> [TextBuilder])
-> Composite a
Composite
              ((Maybe Text, Text)
-> HashSet (Maybe Text, Text) -> HashSet (Maybe Text, Text)
forall a. (Eq a, Hashable a) => a -> HashSet a -> HashSet a
HashSet.insert (Maybe Text
schemaName, Text
typeName) HashSet (Maybe Text, Text)
unknownTypes)
              ( \HashMap (Maybe Text, Text) (Word32, Word32)
oidCache -> \case
                  a
Maybe a1
Nothing ->
                    let oid :: Word32
oid = if Word
dimensionality Word -> Word -> Bool
forall a. Eq a => a -> a -> Bool
== Word
0 then Word32
-> ((Word32, Word32) -> Word32) -> Maybe (Word32, Word32) -> Word32
forall b a. b -> (a -> b) -> Maybe a -> b
maybe Word32
0 (Word32, Word32) -> Word32
forall a b. (a, b) -> a
fst ((Maybe Text, Text)
-> HashMap (Maybe Text, Text) (Word32, Word32)
-> Maybe (Word32, Word32)
forall k v. (Eq k, Hashable k) => k -> HashMap k v -> Maybe v
HashMap.lookup (Maybe Text
schemaName, Text
typeName) HashMap (Maybe Text, Text) (Word32, Word32)
oidCache) else Word32
-> ((Word32, Word32) -> Word32) -> Maybe (Word32, Word32) -> Word32
forall b a. b -> (a -> b) -> Maybe a -> b
maybe Word32
0 (Word32, Word32) -> Word32
forall a b. (a, b) -> b
snd ((Maybe Text, Text)
-> HashMap (Maybe Text, Text) (Word32, Word32)
-> Maybe (Word32, Word32)
forall k v. (Eq k, Hashable k) => k -> HashMap k v -> Maybe v
HashMap.lookup (Maybe Text
schemaName, Text
typeName) HashMap (Maybe Text, Text) (Word32, Word32)
oidCache)
                     in Word32 -> Composite
Binary.nullField Word32
oid
                  Just a1
val ->
                    let oid :: Word32
oid = if Word
dimensionality Word -> Word -> Bool
forall a. Eq a => a -> a -> Bool
== Word
0 then Word32
-> ((Word32, Word32) -> Word32) -> Maybe (Word32, Word32) -> Word32
forall b a. b -> (a -> b) -> Maybe a -> b
maybe Word32
0 (Word32, Word32) -> Word32
forall a b. (a, b) -> a
fst ((Maybe Text, Text)
-> HashMap (Maybe Text, Text) (Word32, Word32)
-> Maybe (Word32, Word32)
forall k v. (Eq k, Hashable k) => k -> HashMap k v -> Maybe v
HashMap.lookup (Maybe Text
schemaName, Text
typeName) HashMap (Maybe Text, Text) (Word32, Word32)
oidCache) else Word32
-> ((Word32, Word32) -> Word32) -> Maybe (Word32, Word32) -> Word32
forall b a. b -> (a -> b) -> Maybe a -> b
maybe Word32
0 (Word32, Word32) -> Word32
forall a b. (a, b) -> b
snd ((Maybe Text, Text)
-> HashMap (Maybe Text, Text) (Word32, Word32)
-> Maybe (Word32, Word32)
forall k v. (Eq k, Hashable k) => k -> HashMap k v -> Maybe v
HashMap.lookup (Maybe Text
schemaName, Text
typeName) HashMap (Maybe Text, Text) (Word32, Word32)
oidCache)
                     in Word32 -> Encoding -> Composite
Binary.field Word32
oid (HashMap (Maybe Text, Text) (Word32, Word32) -> a1 -> Encoding
encode HashMap (Maybe Text, Text) (Word32, Word32)
oidCache a1
val)
              )
              ( \case
                  a
Maybe a1
Nothing -> [TextBuilder
"NULL"]
                  Just a1
val -> [a1 -> TextBuilder
print a1
val]
              )