{-# language DataKinds #-}

{-# options_ghc -fno-warn-redundant-constraints #-}

module Rel8.Expr.Order
  ( asc
  , desc
  , nullsFirst
  , nullsLast
  )
where

-- base
import Data.Bifunctor ( first )
import Prelude

-- opaleye
import Opaleye.Internal.HaskellDB.PrimQuery ( OrderOp( orderDirection, orderNulls ) )
import qualified Opaleye.Internal.HaskellDB.PrimQuery as Opaleye
import qualified Opaleye.Internal.Order as Opaleye

-- rel8
import Rel8.Expr ( Expr )
import Rel8.Expr.Null ( unsafeUnnullify )
import Rel8.Expr.Opaleye ( toPrimExpr )
import Rel8.Order ( Order( Order ) )
import Rel8.Type.Ord ( DBOrd )


-- | Sort a column in ascending order.
asc :: DBOrd a => Order (Expr a)
asc :: forall a. DBOrd a => Order (Expr a)
asc = Order (Expr a) -> Order (Expr a)
forall a. Order a -> Order a
Order (Order (Expr a) -> Order (Expr a))
-> Order (Expr a) -> Order (Expr a)
forall a b. (a -> b) -> a -> b
$ (Expr a -> [(OrderOp, PrimExpr)]) -> Order (Expr a)
forall a. (a -> [(OrderOp, PrimExpr)]) -> Order a
Opaleye.Order (\Expr a
expr -> [(OrderOp
orderOp, Expr a -> PrimExpr
forall a. Expr a -> PrimExpr
toPrimExpr Expr a
expr)])
  where
    orderOp :: Opaleye.OrderOp
    orderOp :: OrderOp
orderOp = Opaleye.OrderOp
      { orderDirection :: OrderDirection
orderDirection = OrderDirection
Opaleye.OpAsc
      , orderNulls :: OrderNulls
orderNulls = OrderNulls
Opaleye.NullsLast
      }


-- | Sort a column in descending order.
desc :: DBOrd a => Order (Expr a)
desc :: forall a. DBOrd a => Order (Expr a)
desc = Order (Expr a) -> Order (Expr a)
forall a. Order a -> Order a
Order (Order (Expr a) -> Order (Expr a))
-> Order (Expr a) -> Order (Expr a)
forall a b. (a -> b) -> a -> b
$ (Expr a -> [(OrderOp, PrimExpr)]) -> Order (Expr a)
forall a. (a -> [(OrderOp, PrimExpr)]) -> Order a
Opaleye.Order (\Expr a
expr -> [(OrderOp
orderOp, Expr a -> PrimExpr
forall a. Expr a -> PrimExpr
toPrimExpr Expr a
expr)])
  where
    orderOp :: Opaleye.OrderOp
    orderOp :: OrderOp
orderOp = Opaleye.OrderOp
      { orderDirection :: OrderDirection
orderDirection = OrderDirection
Opaleye.OpDesc
      , orderNulls :: OrderNulls
orderNulls = OrderNulls
Opaleye.NullsFirst
      }


-- | Transform an ordering so that @null@ values appear first. This corresponds
-- to @NULLS FIRST@ in SQL.
nullsFirst :: Order (Expr a) -> Order (Expr (Maybe a))
nullsFirst :: forall a. Order (Expr a) -> Order (Expr (Maybe a))
nullsFirst (Order (Opaleye.Order Expr a -> [(OrderOp, PrimExpr)]
f)) =
  Order (Expr (Maybe a)) -> Order (Expr (Maybe a))
forall a. Order a -> Order a
Order (Order (Expr (Maybe a)) -> Order (Expr (Maybe a)))
-> Order (Expr (Maybe a)) -> Order (Expr (Maybe a))
forall a b. (a -> b) -> a -> b
$ (Expr (Maybe a) -> [(OrderOp, PrimExpr)]) -> Order (Expr (Maybe a))
forall a. (a -> [(OrderOp, PrimExpr)]) -> Order a
Opaleye.Order ((Expr (Maybe a) -> [(OrderOp, PrimExpr)])
 -> Order (Expr (Maybe a)))
-> (Expr (Maybe a) -> [(OrderOp, PrimExpr)])
-> Order (Expr (Maybe a))
forall a b. (a -> b) -> a -> b
$ ((OrderOp, PrimExpr) -> (OrderOp, PrimExpr))
-> [(OrderOp, PrimExpr)] -> [(OrderOp, PrimExpr)]
forall a b. (a -> b) -> [a] -> [b]
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap ((OrderOp -> OrderOp) -> (OrderOp, PrimExpr) -> (OrderOp, PrimExpr)
forall a b c. (a -> b) -> (a, c) -> (b, c)
forall (p :: * -> * -> *) a b c.
Bifunctor p =>
(a -> b) -> p a c -> p b c
first OrderOp -> OrderOp
g) ([(OrderOp, PrimExpr)] -> [(OrderOp, PrimExpr)])
-> (Expr (Maybe a) -> [(OrderOp, PrimExpr)])
-> Expr (Maybe a)
-> [(OrderOp, PrimExpr)]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Expr a -> [(OrderOp, PrimExpr)]
f (Expr a -> [(OrderOp, PrimExpr)])
-> (Expr (Maybe a) -> Expr a)
-> Expr (Maybe a)
-> [(OrderOp, PrimExpr)]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Expr (Maybe a) -> Expr a
forall a. Expr (Maybe a) -> Expr a
unsafeUnnullify
  where
    g :: Opaleye.OrderOp -> Opaleye.OrderOp
    g :: OrderOp -> OrderOp
g OrderOp
orderOp = OrderOp
orderOp { Opaleye.orderNulls = Opaleye.NullsFirst }


-- | Transform an ordering so that @null@ values appear first. This corresponds
-- to @NULLS LAST@ in SQL.
nullsLast :: Order (Expr a) -> Order (Expr (Maybe a))
nullsLast :: forall a. Order (Expr a) -> Order (Expr (Maybe a))
nullsLast (Order (Opaleye.Order Expr a -> [(OrderOp, PrimExpr)]
f)) =
  Order (Expr (Maybe a)) -> Order (Expr (Maybe a))
forall a. Order a -> Order a
Order (Order (Expr (Maybe a)) -> Order (Expr (Maybe a)))
-> Order (Expr (Maybe a)) -> Order (Expr (Maybe a))
forall a b. (a -> b) -> a -> b
$ (Expr (Maybe a) -> [(OrderOp, PrimExpr)]) -> Order (Expr (Maybe a))
forall a. (a -> [(OrderOp, PrimExpr)]) -> Order a
Opaleye.Order ((Expr (Maybe a) -> [(OrderOp, PrimExpr)])
 -> Order (Expr (Maybe a)))
-> (Expr (Maybe a) -> [(OrderOp, PrimExpr)])
-> Order (Expr (Maybe a))
forall a b. (a -> b) -> a -> b
$ ((OrderOp, PrimExpr) -> (OrderOp, PrimExpr))
-> [(OrderOp, PrimExpr)] -> [(OrderOp, PrimExpr)]
forall a b. (a -> b) -> [a] -> [b]
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap ((OrderOp -> OrderOp) -> (OrderOp, PrimExpr) -> (OrderOp, PrimExpr)
forall a b c. (a -> b) -> (a, c) -> (b, c)
forall (p :: * -> * -> *) a b c.
Bifunctor p =>
(a -> b) -> p a c -> p b c
first OrderOp -> OrderOp
g) ([(OrderOp, PrimExpr)] -> [(OrderOp, PrimExpr)])
-> (Expr (Maybe a) -> [(OrderOp, PrimExpr)])
-> Expr (Maybe a)
-> [(OrderOp, PrimExpr)]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Expr a -> [(OrderOp, PrimExpr)]
f (Expr a -> [(OrderOp, PrimExpr)])
-> (Expr (Maybe a) -> Expr a)
-> Expr (Maybe a)
-> [(OrderOp, PrimExpr)]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Expr (Maybe a) -> Expr a
forall a. Expr (Maybe a) -> Expr a
unsafeUnnullify
  where
    g :: Opaleye.OrderOp -> Opaleye.OrderOp
    g :: OrderOp -> OrderOp
g OrderOp
orderOp = OrderOp
orderOp { Opaleye.orderNulls = Opaleye.NullsLast }