rel8-1.5.0.0: src/Rel8/Type/Decoder.hs
{-# language DerivingStrategies #-}
{-# language DeriveFunctor #-}
{-# language GADTs #-}
{-# language NamedFieldPuns #-}
{-# language StandaloneKindSignatures #-}
module Rel8.Type.Decoder (
Decoder (..),
NullableOrNot (..),
Parser,
parseDecoder,
) where
-- base
import Control.Monad ((>=>))
import Data.Bifunctor (first)
import Data.Kind (Type)
import Prelude
-- bytestring
import Data.ByteString (ByteString)
-- hasql
import qualified Hasql.Decoders as Hasql
-- text
import qualified Data.Text as Text
type Parser :: Type -> Type
type Parser a = ByteString -> Either String a
type Decoder :: Type -> Type
data Decoder a = Decoder
{ binary :: Hasql.Value a
-- ^ How to deserialize from PostgreSQL's binary format.
, parser :: Parser a
-- ^ How to deserialize from PostgreSQL's text format.
, delimiter :: Char
-- ^ The delimiter that is used in PostgreSQL's text format in arrays of
-- this type (this is almost always ',').
}
deriving stock (Functor)
-- | Apply a parser to 'Decoder'.
--
-- This can be used if the data stored in the database should only be subset of
-- a given 'Decoder'. The parser is applied when deserializing rows
-- returned.
parseDecoder :: (a -> Either String b) -> Decoder a -> Decoder b
parseDecoder f Decoder {binary, parser, delimiter} =
Decoder
{ binary = Hasql.refine (first Text.pack . f) binary
, parser = parser >=> f
, delimiter
}
type NullableOrNot :: (Type -> Type) -> Type -> Type
data NullableOrNot decoder a where
NonNullable :: decoder a -> NullableOrNot decoder a
Nullable :: decoder a -> NullableOrNot decoder (Maybe a)