packages feed

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

module Sqel.Migration.Metadata where

import qualified Data.Map.Strict as Map
import Exon (exon)
import Prettyprinter (Pretty, pretty, vsep, (<+>))

import qualified Sqel.Class.MigrationEffect as MigrationEffect
import Sqel.Class.MigrationEffect (MigrationEffect)
import qualified Sqel.Data.PgType as PgType
import Sqel.Data.PgType (
  ColumnType,
  PgColumn (PgColumn),
  PgColumnName (PgColumnName),
  PgPrimName (PgPrimName),
  PgTypeRef (PgTypeRef),
  )
import Sqel.Data.PgTypeName (PgTableName, pattern PgTypeName, PgTypeName)
import Sqel.Data.Sql (Sql)
import qualified Sqel.Statement as Statement
import Sqel.Statement (tableColumnsSql)

newtype DbCols =
  DbCols { unDbCols :: Map PgColumnName (Either PgTypeRef PgPrimName) }
  deriving stock (Eq, Show, Generic)

newtype PrettyColMap =
  PrettyColMap { unPrettyColMap :: DbCols }
  deriving stock (Eq, Show, Generic)

instance Pretty PrettyColMap where
  pretty (PrettyColMap (DbCols cols)) =
    vsep (uncurry col <$> Map.toList cols)
    where
      col name = \case
        Right tpe -> "*" <+> pretty name <+> pretty tpe
        Left ref -> "+" <+> pretty name <+> pretty ref

typeColumns ::
  Monad m =>
  MigrationEffect m =>
  Sql ->
  PgTypeName table ->
  m DbCols
typeColumns code (PgTypeName name) = do
  cols <- traverse mktype =<< MigrationEffect.runStatement name (Statement.dbColumns code)
  pure (DbCols (Map.fromList cols))
  where
    mktype = \case
      (col, "USER-DEFINED", n, _) ->
        pure (PgColumnName col, Left (PgTypeRef n))
      (col, "ARRAY", _, Just n) ->
        pure (PgColumnName col, Right (PgPrimName [exon|#{n}[]|]))
      (col, n, _, Nothing) ->
        pure (PgColumnName col, Right (PgPrimName n))
      (col, n, _, Just e) -> do
        MigrationEffect.error [exon|Error: non-array column with element type: ##{n} | ##{e}|]
        pure (PgColumnName col, Right (PgPrimName n))

tableColumns ::
  Monad m =>
  MigrationEffect m =>
  PgTableName ->
  m DbCols
tableColumns =
  typeColumns tableColumnsSql

columnMap :: [PgColumn] -> Map PgColumnName ColumnType
columnMap =
  Map.fromList . fmap \ PgColumn {name, pgType} -> (name, pgType)

logType ::
  MigrationEffect m =>
  Text ->
  DbCols ->
  DbCols ->
  m ()
logType desc dbCols colsByName =
  MigrationEffect.log [exon|Trying #{desc} with:
#{show (pretty (PrettyColMap colsByName))}
for existing #{desc} with
#{show (pretty (PrettyColMap dbCols))}|]

data TypeStatus =
  Absent
  |
  Mismatch
  |
  Match
  deriving stock (Eq, Show, Generic)

typeStatus ::
  DbCols ->
  DbCols ->
  TypeStatus
typeStatus (DbCols dbCols) (DbCols colByName)
  | Map.null dbCols = Absent
  | dbCols == colByName = Match
  | otherwise = Mismatch