{-# LANGUAGE GeneralizedNewtypeDeriving #-}

{- |
Copyright : Flipstone Technology Partners 2023
License   : MIT
Stability : Stable

@since 1.0.0.0
-}
module Orville.PostgreSQL.Expr.ColumnDefinition
  ( ColumnDefinition
  , columnDefinition
  , ColumnConstraint
  , notNullConstraint
  , nullConstraint
  , ColumnDefault
  , columnDefault
  )
where

import qualified Data.Maybe as Maybe

import Orville.PostgreSQL.Expr.DataType (DataType)
import Orville.PostgreSQL.Expr.Name (ColumnName)
import Orville.PostgreSQL.Expr.ValueExpression (ValueExpression)
import qualified Orville.PostgreSQL.Raw.RawSql as RawSql

{- |
Represent a complete definition of a column. E.G.

> foo INTEGER

'ColumnDefinition' provides a 'RawSql.SqlExpression' instance. See
'RawSql.unsafeSqlExpression' for how to construct a value with your own custom
SQL.

@since 1.0.0.0
-}
newtype ColumnDefinition
  = ColumnDefinition RawSql.RawSql
  deriving
    ( -- | @since 1.0.0.0
      RawSql -> ColumnDefinition
ColumnDefinition -> RawSql
(ColumnDefinition -> RawSql)
-> (RawSql -> ColumnDefinition) -> SqlExpression ColumnDefinition
forall a. (a -> RawSql) -> (RawSql -> a) -> SqlExpression a
$ctoRawSql :: ColumnDefinition -> RawSql
toRawSql :: ColumnDefinition -> RawSql
$cunsafeFromRawSql :: RawSql -> ColumnDefinition
unsafeFromRawSql :: RawSql -> ColumnDefinition
RawSql.SqlExpression
    )

{- | Smart constructor for ensuring a 'ColumnDefinition' is set up correctly.

@since 1.0.0.0
-}
columnDefinition ::
  -- | The name the resulting column should have.
  ColumnName ->
  -- | The SQL type of the column.
  DataType ->
  -- | The constraint on the column, if any.
  Maybe ColumnConstraint ->
  -- | The default value for the column, if any.
  Maybe ColumnDefault ->
  ColumnDefinition
columnDefinition :: ColumnName
-> DataType
-> Maybe ColumnConstraint
-> Maybe ColumnDefault
-> ColumnDefinition
columnDefinition ColumnName
columnName DataType
dataType Maybe ColumnConstraint
maybeColumnConstraint Maybe ColumnDefault
maybeColumnDefault =
  RawSql -> ColumnDefinition
ColumnDefinition
    (RawSql -> ColumnDefinition)
-> ([RawSql] -> RawSql) -> [RawSql] -> ColumnDefinition
forall b c a. (b -> c) -> (a -> b) -> a -> c
. RawSql -> [RawSql] -> RawSql
forall sql (f :: * -> *).
(SqlExpression sql, Foldable f) =>
RawSql -> f sql -> RawSql
RawSql.intercalate RawSql
RawSql.space
    ([RawSql] -> ColumnDefinition) -> [RawSql] -> ColumnDefinition
forall a b. (a -> b) -> a -> b
$ [Maybe RawSql] -> [RawSql]
forall a. [Maybe a] -> [a]
Maybe.catMaybes
      [ RawSql -> Maybe RawSql
forall a. a -> Maybe a
Just (RawSql -> Maybe RawSql) -> RawSql -> Maybe RawSql
forall a b. (a -> b) -> a -> b
$ ColumnName -> RawSql
forall a. SqlExpression a => a -> RawSql
RawSql.toRawSql ColumnName
columnName
      , RawSql -> Maybe RawSql
forall a. a -> Maybe a
Just (RawSql -> Maybe RawSql) -> RawSql -> Maybe RawSql
forall a b. (a -> b) -> a -> b
$ DataType -> RawSql
forall a. SqlExpression a => a -> RawSql
RawSql.toRawSql DataType
dataType
      , (ColumnConstraint -> RawSql)
-> Maybe ColumnConstraint -> Maybe RawSql
forall a b. (a -> b) -> Maybe a -> Maybe b
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap ColumnConstraint -> RawSql
forall a. SqlExpression a => a -> RawSql
RawSql.toRawSql Maybe ColumnConstraint
maybeColumnConstraint
      , (ColumnDefault -> RawSql) -> Maybe ColumnDefault -> Maybe RawSql
forall a b. (a -> b) -> Maybe a -> Maybe b
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap ColumnDefault -> RawSql
forall a. SqlExpression a => a -> RawSql
RawSql.toRawSql Maybe ColumnDefault
maybeColumnDefault
      ]

{- |
Represent constraints, such as nullability, on a column. E.G.

> NOT NULL

'ColumnConstraint' provides a 'RawSql.SqlExpression' instance. See
'RawSql.unsafeSqlExpression' for how to construct a value with your own custom
SQL.

@since 1.0.0.0
-}
newtype ColumnConstraint
  = ColumnConstraint RawSql.RawSql
  deriving
    ( -- | @since 1.0.0.0
      RawSql -> ColumnConstraint
ColumnConstraint -> RawSql
(ColumnConstraint -> RawSql)
-> (RawSql -> ColumnConstraint) -> SqlExpression ColumnConstraint
forall a. (a -> RawSql) -> (RawSql -> a) -> SqlExpression a
$ctoRawSql :: ColumnConstraint -> RawSql
toRawSql :: ColumnConstraint -> RawSql
$cunsafeFromRawSql :: RawSql -> ColumnConstraint
unsafeFromRawSql :: RawSql -> ColumnConstraint
RawSql.SqlExpression
    )

{- | Express that a column may not contain NULL.

@since 1.0.0.0
-}
notNullConstraint :: ColumnConstraint
notNullConstraint :: ColumnConstraint
notNullConstraint =
  RawSql -> ColumnConstraint
ColumnConstraint (String -> RawSql
RawSql.fromString String
"NOT NULL")

{- | Express that a column may contain NULL.

@since 1.0.0.0
-}
nullConstraint :: ColumnConstraint
nullConstraint :: ColumnConstraint
nullConstraint =
  RawSql -> ColumnConstraint
ColumnConstraint (String -> RawSql
RawSql.fromString String
"NULL")

{- |
Represents the default value of a column. E.G.

> now()

'ColumnDefault' provides' a 'RawSql.SqlExpression' instance. See
'RawSql.unsafeSqlExpression' for how to construct a value with your own custom
SQL.

@since 1.0.0.0
-}
newtype ColumnDefault
  = ColumnDefault RawSql.RawSql
  deriving
    ( -- | @since 1.0.0.0
      RawSql -> ColumnDefault
ColumnDefault -> RawSql
(ColumnDefault -> RawSql)
-> (RawSql -> ColumnDefault) -> SqlExpression ColumnDefault
forall a. (a -> RawSql) -> (RawSql -> a) -> SqlExpression a
$ctoRawSql :: ColumnDefault -> RawSql
toRawSql :: ColumnDefault -> RawSql
$cunsafeFromRawSql :: RawSql -> ColumnDefault
unsafeFromRawSql :: RawSql -> ColumnDefault
RawSql.SqlExpression
    )

{- | Given a 'ValueExpression', use that as a 'ColumnDefault'. This is the preferred path to creating a
   column default. Note that it is up to the caller to ensure the 'ValueExpression' makes sense for
   the resulting 'ColumnDefinition' this will be a part of.

@since 1.0.0.0
-}
columnDefault ::
  ValueExpression ->
  ColumnDefault
columnDefault :: ValueExpression -> ColumnDefault
columnDefault ValueExpression
defaultValue =
  RawSql -> ColumnDefault
ColumnDefault (String -> RawSql
RawSql.fromString String
"DEFAULT " RawSql -> RawSql -> RawSql
forall a. Semigroup a => a -> a -> a
<> ValueExpression -> RawSql
forall a. SqlExpression a => a -> RawSql
RawSql.toRawSql ValueExpression
defaultValue)