content: {-|
  Module: Squeal.PostgreSQL.Expression.Logic
  Description: logical expressions and operators
  Copyright: (c) Eitan Chatav, 2019
  Maintainer: eitan@morphism.tech
  Stability: experimental
  
  logical expressions and operators
  -}
  
  {-# LANGUAGE
      DataKinds
    , OverloadedStrings
    , TypeOperators
  #-}
  
  module Squeal.PostgreSQL.Expression.Logic
    ( -- * Condition
      Condition
    , true
    , false
      -- * Logic
    , not_
    , (.&&)
    , (.||)
      -- * Conditional
    , caseWhenThenElse
    , ifThenElse
    ) where
  
  import Squeal.PostgreSQL.Expression
  import Squeal.PostgreSQL.Render
  import Squeal.PostgreSQL.Type.Schema
  
  -- | A `Condition` is an `Expression`, which can evaluate
  -- to `true`, `false` or `Squeal.PostgreSQL.Null.null_`. This is because SQL uses
  -- a three valued logic.
  type Condition grp lat with db params from =
    Expression grp lat with db params from ('Null 'PGbool)
  
  -- | >>> printSQL true
  -- TRUE
  true :: Expr (null 'PGbool)
  true = UnsafeExpression "TRUE"
  
  -- | >>> printSQL false
  -- FALSE
  false :: Expr (null 'PGbool)
  false = UnsafeExpression "FALSE"
  
  -- | >>> printSQL $ not_ true
  -- (NOT TRUE)
  not_ :: null 'PGbool --> null 'PGbool
  not_ = unsafeLeftOp "NOT"
  
  -- | >>> printSQL $ true .&& false
  -- (TRUE AND FALSE)
  (.&&) :: Operator (null 'PGbool) (null 'PGbool) (null 'PGbool)
  infixr 3 .&&
  (.&&) = unsafeBinaryOp "AND"
  
  -- | >>> printSQL $ true .|| false
  -- (TRUE OR FALSE)
  (.||) :: Operator (null 'PGbool) (null 'PGbool) (null 'PGbool)
  infixr 2 .||
  (.||) = unsafeBinaryOp "OR"
  
  -- | >>> :{
  -- let
  --   expression :: Expression grp lat with db params from (null 'PGint2)
  --   expression = caseWhenThenElse [(true, 1), (false, 2)] 3
  -- in printSQL expression
  -- :}
  -- CASE WHEN TRUE THEN (1 :: int2) WHEN FALSE THEN (2 :: int2) ELSE (3 :: int2) END
  caseWhenThenElse
    :: [ ( Condition grp lat with db params from
         , Expression grp lat with db params from ty
       ) ]
    -- ^ whens and thens
    -> Expression grp lat with db params from ty
    -- ^ else
    -> Expression grp lat with db params from ty
  caseWhenThenElse whenThens else_ = UnsafeExpression $ mconcat
    [ "CASE"
    , mconcat
      [ mconcat
        [ " WHEN ", renderSQL when_
        , " THEN ", renderSQL then_
        ]
      | (when_,then_) <- whenThens
      ]
    , " ELSE ", renderSQL else_
    , " END"
    ]
  
  -- | >>> :{
  -- let
  --   expression :: Expression grp lat with db params from (null 'PGint2)
  --   expression = ifThenElse true 1 0
  -- in printSQL expression
  -- :}
  -- CASE WHEN TRUE THEN (1 :: int2) ELSE (0 :: int2) END
  ifThenElse
    :: Condition grp lat with db params from
    -> Expression grp lat with db params from ty -- ^ then
    -> Expression grp lat with db params from ty -- ^ else
    -> Expression grp lat with db params from ty
  ifThenElse if_ then_ else_ = caseWhenThenElse [(if_,then_)] else_
  