packages feed

hasql-th-0.5: src/library/Hasql/TH/Extraction/Exp.hs

module Hasql.TH.Extraction.Exp where

import qualified Hasql.Decoders as Decoders
import qualified Hasql.Encoders as Encoders
import qualified Hasql.TH.Construction.Exp as Exp
import qualified Hasql.TH.Extraction.InputTypeList as InputTypeList
import qualified Hasql.TH.Extraction.OutputTypeList as OutputTypeList
import qualified Hasql.TH.Extraction.PrimitiveType as PrimitiveType
import Hasql.TH.Prelude
import Language.Haskell.TH
import qualified PostgresqlSyntax.Ast as Ast
import qualified PostgresqlSyntax.Rendering as Rendering

undecodedStatement :: (Exp -> Exp) -> Ast.PreparableStmt -> Either Text Exp
undecodedStatement decoderProj ast =
  let sql = (Exp.text . Rendering.toText . Rendering.preparableStmt) ast
   in do
        encoder <- paramsEncoder ast
        rowDecoder' <- rowDecoder ast
        return (Exp.statement sql encoder (decoderProj rowDecoder'))

foldStatement :: Ast.PreparableStmt -> Either Text Exp
foldStatement ast =
  let sql = (Exp.text . Rendering.toText . Rendering.preparableStmt) ast
   in do
        encoder <- paramsEncoder ast
        rowDecoder' <- rowDecoder ast
        return (Exp.foldStatement sql encoder rowDecoder')

paramsEncoder :: Ast.PreparableStmt -> Either Text Exp
paramsEncoder a = do
  b <- InputTypeList.preparableStmt a
  c <- traverse paramEncoder b
  return (Exp.contrazip c)

rowDecoder :: Ast.PreparableStmt -> Either Text Exp
rowDecoder a = do
  b <- OutputTypeList.preparableStmt a
  c <- traverse columnDecoder b
  return (Exp.cozip c)

paramEncoder :: Ast.Typename -> Either Text Exp
paramEncoder =
  byTypename
    (\a b -> valueEncoder a & fmap (Exp.unidimensionalParamEncoder b))
    (\a b c d -> valueEncoder a & fmap (Exp.multidimensionalParamEncoder b c d))

columnDecoder :: Ast.Typename -> Either Text Exp
columnDecoder =
  byTypename
    (\a b -> valueDecoder a & fmap (Exp.unidimensionalColumnDecoder b))
    (\a b c d -> valueDecoder a & fmap (Exp.multidimensionalColumnDecoder b c d))

byTypename :: (PrimitiveType.PrimitiveType -> Bool -> Either Text Exp) -> (PrimitiveType.PrimitiveType -> Bool -> Int -> Bool -> Either Text Exp) -> Ast.Typename -> Either Text Exp
byTypename unidimensional multidimensional (Ast.Typename a b c d) =
  if a
    then Left "SETOF is not supported"
    else do
      e <- PrimitiveType.simpleTypename b
      case d of
        Nothing -> unidimensional e c
        Just (f, g) -> case f of
          Ast.BoundsTypenameArrayDimensions h -> multidimensional e c (length h) g
          Ast.ExplicitTypenameArrayDimensions _ -> multidimensional e c 1 g

valueEncoder :: PrimitiveType.PrimitiveType -> Either Text Exp
valueEncoder =
  Right . VarE . \case
    PrimitiveType.BoolPrimitiveType -> 'Encoders.bool
    PrimitiveType.Int2PrimitiveType -> 'Encoders.int2
    PrimitiveType.Int4PrimitiveType -> 'Encoders.int4
    PrimitiveType.Int8PrimitiveType -> 'Encoders.int8
    PrimitiveType.Float4PrimitiveType -> 'Encoders.float4
    PrimitiveType.Float8PrimitiveType -> 'Encoders.float8
    PrimitiveType.NumericPrimitiveType -> 'Encoders.numeric
    PrimitiveType.CharPrimitiveType -> 'Encoders.char
    PrimitiveType.TextPrimitiveType -> 'Encoders.text
    PrimitiveType.ByteaPrimitiveType -> 'Encoders.bytea
    PrimitiveType.DatePrimitiveType -> 'Encoders.date
    PrimitiveType.TimestampPrimitiveType -> 'Encoders.timestamp
    PrimitiveType.TimestamptzPrimitiveType -> 'Encoders.timestamptz
    PrimitiveType.TimePrimitiveType -> 'Encoders.time
    PrimitiveType.TimetzPrimitiveType -> 'Encoders.timetz
    PrimitiveType.IntervalPrimitiveType -> 'Encoders.interval
    PrimitiveType.UuidPrimitiveType -> 'Encoders.uuid
    PrimitiveType.InetPrimitiveType -> 'Encoders.inet
    PrimitiveType.JsonPrimitiveType -> 'Encoders.json
    PrimitiveType.JsonbPrimitiveType -> 'Encoders.jsonb

valueDecoder :: PrimitiveType.PrimitiveType -> Either Text Exp
valueDecoder =
  Right . VarE . \case
    PrimitiveType.BoolPrimitiveType -> 'Decoders.bool
    PrimitiveType.Int2PrimitiveType -> 'Decoders.int2
    PrimitiveType.Int4PrimitiveType -> 'Decoders.int4
    PrimitiveType.Int8PrimitiveType -> 'Decoders.int8
    PrimitiveType.Float4PrimitiveType -> 'Decoders.float4
    PrimitiveType.Float8PrimitiveType -> 'Decoders.float8
    PrimitiveType.NumericPrimitiveType -> 'Decoders.numeric
    PrimitiveType.CharPrimitiveType -> 'Decoders.char
    PrimitiveType.TextPrimitiveType -> 'Decoders.text
    PrimitiveType.ByteaPrimitiveType -> 'Decoders.bytea
    PrimitiveType.DatePrimitiveType -> 'Decoders.date
    PrimitiveType.TimestampPrimitiveType -> 'Decoders.timestamp
    PrimitiveType.TimestamptzPrimitiveType -> 'Decoders.timestamptz
    PrimitiveType.TimePrimitiveType -> 'Decoders.time
    PrimitiveType.TimetzPrimitiveType -> 'Decoders.timetz
    PrimitiveType.IntervalPrimitiveType -> 'Decoders.interval
    PrimitiveType.UuidPrimitiveType -> 'Decoders.uuid
    PrimitiveType.InetPrimitiveType -> 'Decoders.inet
    PrimitiveType.JsonPrimitiveType -> 'Decoders.json
    PrimitiveType.JsonbPrimitiveType -> 'Decoders.jsonb