{-# language FlexibleContexts #-}
{-# language NamedFieldPuns #-}
{-# language TypeFamilies #-}
{-# language ViewPatterns #-}

module Rel8.Table.Insert
  ( toInsert
  , toInsertDefaults
  )
where

-- base
import Prelude

-- rel8
import Rel8.Expr ( Col( E ) )
import Rel8.Kind.Necessity ( SNecessity( SOptional, SRequired ) )
import Rel8.Schema.HTable ( hfield, htabulate, hspecs )
import Rel8.Schema.Insert ( Inserts, Col( I ), Create(..) )
import Rel8.Schema.Spec ( SSpec(..) )
import Rel8.Table ( fromColumns, toColumns )


-- | @toInsert@ converts a 'Table' of 'Expr's into a 'Table' that can be used
-- with 'Rel8.insert'. This will override any columns that have default values
-- to use exactly what is given. If you want to use default values, you can
-- either override the result of @toInsert@, or use 'toInsertDefaults'.
toInsert :: Inserts exprs inserts => exprs -> inserts
toInsert :: exprs -> inserts
toInsert (exprs -> Columns exprs (Col Expr)
forall (context :: Context) a.
Table context a =>
a -> Columns a (Col context)
toColumns -> Columns exprs (Col Expr)
exprs) = Columns inserts (Col Insert) -> inserts
forall (context :: Context) a.
Table context a =>
Columns a (Col context) -> a
fromColumns (Columns inserts (Col Insert) -> inserts)
-> Columns inserts (Col Insert) -> inserts
forall a b. (a -> b) -> a -> b
$ (forall (spec :: Spec).
 HField (Columns inserts) spec -> Col Insert spec)
-> Columns inserts (Col Insert)
forall (t :: HTable) (context :: HContext).
HTable t =>
(forall (spec :: Spec). HField t spec -> context spec) -> t context
htabulate ((forall (spec :: Spec).
  HField (Columns inserts) spec -> Col Insert spec)
 -> Columns inserts (Col Insert))
-> (forall (spec :: Spec).
    HField (Columns inserts) spec -> Col Insert spec)
-> Columns inserts (Col Insert)
forall a b. (a -> b) -> a -> b
$ \HField (Columns inserts) spec
field ->
  case Columns inserts SSpec
-> HField (Columns inserts) spec -> SSpec spec
forall (t :: HTable) (context :: HContext) (spec :: Spec).
HTable t =>
t context -> HField t spec -> context spec
hfield Columns inserts SSpec
forall (t :: HTable). HTable t => t SSpec
hspecs HField (Columns inserts) spec
field of
    SSpec {} -> Create necessity a -> Col Insert ('Spec labels necessity a)
forall (necessity :: Necessity) a (labels :: Labels).
Create necessity a -> Col Insert ('Spec labels necessity a)
I (Create necessity a -> Col Insert ('Spec labels necessity a))
-> Create necessity a -> Col Insert ('Spec labels necessity a)
forall a b. (a -> b) -> a -> b
$ case Columns inserts (Col Expr)
-> HField (Columns inserts) ('Spec labels necessity a)
-> Col Expr ('Spec labels necessity a)
forall (t :: HTable) (context :: HContext) (spec :: Spec).
HTable t =>
t context -> HField t spec -> context spec
hfield Columns exprs (Col Expr)
Columns inserts (Col Expr)
exprs HField (Columns inserts) spec
HField (Columns inserts) ('Spec labels necessity a)
field of
      E expr -> Expr a -> Create necessity a
forall a (necessity :: Necessity). Expr a -> Create necessity a
Value Expr a
expr


-- | @toInsertDefaults@ converts a 'Table' of 'Expr's into a 'Table' that can
-- be used with 'Rel8.insert'. Any columns that have a default value will
-- override whatever is in the input expression. 
--
-- One example where this is useful is for any table that has a special @id@
-- column, which has a default value to draw a new value from a sequence. If we
-- use 'toInsertDefaults', we can provide a dummy value that will be replaced
-- with a call to @DEFAULT@.
toInsertDefaults :: Inserts exprs inserts => exprs -> inserts
toInsertDefaults :: exprs -> inserts
toInsertDefaults (exprs -> Columns exprs (Col Expr)
forall (context :: Context) a.
Table context a =>
a -> Columns a (Col context)
toColumns -> Columns exprs (Col Expr)
exprs) = Columns inserts (Col Insert) -> inserts
forall (context :: Context) a.
Table context a =>
Columns a (Col context) -> a
fromColumns (Columns inserts (Col Insert) -> inserts)
-> Columns inserts (Col Insert) -> inserts
forall a b. (a -> b) -> a -> b
$ (forall (spec :: Spec).
 HField (Columns inserts) spec -> Col Insert spec)
-> Columns inserts (Col Insert)
forall (t :: HTable) (context :: HContext).
HTable t =>
(forall (spec :: Spec). HField t spec -> context spec) -> t context
htabulate ((forall (spec :: Spec).
  HField (Columns inserts) spec -> Col Insert spec)
 -> Columns inserts (Col Insert))
-> (forall (spec :: Spec).
    HField (Columns inserts) spec -> Col Insert spec)
-> Columns inserts (Col Insert)
forall a b. (a -> b) -> a -> b
$ \HField (Columns inserts) spec
field ->
  case Columns inserts SSpec
-> HField (Columns inserts) spec -> SSpec spec
forall (t :: HTable) (context :: HContext) (spec :: Spec).
HTable t =>
t context -> HField t spec -> context spec
hfield Columns inserts SSpec
forall (t :: HTable). HTable t => t SSpec
hspecs HField (Columns inserts) spec
field of
    SSpec {SNecessity necessity
necessity :: forall (labels :: Labels) (necessity :: Necessity) a.
SSpec ('Spec labels necessity a) -> SNecessity necessity
necessity :: SNecessity necessity
necessity} -> case Columns inserts (Col Expr)
-> HField (Columns inserts) ('Spec labels necessity a)
-> Col Expr ('Spec labels necessity a)
forall (t :: HTable) (context :: HContext) (spec :: Spec).
HTable t =>
t context -> HField t spec -> context spec
hfield Columns exprs (Col Expr)
Columns inserts (Col Expr)
exprs HField (Columns inserts) spec
HField (Columns inserts) ('Spec labels necessity a)
field of
      E expr -> Create necessity a -> Col Insert ('Spec labels necessity a)
forall (necessity :: Necessity) a (labels :: Labels).
Create necessity a -> Col Insert ('Spec labels necessity a)
I (Create necessity a -> Col Insert ('Spec labels necessity a))
-> Create necessity a -> Col Insert ('Spec labels necessity a)
forall a b. (a -> b) -> a -> b
$ case SNecessity necessity
necessity of
        SNecessity necessity
SRequired -> Expr a -> Create necessity a
forall a (necessity :: Necessity). Expr a -> Create necessity a
Value Expr a
expr
        SNecessity necessity
SOptional -> Create necessity a
forall a. Create 'Optional a
Default