packages feed

sqel-0.0.1.0: lib/Sqel/Codec/PrimDecoder.hs

module Sqel.Codec.PrimDecoder where

import qualified Chronos as Chronos
import qualified Data.Map.Strict as Map
import Data.Scientific (Scientific)
import qualified Data.Set as Set
import Data.Time (Day, DiffTime, LocalTime (LocalTime), TimeOfDay (TimeOfDay), TimeZone, UTCTime, toModifiedJulianDay)
import Data.UUID (UUID)
import Data.Vector (Vector)
import Hasql.Decoders (
  Value,
  bool,
  bytea,
  char,
  custom,
  date,
  enum,
  float4,
  float8,
  int2,
  int4,
  int8,
  interval,
  listArray,
  nonNullable,
  numeric,
  refine,
  text,
  time,
  timestamp,
  timestamptz,
  timetz,
  uuid,
  vectorArray,
  )
import Path (Abs, Dir, File, Path, Rel, parseAbsDir, parseAbsFile, parseRelDir, parseRelFile)
import Prelude hiding (Enum, bool)

import Sqel.SOP.Enum (EnumTable (enumTable))

class PrimDecoder a where
  primDecoder :: Value a

instance PrimDecoder () where
  primDecoder =
    void bool

instance PrimDecoder Bool where
  primDecoder =
    bool

instance PrimDecoder Int16 where
  primDecoder =
    int2

instance PrimDecoder Int32 where
  primDecoder =
    int4

instance PrimDecoder Int64 where
  primDecoder =
    int8

instance PrimDecoder Int where
  primDecoder =
    fromIntegral <$> int8

instance PrimDecoder Float where
  primDecoder =
    float4

instance PrimDecoder Double where
  primDecoder =
    float8

instance PrimDecoder Scientific where
  primDecoder =
    numeric

instance PrimDecoder Char where
  primDecoder =
    char

instance PrimDecoder Text where
  primDecoder =
    text

instance PrimDecoder ByteString where
  primDecoder =
    bytea

instance PrimDecoder Day where
  primDecoder =
    date

instance PrimDecoder LocalTime where
  primDecoder =
    timestamp

instance PrimDecoder UTCTime where
  primDecoder =
    timestamptz

instance PrimDecoder TimeOfDay where
  primDecoder =
    time

instance PrimDecoder (TimeOfDay, TimeZone) where
  primDecoder =
    timetz

instance PrimDecoder DiffTime where
  primDecoder =
    interval

instance PrimDecoder UUID where
  primDecoder =
    uuid

decodePath ::
  Show e =>
  (String -> Either e (Path b t)) ->
  Bool ->
  ByteString ->
  Either Text (Path b t)
decodePath parse _ =
  first show . parse . decodeUtf8

instance PrimDecoder (Path Abs File) where
  primDecoder =
    custom (decodePath parseAbsFile)

instance PrimDecoder (Path Abs Dir) where
  primDecoder =
    custom (decodePath parseAbsDir)

instance PrimDecoder (Path Rel File) where
  primDecoder =
    custom (decodePath parseRelFile)

instance PrimDecoder (Path Rel Dir) where
  primDecoder =
    custom (decodePath parseRelDir)

dayToChronos :: Day -> Chronos.Date
dayToChronos =
  Chronos.dayToDate . Chronos.Day . fromIntegral . toModifiedJulianDay

instance PrimDecoder Chronos.Date where
  primDecoder =
    dayToChronos <$> date

instance PrimDecoder Chronos.Time where
  primDecoder =
    Chronos.Time <$> int8

chronosToTimeOfDay :: TimeOfDay -> Chronos.TimeOfDay
chronosToTimeOfDay (TimeOfDay h m ns) =
  Chronos.TimeOfDay h m (round (ns * 1000000000))

localTimeToDatetime :: LocalTime -> Chronos.Datetime
localTimeToDatetime (LocalTime d t) =
  Chronos.Datetime (dayToChronos d) (chronosToTimeOfDay t)

instance PrimDecoder Chronos.Datetime where
  primDecoder =
    localTimeToDatetime <$> primDecoder

class ArrayDecoder f a where
  arrayDecoder :: Value a -> Value (f a)

instance ArrayDecoder [] a where
  arrayDecoder =
    listArray . nonNullable

instance ArrayDecoder NonEmpty a where
  arrayDecoder =
    refine (maybeToRight "no elements in NonEmpty field" . nonEmpty) .
    listArray .
    nonNullable

instance ArrayDecoder Vector a where
  arrayDecoder =
    vectorArray . nonNullable

instance (
    Ord a
  ) => ArrayDecoder Set a where
  arrayDecoder =
    refine (Right . Set.fromList) .
    listArray .
    nonNullable

enumDecoder ::
  EnumTable a =>
  Value a
enumDecoder =
  enum (`Map.lookup` enumTable)

readDecoder ::
  Read a =>
  Value a
readDecoder =
  enum (readMaybe . fromText)