packages feed

hpgsql-simple-compat-0.1.0.0: src/Database/PostgreSQL/Simple/Range.hs

------------------------------------------------------------------------------

------------------------------------------------------------------------------

-- |
-- Module:      Database.PostgreSQL.Simple.Range
-- Copyright:   (c) 2014-2015 Leonid Onokhov
--              (c) 2014-2015 Leon P Smith
-- License:     BSD3
-- Maintainer:  Leon P Smith <leon@melding-monads.com>
module Database.PostgreSQL.Simple.Range
  ( RangeBound (..),
    PGRange (..),
    empty,
    isEmpty,
    isEmptyBy,
    contains,
    containsBy,
  )
where

import Data.Typeable (Typeable)

-- | Represents boundary of a range
data RangeBound a
  = NegInfinity
  | Inclusive !a
  | Exclusive !a
  | PosInfinity
  deriving (Show, Typeable, Eq, Functor)

-- | Generic range type
data PGRange a = PGRange !(RangeBound a) !(RangeBound a)
  deriving (Show, Typeable, Functor)

empty :: PGRange a
empty = PGRange PosInfinity NegInfinity

instance (Ord a) => Eq (PGRange a) where
  x == y = eq x y || (isEmpty x && isEmpty y)
    where
      eq (PGRange a m) (PGRange b n) = a == b && m == n

isEmptyBy :: (a -> a -> Ordering) -> PGRange a -> Bool
isEmptyBy cmp v =
  case v of
    (PGRange PosInfinity _) -> True
    (PGRange _ NegInfinity) -> True
    (PGRange NegInfinity _) -> False
    (PGRange _ PosInfinity) -> False
    (PGRange (Inclusive x) (Inclusive y)) -> cmp x y == GT
    (PGRange (Inclusive x) (Exclusive y)) -> cmp x y /= LT
    (PGRange (Exclusive x) (Inclusive y)) -> cmp x y /= LT
    (PGRange (Exclusive x) (Exclusive y)) -> cmp x y /= LT

-- | Is a range empty?   If this returns 'True',  then the 'contains'
--   predicate will always return 'False'.   However,  if this returns
--   'False', it is not necessarily true that there exists a point for
--   which 'contains' returns 'True'.
--   Consider @'PGRange' ('Excludes' 2) ('Excludes' 3) :: PGRange Int@,
--   for example.
isEmpty :: (Ord a) => PGRange a -> Bool
isEmpty = isEmptyBy compare

-- | Does a range contain a given point?   Note that in some cases, this may
-- not correspond exactly with a server-side computation.   Consider @UTCTime@
-- for example, which has a resolution of a picosecond,  whereas postgresql's
-- @timestamptz@ types have a resolution of a microsecond.  Putting such
-- Haskell values into the database will result in them being rounded, which
-- can change the value of the containment predicate.
contains :: (Ord a) => PGRange a -> (a -> Bool)
contains = containsBy compare

containsBy :: (a -> a -> Ordering) -> PGRange a -> (a -> Bool)
containsBy cmp rng x =
  case rng of
    PGRange _lb NegInfinity -> False
    PGRange lb ub -> checkLB lb x && checkUB ub x
  where
    checkLB lb y =
      case lb of
        NegInfinity -> True
        PosInfinity -> False
        Inclusive a -> cmp a y /= GT
        Exclusive a -> cmp a y == LT

    checkUB ub y =
      case ub of
        NegInfinity -> False
        PosInfinity -> True
        Inclusive z -> cmp y z /= GT
        Exclusive z -> cmp y z == LT