# LANGUAGE FlexibleInstances #
# LANGUAGE ScopedTypeVariables #
# LANGUAGE TypeApplications #
# LANGUAGE UndecidableInstances #

module Hasql.Interpolate.Internal.Encoder
  ( EncodeValue (..),
    EncodeField (..),
  )
where

import Data.Int
import Data.Scientific (Scientific)
import Data.Text (Text)
import Data.Time (Day, DiffTime, LocalTime, UTCTime)
import Data.UUID (UUID)
import Data.Vector (Vector)
import Hasql.Encoders

-- | This type class determines which encoder we will apply to a field
-- by its type.
--
-- ==== __Example__
--
-- @
--
-- data ThreatLevel = None | Midnight
--
  instance EncodeValue ThreatLevel where
--   encodeValue = enum \\case
--     None     -> "none"
      Midnight - > " midnight "
-- @
class EncodeValue a where
  encodeValue :: Value a

-- | Encode a list as a postgres array using 'foldableArray'
instance EncodeField a => EncodeValue [a] where
  encodeValue = foldableArray encodeField

-- | Encode a 'Vector' as a postgres array using 'foldableArray'
instance EncodeField a => EncodeValue (Vector a) where
  encodeValue = foldableArray encodeField

-- | Encode a 'Bool' as a postgres @boolean@ using 'bool'
instance EncodeValue Bool where
  encodeValue = bool

-- | Encode a 'Text' as a postgres @text@ using 'text'
instance EncodeValue Text where
  encodeValue = text

-- | Encode a 'Int16' as a postgres @int2@ using 'int2'
instance EncodeValue Int16 where
  encodeValue = int2

  | Encode a ' Int32 ' as a postgres @int4@ using ' int4 '
instance EncodeValue Int32 where
  encodeValue = int4

  | Encode a ' Int64 ' as a postgres  using ' int8 '
instance EncodeValue Int64 where
  encodeValue = int8

  | Encode a ' Float ' as a postgres @float4@ using ' float4 '
instance EncodeValue Float where
  encodeValue = float4

  | Encode a ' Double ' as a postgres @float8@ using '  '
instance EncodeValue Double where
  encodeValue = float8

  | Encode a '  ' as a postgres @char@ using ' char '
instance EncodeValue Char where
  encodeValue = char

  | Encode a ' Day ' as a postgres @date@ using ' date '
instance EncodeValue Day where
  encodeValue = date

  | Encode a ' LocalTime ' as a postgres @timestamp@ using ' timestamp '
instance EncodeValue LocalTime where
  encodeValue = timestamp

-- | Encode a 'UTCTime' as a postgres @timestamptz@ using 'timestamptz'
instance EncodeValue UTCTime where
  encodeValue = timestamptz

  | Encode a ' Scientific ' as a postgres @numeric@ using ' numeric '
instance EncodeValue Scientific where
  encodeValue = numeric

-- | Encode a 'DiffTime' as a postgres @interval@ using 'interval'
instance EncodeValue DiffTime where
  encodeValue = interval

-- | Encode a 'UUID' as a postgres @uuid@ using 'uuid'
instance EncodeValue UUID where
  encodeValue = uuid

  | You do not need to define instances for this class ; The two
-- instances exported here cover all uses. The class only exists to
  lift ' Value ' to hasql 's ' NullableOrNot ' GADT .
class EncodeField a where
  encodeField :: NullableOrNot Value a

-- | Overlappable instance for all non-nullable types.
instance {-# OVERLAPPABLE #-} EncodeValue a => EncodeField a where
  encodeField = nonNullable encodeValue

-- | Instance for all nullable types. 'Nothing' is encoded as @null@.
instance EncodeValue a => EncodeField (Maybe a) where
  encodeField = nullable encodeValue

 | This type class determines which encoder we will apply to a field
 by its type.

 ==== __Example__

 @

 data ThreatLevel = None | Midnight

   encodeValue = enum \\case
     None     -> "none"
 @
 | Encode a list as a postgres array using 'foldableArray'
 | Encode a 'Vector' as a postgres array using 'foldableArray'
 | Encode a 'Bool' as a postgres @boolean@ using 'bool'
 | Encode a 'Text' as a postgres @text@ using 'text'
 | Encode a 'Int16' as a postgres @int2@ using 'int2'
 | Encode a 'UTCTime' as a postgres @timestamptz@ using 'timestamptz'
 | Encode a 'DiffTime' as a postgres @interval@ using 'interval'
 | Encode a 'UUID' as a postgres @uuid@ using 'uuid'
 instances exported here cover all uses. The class only exists to
 | Overlappable instance for all non-nullable types.
# OVERLAPPABLE #
 | Instance for all nullable types. 'Nothing' is encoded as @null@.
# LANGUAGE FlexibleInstances #
# LANGUAGE ScopedTypeVariables #
# LANGUAGE TypeApplications #
# LANGUAGE UndecidableInstances #

module Hasql.Interpolate.Internal.Encoder
  ( EncodeValue (..),
    EncodeField (..),
  )
where

import Data.Int
import Data.Scientific (Scientific)
import Data.Text (Text)
import Data.Time (Day, DiffTime, LocalTime, UTCTime)
import Data.UUID (UUID)
import Data.Vector (Vector)
import Hasql.Encoders

  instance EncodeValue ThreatLevel where
      Midnight - > " midnight "
class EncodeValue a where
  encodeValue :: Value a

instance EncodeField a => EncodeValue [a] where
  encodeValue = foldableArray encodeField

instance EncodeField a => EncodeValue (Vector a) where
  encodeValue = foldableArray encodeField

instance EncodeValue Bool where
  encodeValue = bool

instance EncodeValue Text where
  encodeValue = text

instance EncodeValue Int16 where
  encodeValue = int2

  | Encode a ' Int32 ' as a postgres @int4@ using ' int4 '
instance EncodeValue Int32 where
  encodeValue = int4

  | Encode a ' Int64 ' as a postgres  using ' int8 '
instance EncodeValue Int64 where
  encodeValue = int8

  | Encode a ' Float ' as a postgres @float4@ using ' float4 '
instance EncodeValue Float where
  encodeValue = float4

  | Encode a ' Double ' as a postgres @float8@ using '  '
instance EncodeValue Double where
  encodeValue = float8

  | Encode a '  ' as a postgres @char@ using ' char '
instance EncodeValue Char where
  encodeValue = char

  | Encode a ' Day ' as a postgres @date@ using ' date '
instance EncodeValue Day where
  encodeValue = date

  | Encode a ' LocalTime ' as a postgres @timestamp@ using ' timestamp '
instance EncodeValue LocalTime where
  encodeValue = timestamp

instance EncodeValue UTCTime where
  encodeValue = timestamptz

  | Encode a ' Scientific ' as a postgres @numeric@ using ' numeric '
instance EncodeValue Scientific where
  encodeValue = numeric

instance EncodeValue DiffTime where
  encodeValue = interval

instance EncodeValue UUID where
  encodeValue = uuid

  | You do not need to define instances for this class ; The two
  lift ' Value ' to hasql 's ' NullableOrNot ' GADT .
class EncodeField a where
  encodeField :: NullableOrNot Value a

  encodeField = nonNullable encodeValue

instance EncodeValue a => EncodeField (Maybe a) where
  encodeField = nullable encodeValue
