{-# LANGUAGE GeneralizedNewtypeDeriving #-}

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

@since 1.0.0.0
-}
module Orville.PostgreSQL.Expr.OrderBy
  ( OrderByClause
  , orderByClause
  , OrderByExpr
  , appendOrderByExpr
  , orderByColumnName
  , orderByColumnsExpr
  , OrderByDirection
  , NullsOrder (NullsFirst, NullsLast)
  , ascendingOrder
  , descendingOrder
  , ascendingOrderWith
  , descendingOrderWith
  )
where

import qualified Data.List.NonEmpty as NEL

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

{- |
Type to represent a SQL order by clause. E.G.

> ORDER BY foo, bar

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

orderByClause :: OrderByExpr -> OrderByClause
orderByClause :: OrderByExpr -> OrderByClause
orderByClause OrderByExpr
expr = RawSql -> OrderByClause
OrderByClause (String -> RawSql
RawSql.fromString String
"ORDER BY " RawSql -> RawSql -> RawSql
forall a. Semigroup a => a -> a -> a
<> OrderByExpr -> RawSql
forall a. SqlExpression a => a -> RawSql
RawSql.toRawSql OrderByExpr
expr)

{- |
Type to represent a SQL order by expression (the part that follows the @ORDER
BY@ in SQL). E.G.

> foo, bar

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

{- |
@since 1.0.0.0
-}
instance Semigroup OrderByExpr where
  <> :: OrderByExpr -> OrderByExpr -> OrderByExpr
(<>) = OrderByExpr -> OrderByExpr -> OrderByExpr
appendOrderByExpr

{- | Combines two 'OrderByExpr's with a comma between them.

@since 1.0.0.0
-}
appendOrderByExpr :: OrderByExpr -> OrderByExpr -> OrderByExpr
appendOrderByExpr :: OrderByExpr -> OrderByExpr -> OrderByExpr
appendOrderByExpr (OrderByExpr RawSql
a) (OrderByExpr RawSql
b) =
  RawSql -> OrderByExpr
OrderByExpr (RawSql
a RawSql -> RawSql -> RawSql
forall a. Semigroup a => a -> a -> a
<> RawSql
RawSql.commaSpace RawSql -> RawSql -> RawSql
forall a. Semigroup a => a -> a -> a
<> RawSql
b)

{- | Create an 'OrderByExpr' for 'ColumnName' and 'OrderByDirection' pairs, ensuring commas as
  needed.

@since 1.0.0.0
-}
orderByColumnsExpr :: NEL.NonEmpty (ColumnName, OrderByDirection) -> OrderByExpr
orderByColumnsExpr :: NonEmpty (ColumnName, OrderByDirection) -> OrderByExpr
orderByColumnsExpr =
  RawSql -> OrderByExpr
OrderByExpr (RawSql -> OrderByExpr)
-> (NonEmpty (ColumnName, OrderByDirection) -> RawSql)
-> NonEmpty (ColumnName, OrderByDirection)
-> OrderByExpr
forall b c a. (b -> c) -> (a -> b) -> a -> c
. RawSql -> NonEmpty RawSql -> RawSql
forall sql (f :: * -> *).
(SqlExpression sql, Foldable f) =>
RawSql -> f sql -> RawSql
RawSql.intercalate RawSql
RawSql.commaSpace (NonEmpty RawSql -> RawSql)
-> (NonEmpty (ColumnName, OrderByDirection) -> NonEmpty RawSql)
-> NonEmpty (ColumnName, OrderByDirection)
-> RawSql
forall b c a. (b -> c) -> (a -> b) -> a -> c
. ((ColumnName, OrderByDirection) -> RawSql)
-> NonEmpty (ColumnName, OrderByDirection) -> NonEmpty RawSql
forall a b. (a -> b) -> NonEmpty a -> NonEmpty b
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap (ColumnName, OrderByDirection) -> RawSql
columnOrdering
 where
  columnOrdering :: (ColumnName, OrderByDirection) -> RawSql.RawSql
  columnOrdering :: (ColumnName, OrderByDirection) -> RawSql
columnOrdering (ColumnName
columnName, OrderByDirection
orderByDirection) =
    ColumnName -> RawSql
forall a. SqlExpression a => a -> RawSql
RawSql.toRawSql ColumnName
columnName RawSql -> RawSql -> RawSql
forall a. Semigroup a => a -> a -> a
<> RawSql
RawSql.space RawSql -> RawSql -> RawSql
forall a. Semigroup a => a -> a -> a
<> OrderByDirection -> RawSql
forall a. SqlExpression a => a -> RawSql
RawSql.toRawSql OrderByDirection
orderByDirection

{-- |
  Orders a query by the given column name in the given order direction.

@since 1.0.0.0
-}
orderByColumnName :: ColumnName -> OrderByDirection -> OrderByExpr
orderByColumnName :: ColumnName -> OrderByDirection -> OrderByExpr
orderByColumnName =
  ((ColumnName, OrderByDirection) -> OrderByExpr)
-> ColumnName -> OrderByDirection -> OrderByExpr
forall a b c. ((a, b) -> c) -> a -> b -> c
curry (NonEmpty (ColumnName, OrderByDirection) -> OrderByExpr
orderByColumnsExpr (NonEmpty (ColumnName, OrderByDirection) -> OrderByExpr)
-> ((ColumnName, OrderByDirection)
    -> NonEmpty (ColumnName, OrderByDirection))
-> (ColumnName, OrderByDirection)
-> OrderByExpr
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (ColumnName, OrderByDirection)
-> NonEmpty (ColumnName, OrderByDirection)
forall a. a -> NonEmpty a
forall (f :: * -> *) a. Applicative f => a -> f a
pure)

{- |
Type to represent a SQL order by direction expression. E.G.

> ASC

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

{- |
Type to represent the ordering of Null, intended to be used with 'OrderByDirection'.

@since 1.0.0.0
-}
data NullsOrder
  = NullsFirst
  | NullsLast
  deriving
    ( -- | @since 1.0.0.0
      NullsOrder -> NullsOrder -> Bool
(NullsOrder -> NullsOrder -> Bool)
-> (NullsOrder -> NullsOrder -> Bool) -> Eq NullsOrder
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
$c== :: NullsOrder -> NullsOrder -> Bool
== :: NullsOrder -> NullsOrder -> Bool
$c/= :: NullsOrder -> NullsOrder -> Bool
/= :: NullsOrder -> NullsOrder -> Bool
Eq
    , -- | @since 1.0.0.0
      Int -> NullsOrder -> ShowS
[NullsOrder] -> ShowS
NullsOrder -> String
(Int -> NullsOrder -> ShowS)
-> (NullsOrder -> String)
-> ([NullsOrder] -> ShowS)
-> Show NullsOrder
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
$cshowsPrec :: Int -> NullsOrder -> ShowS
showsPrec :: Int -> NullsOrder -> ShowS
$cshow :: NullsOrder -> String
show :: NullsOrder -> String
$cshowList :: [NullsOrder] -> ShowS
showList :: [NullsOrder] -> ShowS
Show
    , -- | @since 1.0.0.0
      Eq NullsOrder
Eq NullsOrder
-> (NullsOrder -> NullsOrder -> Ordering)
-> (NullsOrder -> NullsOrder -> Bool)
-> (NullsOrder -> NullsOrder -> Bool)
-> (NullsOrder -> NullsOrder -> Bool)
-> (NullsOrder -> NullsOrder -> Bool)
-> (NullsOrder -> NullsOrder -> NullsOrder)
-> (NullsOrder -> NullsOrder -> NullsOrder)
-> Ord NullsOrder
NullsOrder -> NullsOrder -> Bool
NullsOrder -> NullsOrder -> Ordering
NullsOrder -> NullsOrder -> NullsOrder
forall a.
Eq a
-> (a -> a -> Ordering)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> a)
-> (a -> a -> a)
-> Ord a
$ccompare :: NullsOrder -> NullsOrder -> Ordering
compare :: NullsOrder -> NullsOrder -> Ordering
$c< :: NullsOrder -> NullsOrder -> Bool
< :: NullsOrder -> NullsOrder -> Bool
$c<= :: NullsOrder -> NullsOrder -> Bool
<= :: NullsOrder -> NullsOrder -> Bool
$c> :: NullsOrder -> NullsOrder -> Bool
> :: NullsOrder -> NullsOrder -> Bool
$c>= :: NullsOrder -> NullsOrder -> Bool
>= :: NullsOrder -> NullsOrder -> Bool
$cmax :: NullsOrder -> NullsOrder -> NullsOrder
max :: NullsOrder -> NullsOrder -> NullsOrder
$cmin :: NullsOrder -> NullsOrder -> NullsOrder
min :: NullsOrder -> NullsOrder -> NullsOrder
Ord
    , -- | @since 1.0.0.0
      Int -> NullsOrder
NullsOrder -> Int
NullsOrder -> [NullsOrder]
NullsOrder -> NullsOrder
NullsOrder -> NullsOrder -> [NullsOrder]
NullsOrder -> NullsOrder -> NullsOrder -> [NullsOrder]
(NullsOrder -> NullsOrder)
-> (NullsOrder -> NullsOrder)
-> (Int -> NullsOrder)
-> (NullsOrder -> Int)
-> (NullsOrder -> [NullsOrder])
-> (NullsOrder -> NullsOrder -> [NullsOrder])
-> (NullsOrder -> NullsOrder -> [NullsOrder])
-> (NullsOrder -> NullsOrder -> NullsOrder -> [NullsOrder])
-> Enum NullsOrder
forall a.
(a -> a)
-> (a -> a)
-> (Int -> a)
-> (a -> Int)
-> (a -> [a])
-> (a -> a -> [a])
-> (a -> a -> [a])
-> (a -> a -> a -> [a])
-> Enum a
$csucc :: NullsOrder -> NullsOrder
succ :: NullsOrder -> NullsOrder
$cpred :: NullsOrder -> NullsOrder
pred :: NullsOrder -> NullsOrder
$ctoEnum :: Int -> NullsOrder
toEnum :: Int -> NullsOrder
$cfromEnum :: NullsOrder -> Int
fromEnum :: NullsOrder -> Int
$cenumFrom :: NullsOrder -> [NullsOrder]
enumFrom :: NullsOrder -> [NullsOrder]
$cenumFromThen :: NullsOrder -> NullsOrder -> [NullsOrder]
enumFromThen :: NullsOrder -> NullsOrder -> [NullsOrder]
$cenumFromTo :: NullsOrder -> NullsOrder -> [NullsOrder]
enumFromTo :: NullsOrder -> NullsOrder -> [NullsOrder]
$cenumFromThenTo :: NullsOrder -> NullsOrder -> NullsOrder -> [NullsOrder]
enumFromThenTo :: NullsOrder -> NullsOrder -> NullsOrder -> [NullsOrder]
Enum
    , -- | @since 1.0.0.0
      NullsOrder
NullsOrder -> NullsOrder -> Bounded NullsOrder
forall a. a -> a -> Bounded a
$cminBound :: NullsOrder
minBound :: NullsOrder
$cmaxBound :: NullsOrder
maxBound :: NullsOrder
Bounded
    )

{- | The SQL ASC order direction.

@since 1.0.0.0
-}
ascendingOrder :: OrderByDirection
ascendingOrder :: OrderByDirection
ascendingOrder = RawSql -> OrderByDirection
OrderByDirection (RawSql -> OrderByDirection) -> RawSql -> OrderByDirection
forall a b. (a -> b) -> a -> b
$ String -> RawSql
RawSql.fromString String
"ASC"

{- | The SQL DESC order direction.

@since 1.0.0.0
-}
descendingOrder :: OrderByDirection
descendingOrder :: OrderByDirection
descendingOrder = RawSql -> OrderByDirection
OrderByDirection (RawSql -> OrderByDirection) -> RawSql -> OrderByDirection
forall a b. (a -> b) -> a -> b
$ String -> RawSql
RawSql.fromString String
"DESC"

{- | The SQL ASC order direction with NULLs ordered as given.

@since 1.0.0.0
-}
ascendingOrderWith :: NullsOrder -> OrderByDirection
ascendingOrderWith :: NullsOrder -> OrderByDirection
ascendingOrderWith NullsOrder
no =
  RawSql -> OrderByDirection
OrderByDirection (RawSql -> OrderByDirection) -> RawSql -> OrderByDirection
forall a b. (a -> b) -> a -> b
$ OrderByDirection -> RawSql
forall a. SqlExpression a => a -> RawSql
RawSql.toRawSql OrderByDirection
ascendingOrder RawSql -> RawSql -> RawSql
forall a. Semigroup a => a -> a -> a
<> RawSql
RawSql.space RawSql -> RawSql -> RawSql
forall a. Semigroup a => a -> a -> a
<> NullsOrder -> RawSql
nullsOrder NullsOrder
no

{- | The SQL DESC order direction with NULLs ordered as given.

@since 1.0.0.0
-}
descendingOrderWith :: NullsOrder -> OrderByDirection
descendingOrderWith :: NullsOrder -> OrderByDirection
descendingOrderWith NullsOrder
no =
  RawSql -> OrderByDirection
OrderByDirection (RawSql -> OrderByDirection) -> RawSql -> OrderByDirection
forall a b. (a -> b) -> a -> b
$ OrderByDirection -> RawSql
forall a. SqlExpression a => a -> RawSql
RawSql.toRawSql OrderByDirection
descendingOrder RawSql -> RawSql -> RawSql
forall a. Semigroup a => a -> a -> a
<> RawSql
RawSql.space RawSql -> RawSql -> RawSql
forall a. Semigroup a => a -> a -> a
<> NullsOrder -> RawSql
nullsOrder NullsOrder
no

nullsOrder :: NullsOrder -> RawSql.RawSql
nullsOrder :: NullsOrder -> RawSql
nullsOrder NullsOrder
no = case NullsOrder
no of
  NullsOrder
NullsFirst -> String -> RawSql
RawSql.fromString String
"NULLS FIRST"
  NullsOrder
NullsLast -> String -> RawSql
RawSql.fromString String
"NULLS LAST"