module Sqel.Codec where

import qualified Chronos as Chronos
import Data.Time (Day, DiffTime, LocalTime, TimeOfDay, TimeZone, UTCTime)
import Data.UUID (UUID)
import qualified Hasql.Decoders as Hasql
import qualified Hasql.Decoders as Decoders
import Hasql.Decoders (Row, custom)
import qualified Hasql.Encoders as Encoders
import Hasql.Encoders (Params)
import Path (Path)

import qualified Sqel.Codec.PrimDecoder as PrimDecoder
import Sqel.Codec.PrimDecoder (PrimDecoder)
import qualified Sqel.Codec.PrimEncoder as PrimEncoder
import Sqel.Codec.PrimEncoder (PrimEncoder)
import Sqel.Codec.Sum (ignoreEncoder)
import qualified Sqel.Data.Codec as Codec
import Sqel.Data.Codec (Codec (Codec), Decoder (Decoder), Encoder (Encoder), FullCodec, ValueCodec)
import Sqel.Data.PgType (PgPrimName)
import Sqel.SOP.Error (Quoted, QuotedType)

ignoreDecoder :: Row (Maybe a)
ignoreDecoder :: forall a. Row (Maybe a)
ignoreDecoder =
  forall (m :: * -> *) a. Monad m => m (m a) -> m a
join forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> forall a. NullableOrNot Value a -> Row a
Hasql.column (forall (decoder :: * -> *) a.
decoder a -> NullableOrNot decoder (Maybe a)
Hasql.nullable (forall a. (Bool -> ByteString -> Either Text a) -> Value a
custom \ Bool
_ ByteString
_ -> forall (f :: * -> *) a. Applicative f => a -> f a
pure forall a. Maybe a
Nothing))

class ColumnEncoder f where
  columnEncoder :: f a -> Params a
  columnEncoderNullable :: f a -> Params (Maybe a)
  columnEncoderIgnore :: f a -> Params b

instance ColumnEncoder Encoders.Value where
  columnEncoder :: forall a. Value a -> Params a
columnEncoder =
    forall a. NullableOrNot Value a -> Params a
Encoders.param forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall (encoder :: * -> *) a. encoder a -> NullableOrNot encoder a
Encoders.nonNullable
  columnEncoderNullable :: forall a. Value a -> Params (Maybe a)
columnEncoderNullable =
    forall a. NullableOrNot Value a -> Params a
Encoders.param forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall (encoder :: * -> *) a.
encoder a -> NullableOrNot encoder (Maybe a)
Encoders.nullable
  columnEncoderIgnore :: forall a b. Value a -> Params b
columnEncoderIgnore =
    forall a b. Value a -> Params b
ignoreEncoder

class ColumnDecoder f where
  columnDecoder :: f a -> Row a
  columnDecoderNullable :: f a -> Row (Maybe a)

instance ColumnDecoder Decoders.Value where
  columnDecoder :: forall a. Value a -> Row a
columnDecoder =
    forall a. NullableOrNot Value a -> Row a
Decoders.column forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall (decoder :: * -> *) a. decoder a -> NullableOrNot decoder a
Decoders.nonNullable
  columnDecoderNullable :: forall a. Value a -> Row (Maybe a)
columnDecoderNullable =
    forall a. NullableOrNot Value a -> Row a
Decoders.column forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall (decoder :: * -> *) a.
decoder a -> NullableOrNot decoder (Maybe a)
Decoders.nullable

class PrimColumn a where
  primDecoder :: Decoders.Value a
  default primDecoder :: PrimDecoder a => Decoders.Value a
  primDecoder = forall a. PrimDecoder a => Value a
PrimDecoder.primDecoder

  primEncoder :: Encoders.Value a
  default primEncoder :: PrimEncoder a => Encoders.Value a
  primEncoder = forall d. PrimEncoder d => Value d
PrimEncoder.primEncoder

  pgType :: PgPrimName

