packages feed

sqel-0.0.1.0: lib/Sqel/ColumnConstraints.hs

module Sqel.ColumnConstraints where

import Generics.SOP (I (I), NP (Nil, (:*)))
import Lens.Micro ((%~), (.~))
import Sqel.Data.Mods (
  Mods (Mods),
  Nullable (Nullable),
  PgDefault (PgDefault),
  PrimaryKey (PrimaryKey),
  Unique (Unique),
  )
import Sqel.Data.Sql (Sql, sql)

data Constraints =
  Constraints {
    unique :: Bool,
    nullable :: Bool,
    extra :: [Sql]
  }
  deriving stock (Eq, Show, Generic)

class ColumnConstraint mod where
  columnConstraint :: mod -> Constraints -> Constraints

instance {-# overlappable #-} ColumnConstraint mod where
  columnConstraint _ = id

instance ColumnConstraint Nullable where
  columnConstraint Nullable =
    #nullable .~ True

instance ColumnConstraint PrimaryKey where
  columnConstraint PrimaryKey Constraints {..} =
    Constraints {
      unique = True,
      extra = "primary key" : extra,
      ..
    }

instance ColumnConstraint PgDefault where
  columnConstraint (PgDefault val) =
    #extra %~ ([sql|default ##{val}|] :)

instance ColumnConstraint Unique where
  columnConstraint Unique Constraints {..} =
    Constraints {
      unique = True,
      extra = "unique" : extra,
      ..
    }

class ColumnConstraints mods where
  collectConstraints :: NP I mods -> Constraints

instance ColumnConstraints '[] where
  collectConstraints Nil = Constraints False False []

instance (
    ColumnConstraint mod,
    ColumnConstraints mods
  ) => ColumnConstraints (mod : mods) where
  collectConstraints (I h :* t) =
    columnConstraint h (collectConstraints t)

columnConstraints ::
  ColumnConstraints mods =>
  Mods mods ->
  (Bool, [Sql])
columnConstraints (Mods mods) =
  (unique, notNull <> extra)
  where
    notNull | nullable = []
            | otherwise = ["not null"]
    Constraints {..} = collectConstraints mods