{-# LANGUAGE GeneralizedNewtypeDeriving #-}

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

Provides a type representing SQL operators with exactly two arguments, as well
as values of that type for many common operators.

@since 1.0.0.0
-}
module Orville.PostgreSQL.Expr.BinaryOperator
  ( BinaryOperator
  , binaryOperator
  , binaryOpExpression
  , equalsOp
  , notEqualsOp
  , greaterThanOp
  , lessThanOp
  , greaterThanOrEqualsOp
  , lessThanOrEqualsOp
  , likeOp
  , iLikeOp
  , orOp
  , andOp
  , plusOp
  , minusOp
  , multiplicationOp
  , divisionOp
  , moduloOp
  , exponentiationOp
  , bitwiseAndOp
  , bitwiseOrOp
  , bitwiseXorOp
  , bitwiseShiftLeftOp
  , bitwiseShiftRightOp
  )
where

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

{- |
Type to represent any SQL operator of two arguments. E.G.

> AND

'BinaryOperator' 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 BinaryOperator
  = BinaryOperator RawSql.RawSql
  deriving
    ( -- | @since 1.0.0.0
      RawSql -> BinaryOperator
BinaryOperator -> RawSql
(BinaryOperator -> RawSql)
-> (RawSql -> BinaryOperator) -> SqlExpression BinaryOperator
forall a. (a -> RawSql) -> (RawSql -> a) -> SqlExpression a
$ctoRawSql :: BinaryOperator -> RawSql
toRawSql :: BinaryOperator -> RawSql
$cunsafeFromRawSql :: RawSql -> BinaryOperator
unsafeFromRawSql :: RawSql -> BinaryOperator
RawSql.SqlExpression
    )

{- | Construct a binary operator. Note that this does not include any check to determine if the
operator is valid, either by being a native SQL operator, or a custom-defined operator in the
database.

@since 1.0.0.0
-}
binaryOperator :: String -> BinaryOperator
binaryOperator :: String -> BinaryOperator
binaryOperator =
  RawSql -> BinaryOperator
BinaryOperator (RawSql -> BinaryOperator)
-> (String -> RawSql) -> String -> BinaryOperator
forall b c a. (b -> c) -> (a -> b) -> a -> c
. String -> RawSql
RawSql.fromString

{- | The SQL equal binary operator.

@since 1.0.0.0
-}
equalsOp :: BinaryOperator
equalsOp :: BinaryOperator
equalsOp =
  String -> BinaryOperator
binaryOperator String
"="

{- | The SQL not equal binary operator.

@since 1.0.0.0
-}
notEqualsOp :: BinaryOperator
notEqualsOp :: BinaryOperator
notEqualsOp =
  String -> BinaryOperator
binaryOperator String
"<>"

{- | The SQL strictly greater than binary operator.

@since 1.0.0.0
-}
greaterThanOp :: BinaryOperator
greaterThanOp :: BinaryOperator
greaterThanOp =
  String -> BinaryOperator
binaryOperator String
">"

{- | The SQL strictly less than binary operator.

@since 1.0.0.0
-}
lessThanOp :: BinaryOperator
lessThanOp :: BinaryOperator
lessThanOp =
  String -> BinaryOperator
binaryOperator String
"<"

{- | The SQL greater than or equal binary operator.

@since 1.0.0.0
-}
greaterThanOrEqualsOp :: BinaryOperator
greaterThanOrEqualsOp :: BinaryOperator
greaterThanOrEqualsOp =
  String -> BinaryOperator
binaryOperator String
">="

{- | The SQL less than or equal binary operator.

@since 1.0.0.0
-}
lessThanOrEqualsOp :: BinaryOperator
lessThanOrEqualsOp :: BinaryOperator
lessThanOrEqualsOp =
  String -> BinaryOperator
binaryOperator String
"<="

{- | The SQL LIKE binary operator.

@since 1.0.0.0
-}
likeOp :: BinaryOperator
likeOp :: BinaryOperator
likeOp =
  String -> BinaryOperator
binaryOperator String
"LIKE"

{- | The SQL ILIKE binary operator.

@since 1.0.0.0
-}
iLikeOp :: BinaryOperator
iLikeOp :: BinaryOperator
iLikeOp =
  String -> BinaryOperator
binaryOperator String
"ILIKE"

{- | The SQL logical or binary operator.

@since 1.0.0.0
-}
orOp :: BinaryOperator
orOp :: BinaryOperator
orOp =
  String -> BinaryOperator
binaryOperator String
"OR"

{- | The SQL logical and binary operator.

@since 1.0.0.0
-}
andOp :: BinaryOperator
andOp :: BinaryOperator
andOp =
  String -> BinaryOperator
binaryOperator String
"AND"

{- | The SQL + binary operator.

@since 1.0.0.0
-}
plusOp :: BinaryOperator
plusOp :: BinaryOperator
plusOp =
  String -> BinaryOperator
binaryOperator String
"+"

{- | The SQL - binary operator.

@since 1.0.0.0
-}
minusOp :: BinaryOperator
minusOp :: BinaryOperator
minusOp =
  String -> BinaryOperator
binaryOperator String
"-"

{- | The SQL * binary operator.

@since 1.0.0.0
-}
multiplicationOp :: BinaryOperator
multiplicationOp :: BinaryOperator
multiplicationOp =
  String -> BinaryOperator
binaryOperator String
"*"

{- | The SQL / binary operator.

@since 1.0.0.0
-}
divisionOp :: BinaryOperator
divisionOp :: BinaryOperator
divisionOp =
  String -> BinaryOperator
binaryOperator String
"/"

{- | The SQL % binary operator.

@since 1.0.0.0
-}
moduloOp :: BinaryOperator
moduloOp :: BinaryOperator
moduloOp =
  String -> BinaryOperator
binaryOperator String
"%"

{- | The SQL ^ binary operator.

@since 1.0.0.0
-}
exponentiationOp :: BinaryOperator
exponentiationOp :: BinaryOperator
exponentiationOp =
  String -> BinaryOperator
binaryOperator String
"^"

{- | The SQL bitwise and (a.k.a &) binary operator.

@since 1.0.0.0
-}
bitwiseAndOp :: BinaryOperator
bitwiseAndOp :: BinaryOperator
bitwiseAndOp =
  String -> BinaryOperator
binaryOperator String
"&"

{- | The SQL bitwise or (a.k.a |) binary operator.

@since 1.0.0.0
-}
bitwiseOrOp :: BinaryOperator
bitwiseOrOp :: BinaryOperator
bitwiseOrOp =
  String -> BinaryOperator
binaryOperator String
"|"

{- | The SQL bitwise exclusive or (a.k.a #) binary operator.

@since 1.0.0.0
-}
bitwiseXorOp :: BinaryOperator
bitwiseXorOp :: BinaryOperator
bitwiseXorOp =
  String -> BinaryOperator
binaryOperator String
"#"

{- | The SQL bitwise left shift (a.k.a <<) binary operator.

@since 1.0.0.0
-}
bitwiseShiftLeftOp :: BinaryOperator
bitwiseShiftLeftOp :: BinaryOperator
bitwiseShiftLeftOp =
  String -> BinaryOperator
binaryOperator String
"<<"

{- | The SQL bitwise right shift (a.k.a >>) binary operator.

@since 1.0.0.0
-}
bitwiseShiftRightOp :: BinaryOperator
bitwiseShiftRightOp :: BinaryOperator
bitwiseShiftRightOp =
  String -> BinaryOperator
binaryOperator String
">>"

{- | Apply a binary operator to two 'ValueExpression's resulting in some
 'RawSql.SqlExpression'. Note that this does *NOT* extend typechecking to the 'ValueExpression's
 being used with the 'BinaryOperator'. It is left to the caller to ensure that the operator makes
 sense with the arguments being passed.

@since 1.0.0.0
-}
binaryOpExpression ::
  RawSql.SqlExpression sql =>
  BinaryOperator ->
  ValueExpression ->
  ValueExpression ->
  sql
binaryOpExpression :: forall sql.
SqlExpression sql =>
BinaryOperator -> ValueExpression -> ValueExpression -> sql
binaryOpExpression BinaryOperator
op ValueExpression
left ValueExpression
right =
  BinaryOperator -> ValueExpression -> ValueExpression -> sql
forall sql.
SqlExpression sql =>
BinaryOperator -> ValueExpression -> ValueExpression -> sql
binaryOpExpressionUnparenthenizedArguments
    BinaryOperator
op
    (RawSql -> ValueExpression
forall a. SqlExpression a => RawSql -> a
RawSql.unsafeFromRawSql (RawSql
RawSql.leftParen RawSql -> RawSql -> RawSql
forall a. Semigroup a => a -> a -> a
<> ValueExpression -> RawSql
forall a. SqlExpression a => a -> RawSql
RawSql.toRawSql ValueExpression
left RawSql -> RawSql -> RawSql
forall a. Semigroup a => a -> a -> a
<> RawSql
RawSql.rightParen))
    (RawSql -> ValueExpression
forall a. SqlExpression a => RawSql -> a
RawSql.unsafeFromRawSql (RawSql
RawSql.leftParen RawSql -> RawSql -> RawSql
forall a. Semigroup a => a -> a -> a
<> ValueExpression -> RawSql
forall a. SqlExpression a => a -> RawSql
RawSql.toRawSql ValueExpression
right RawSql -> RawSql -> RawSql
forall a. Semigroup a => a -> a -> a
<> RawSql
RawSql.rightParen))

-- internal helper function
binaryOpExpressionUnparenthenizedArguments ::
  RawSql.SqlExpression sql =>
  BinaryOperator ->
  ValueExpression ->
  ValueExpression ->
  sql
binaryOpExpressionUnparenthenizedArguments :: forall sql.
SqlExpression sql =>
BinaryOperator -> ValueExpression -> ValueExpression -> sql
binaryOpExpressionUnparenthenizedArguments BinaryOperator
op ValueExpression
left ValueExpression
right =
  RawSql -> sql
forall a. SqlExpression a => RawSql -> a
RawSql.unsafeFromRawSql (RawSql -> sql) -> RawSql -> sql
forall a b. (a -> b) -> a -> b
$
    ValueExpression -> RawSql
forall a. SqlExpression a => a -> RawSql
RawSql.toRawSql ValueExpression
left
      RawSql -> RawSql -> RawSql
forall a. Semigroup a => a -> a -> a
<> RawSql
RawSql.space
      RawSql -> RawSql -> RawSql
forall a. Semigroup a => a -> a -> a
<> BinaryOperator -> RawSql
forall a. SqlExpression a => a -> RawSql
RawSql.toRawSql BinaryOperator
op
      RawSql -> RawSql -> RawSql
forall a. Semigroup a => a -> a -> a
<> RawSql
RawSql.space
      RawSql -> RawSql -> RawSql
forall a. Semigroup a => a -> a -> a
<> ValueExpression -> RawSql
forall a. SqlExpression a => a -> RawSql
RawSql.toRawSql ValueExpression
right