instance {-# overlappable #-} (
    TypeError (
      "A column of type " <> QuotedType a <> " was declared as primitive," %
      "but there is no instance of " <> Quoted "PrimColumn" <> " for that type." %
      "If it is a newtype, ensure that it has " <> Quoted "Generic" <> " and use " <> Quoted "primNewtype" <> "."
    )
  ) => PrimColumn a where
    primDecoder :: Value a
primDecoder = forall a. HasCallStack => [Char] -> a
error [Char]
"no instance for PrimColumn"
    primEncoder :: Value a
primEncoder = forall a. HasCallStack => [Char] -> a
error [Char]
"no instance for PrimColumn"
    pgType :: PgPrimName
pgType = forall a. HasCallStack => [Char] -> a
error [Char]
"no instance for PrimColumn"

instance PrimColumn Bool where pgType :: PgPrimName
pgType = PgPrimName
"boolean"
instance PrimColumn Int where pgType :: PgPrimName
pgType = PgPrimName
"bigint"
instance PrimColumn Int64 where pgType :: PgPrimName
pgType = PgPrimName
"bigint"
instance PrimColumn Double where pgType :: PgPrimName
pgType = PgPrimName
"double precision"
instance PrimColumn Text where pgType :: PgPrimName
pgType = PgPrimName
"text"
instance PrimColumn ByteString where pgType :: PgPrimName
pgType = PgPrimName
"bytes"
instance PrimColumn UUID where pgType :: PgPrimName
pgType = PgPrimName
"uuid"
instance PrimColumn Day where pgType :: PgPrimName
pgType = PgPrimName
"date"
instance PrimColumn LocalTime where pgType :: PgPrimName
pgType = PgPrimName
"timestamp without time zone"
instance PrimColumn UTCTime where pgType :: PgPrimName
pgType = PgPrimName
"timestamp with time zone"
instance PrimColumn TimeOfDay where pgType :: PgPrimName
pgType = PgPrimName
"time without time zone"
instance PrimColumn (TimeOfDay, TimeZone) where pgType :: PgPrimName
pgType = PgPrimName
"time with time zone"
instance PrimColumn DiffTime where pgType :: PgPrimName
pgType = PgPrimName
"interval"
instance PrimColumn Chronos.Date where pgType :: PgPrimName
pgType = PgPrimName
"date"
instance PrimColumn Chronos.Time where pgType :: PgPrimName
pgType = PgPrimName
"bigint"
instance PrimColumn Chronos.Datetime where pgType :: PgPrimName
pgType = PgPrimName
"timestamp without time zone"
instance PrimDecoder (Path b t) => PrimColumn (Path b t) where pgType :: PgPrimName
pgType = PgPrimName
"text"
instance PrimColumn () where pgType :: PgPrimName
pgType = PgPrimName
"boolean"

fullPrimCodec ::
  Encoders.Value a ->
  Decoders.Value a ->
  FullCodec a
fullPrimCodec :: forall a. Value a -> Value a -> FullCodec a
fullPrimCodec Value a
encoder Value a
decoder =
  Codec {
    $sel:encoder:Codec :: Encoder a
encoder = forall a. Params a -> Params () -> Encoder a
Encoder (forall (f :: * -> *) a. ColumnEncoder f => f a -> Params a
columnEncoder Value a
encoder) (forall (f :: * -> *) a b. ColumnEncoder f => f a -> Params b
columnEncoderIgnore Value a
encoder),
    $sel:decoder:Codec :: Decoder a
decoder = forall a. Row a -> Row () -> Decoder a
Decoder (forall (f :: * -> *) a. ColumnDecoder f => f a -> Row a
columnDecoder Value a
decoder) (forall (f :: * -> *) a. Functor f => f a -> f ()
void forall a. Row (Maybe a)
ignoreDecoder)
  }

toFullPrimCodec ::
  ValueCodec a ->
  FullCodec a
toFullPrimCodec :: forall a. ValueCodec a -> FullCodec a
toFullPrimCodec (Codec Value a
encoder Value a
decoder) =
  forall a. Value a -> Value a -> FullCodec a
fullPrimCodec Value a
encoder Value a
decoder