packages feed

sqel-0.0.1.0: lib/Sqel/Prim.hs

module Sqel.Prim where

import Data.Aeson (FromJSON, ToJSON)
import Generics.SOP (I (I), NP (Nil, (:*)))
import Sqel.Class.Mods (MapMod, SymNP, setMod, symMods)
import Sqel.Column (nullable)
import Sqel.Data.Dd (ConCol, Dd (Dd), DdK (DdK), DdStruct (DdPrim), DdType, Struct (Prim))
import Sqel.Data.MigrationParams (
  MigrationDefault (MigrationDefault),
  MigrationDelete (MigrationDelete),
  MigrationRename (MigrationRename),
  MigrationRenameType (MigrationRenameType),
  )
import Sqel.Data.Mods (
  ArrayColumn (ArrayColumn),
  EnumColumn,
  Ignore (Ignore),
  Mods (Mods),
  Newtype (Newtype),
  pattern NoMods,
  type NoMods,
  Nullable,
  ReadShowColumn,
  )
import Sqel.Data.PgType (PgPrimName)
import Sqel.Data.Sel (
  IndexName,
  Sel (SelAuto, SelIndex, SelSymbol, SelUnused),
  SelPrefix (DefaultPrefix),
  SelW (SelWAuto, SelWIndex),
  )
import Sqel.Mods (PrimValueCodec, primEnumMods, primJsonMods, primReadShowMods)
import Sqel.Names (named, selAs)
import Sqel.SOP.Constraint (ProductGCode)
import Sqel.SOP.Error (Quoted)
import Sqel.SOP.Newtype (UnwrapNewtype (unwrapNewtype, wrapNewtype))

type IndexColumnWith prefix name =
  'DdK ('SelIndex prefix name) NoMods Int64 'Prim

type IndexColumn name =
  IndexColumnWith 'DefaultPrefix name

column :: Mods p -> Dd ('DdK 'SelAuto p a 'Prim)
column m =
  Dd SelWAuto m DdPrim

mods ::
  SymNP p ps =>
  p ->
  Mods ps
mods =
  symMods

primMod ::
  p ->
  Dd ('DdK 'SelAuto '[p] a 'Prim)
primMod p =
  column (Mods (I p :* Nil))

primMods ::
  SymNP p ps =>
  p ->
  Dd ('DdK 'SelAuto ps a 'Prim)
primMods p =
  column (mods p)

prim ::
  ∀ a .
  Dd ('DdK 'SelAuto NoMods a 'Prim)
prim =
  column NoMods

ignore ::
  ∀ a .
  Dd ('DdK 'SelUnused '[Ignore] a 'Prim)
ignore =
  selAs (primMod Ignore)

type NewtypeError =
  Quoted "primNewtype" <> " declares a column for a newtype using " <> Quoted "Generic" <> "."

primNewtype ::
  ∀ a w err .
  err ~ NewtypeError =>
  UnwrapNewtype err a w =>
  Dd ('DdK 'SelAuto '[Newtype a w] a 'Prim)
primNewtype =
  primMod (Newtype (unwrapNewtype @err) (wrapNewtype @err))

primCoerce ::
  ∀ a w .
  Coercible a w =>
  Dd ('DdK 'SelAuto '[Newtype a w] a 'Prim)
primCoerce =
  primMod (Newtype coerce coerce)

primIndex ::
  ∀ tpe name .
  IndexName 'DefaultPrefix tpe name =>
  Dd (IndexColumn tpe)
primIndex =
  Dd (SelWIndex Proxy) NoMods DdPrim

-- TODO move aeson to reify
json ::
  ∀ a .
  ToJSON a =>
  FromJSON a =>
  Dd ('DdK 'SelAuto [PgPrimName, PrimValueCodec a] a 'Prim)
json =
  column primJsonMods

enum ::
  ∀ a .
  Dd ('DdK 'SelAuto [PgPrimName, EnumColumn] a 'Prim)
enum =
  column primEnumMods

readShow ::
  ∀ a .
  Dd ('DdK 'SelAuto [PgPrimName, ReadShowColumn] a 'Prim)
readShow =
  column primReadShowMods

primNullable ::
  ∀ a .
  Dd ('DdK 'SelAuto '[Nullable] (Maybe a) 'Prim)
primNullable =
  nullable (prim @a)

primAs ::
  ∀ name a .
  KnownSymbol name =>
  Dd ('DdK ('SelSymbol name) '[] a 'Prim)
primAs =
  named @name (prim @a)

-- TODO are composite arrays legal?
array ::
  ∀ f a p sel .
  Dd ('DdK sel p a 'Prim) ->
  Dd ('DdK sel (ArrayColumn f : p) (f a) 'Prim)
array (Dd sel (Mods p) s) =
  Dd sel (Mods (I ArrayColumn :* p)) s

migrateDef ::
  ∀ s0 s1 .
  MapMod (MigrationDefault (DdType s0)) s0 s1 =>
  DdType s0 ->
  Dd s0 ->
  Dd s1
migrateDef a =
  setMod (MigrationDefault a)

migrateRename ::
  ∀ name s0 s1 .
  MapMod (MigrationRename name) s0 s1 =>
  Dd s0 ->
  Dd s1
migrateRename =
  setMod (MigrationRename @name)

migrateRenameType ::
  ∀ name s0 s1 .
  MapMod (MigrationRenameType name) s0 s1 =>
  Dd s0 ->
  Dd s1
migrateRenameType =
  setMod (MigrationRenameType @name)

migrateDelete ::
  ∀ s0 s1 .
  MapMod MigrationDelete s0 s1 =>
  Dd s0 ->
  Dd s1
migrateDelete =
  setMod MigrationDelete

newtype Prims a s =
  Prims { unPrims :: NP Dd s }
  deriving stock (Generic)

class MkPrims as s | as -> s where
  mkPrims :: NP Dd s

instance MkPrims '[] '[] where
  mkPrims = Nil

instance (
    MkPrims as s
  ) => MkPrims (a : as) ('DdK 'SelAuto '[] a 'Prim : s) where
    mkPrims = prim :* mkPrims @as @s

type family PrimProd (a :: Type) :: [Type] where
  PrimProd (ConCol _ _ _ as) = as
  PrimProd a = ProductGCode a

prims ::
  ∀ (a :: Type) (s :: [DdK]) .
  MkPrims (PrimProd a) s =>
  Prims a s
prims =
  Prims (mkPrims @(PrimProd a))

class MkPrimNewtypes as s | as -> s where
  mkPrimNewtypes :: NP Dd s

instance MkPrimNewtypes '[] '[] where
  mkPrimNewtypes = Nil

instance (
    MkPrimNewtypes as s,
    err ~ NewtypeError,
    UnwrapNewtype err a w
  ) => MkPrimNewtypes (a : as) ('DdK 'SelAuto '[Newtype a w] a 'Prim : s) where
    mkPrimNewtypes = primNewtype :* mkPrimNewtypes @as @s

primNewtypes ::
  ∀ (a :: Type) (s :: [DdK]) .
  MkPrimNewtypes (PrimProd a) s =>
  Prims a s
primNewtypes =
  Prims (mkPrimNewtypes @(PrimProd a))