packages feed

sqel-0.0.1.0: lib/Sqel/Data/Migration.hs

module Sqel.Data.Migration where

import Exon (exon)
import Generics.SOP (NP (Nil, (:*)))
import Hasql.Encoders (Params)

import Sqel.Data.Dd ((:>) ((:>)))
import Sqel.Data.PgType (ColumnType, PgColumnName, PgColumns, PgTable)
import Sqel.Data.PgTypeName (PgCompName, PgTableName, PgTypeName)

data ColumnAction where
  AddColumn :: PgColumnName -> ColumnType -> Maybe (a, Params a) -> ColumnAction
  RemoveColumn :: PgColumnName -> ColumnType -> ColumnAction
  RenameColumn :: PgColumnName -> PgColumnName -> ColumnAction
  RenameColumnType :: PgColumnName -> PgCompName -> ColumnAction

instance Show ColumnAction where
  showsPrec d =
    showParen (d > 10) . \case
      AddColumn name tpe _ ->
        [exon|AddColumn #{showsPrec 11 name} #{showsPrec 11 tpe}|]
      RemoveColumn name tpe ->
        [exon|RemoveColumn #{showsPrec 11 name} #{showsPrec 11 tpe}|]
      RenameColumn old new ->
        [exon|RenameColumn #{showsPrec 11 old} #{showsPrec 11 new}|]
      RenameColumnType name new ->
        [exon|RenameColumnType #{showsPrec 11 name} #{showsPrec 11 new}|]

data TypeAction (table :: Bool) where
  ModifyAction :: PgTypeName table -> [ColumnAction] -> TypeAction table
  RenameAction :: PgCompName -> [ColumnAction] -> TypeAction 'False
  AddAction :: PgColumns -> TypeAction 'False

type TableAction = TypeAction 'True
type CompAction = TypeAction 'False

data MigrationActions ext =
  AutoActions {
    table :: TableAction,
    types :: Map PgCompName CompAction
  }
  |
  CustomActions ext
  deriving stock (Generic)

data Mig =
  Mig {
    from :: Type,
    to :: Type,
    effect :: Type -> Type,
    ext :: Type
  }

type Migration :: Mig -> Type
data Migration t where
  Migration :: {
    tableFrom :: PgTable from,
    tableTo :: PgTable to,
    actions :: MigrationActions ext
  } -> Migration ('Mig from to m ext)

type family MigFrom (mig :: Mig) :: Type where
  MigFrom ('Mig from _ _ _) = from

type family MigTo (mig :: Mig) :: Type where
  MigTo ('Mig _ to _ _) = to

type family MigEff (mig :: Mig) :: Type -> Type where
  MigEff ('Mig _ _ m _) = m

type family MigExt (mig :: Mig) :: Type where
  MigExt ('Mig _ _ _ ext) = ext

type UniMigList :: (Type -> Type) -> Type -> [Type] -> [Mig]
type family UniMigList m ext as where
  UniMigList _ _ '[] = '[]
  UniMigList m ext [new, old] = '[ 'Mig old new m ext]
  UniMigList m ext (new : old : as) = 'Mig old new m ext : UniMigList m ext (old : as)

type UniMigs :: (Type -> Type) -> Type -> [Type] -> Type -> [Mig]
type family UniMigs m ext old cur where
  UniMigs _ _ '[] _ = '[]
  UniMigs m ext '[o] cur = '[ 'Mig o cur m ext]
  UniMigs m ext (o : os) cur = 'Mig o cur m ext : UniMigList m ext (o : os)

type Migrations :: (Type -> Type) -> [Mig] -> Type
newtype Migrations m migs =
  Migrations { unMigrations :: NP Migration migs }

type UniMigrations m ext old cur =
  Migrations m (UniMigs m ext old cur)

type AutoMigrations m old cur =
  UniMigrations m Void old cur

class MkMigrations arg migs | arg -> migs, migs -> arg where
  mkMigrations :: arg -> NP Migration migs

instance (
    MkMigrations old (mig1 : migs)
  ) => MkMigrations (Migration ('Mig from to m ext) :> old) ('Mig from to m ext : mig1 : migs) where
    mkMigrations (next :> old) =
      next :* mkMigrations old

instance MkMigrations (Migration ('Mig from to m ext)) '[ 'Mig from to m ext] where
  mkMigrations next =
    next :* Nil

migrate ::
  MkMigrations arg migs =>
  arg ->
  Migrations m migs
migrate =
  Migrations . mkMigrations

noMigrations :: Migrations m '[]
noMigrations =
  Migrations Nil

class CustomMigration m mig where
  customTypeKeys :: MigExt mig -> m (Set (PgCompName, Bool))
  customMigration :: PgTableName -> Set PgCompName -> MigExt mig -> m ()

instance CustomMigration m ('Mig from to m Void) where
  customTypeKeys = \case
  customMigration _ _ = \case

class HoistMigration m n ext ext' | m n ext -> ext' where
  hoistMigration :: (∀ x . m x -> n x) -> ext -> ext'

instance HoistMigration m n Void Void where
  hoistMigration _ = \case

class HoistMigrations m n migs migs' | m n migs -> migs' where
  hoistMigrations :: (∀ x . m x -> n x) -> Migrations m migs -> Migrations n migs'

instance HoistMigrations m n '[] '[] where
  hoistMigrations _ Migrations {..} = Migrations {..}

instance (
    HoistMigration m n ext ext',
    HoistMigrations m n migs migs'
  ) => HoistMigrations m n ('Mig from to m ext : migs) ('Mig from to n ext' : migs') where
  hoistMigrations f (Migrations (Migration {..} :* migs)) =
    Migrations (Migration {actions = hoistAction actions, ..} :* unMigrations (hoistMigrations f (Migrations migs)))
    where
      hoistAction = \case
        CustomActions ext -> CustomActions (hoistMigration f ext)
        AutoActions {..} -> AutoActions {..}