{-# LANGUAGE RecordWildCards #-}
{-# LANGUAGE TypeApplications #-}
module Servant.Pagination
(
Ranges
, Range(..)
, RangeOrder(..)
, AcceptRanges (..)
, ContentRange (..)
, PageHeaders
, IsRangeType
, PutRange
, ExtractRange
, HasPagination(..)
, RangeOptions(..)
, defaultOptions
, extractRange
, putRange
, addPageHeaders
, returnRange
, applyRange
) where
import Data.List (filter, find, intercalate)
import Data.Maybe (listToMaybe)
import Data.Proxy (Proxy (..))
import Data.Semigroup ((<>))
import Data.Text (Text)
import GHC.Generics (Generic)
import GHC.TypeLits (KnownSymbol, Symbol, symbolVal)
import Network.URI.Encode (decodeText, encodeText)
import Servant
import qualified Data.List as List
import qualified Data.Text as Text
import qualified Safe
type IsRangeType a =
( Show a
, Ord a
, Eq a
, FromHttpApiData a
, ToHttpApiData a
)
data Ranges :: [Symbol] -> * -> * where
Lift :: Ranges fields resource -> Ranges (y ': fields) resource
Ranges
:: HasPagination resource field
=> Range field (RangeType resource field)
-> Ranges (field ': fields) resource
instance (Show (Ranges '[] res)) where
showsPrec :: Int -> Ranges '[] res -> ShowS
showsPrec Int
_ Ranges '[] res
_ = forall a b c. (a -> b -> c) -> b -> a -> c
flip forall a. Monoid a => a -> a -> a
mappend String
"Ranges"
instance (Show (Ranges fields res)) => Show (Ranges (field ': fields) res) where
showsPrec :: Int -> Ranges (field : fields) res -> ShowS
showsPrec Int
prec (Lift Ranges fields res
r) String
s = forall a. Show a => Int -> a -> ShowS
showsPrec Int
prec Ranges fields res
r String
s
showsPrec Int
prec (Ranges Range field (RangeType res field)
r) String
s =
let
inner :: String
inner = String
"Ranges@" forall a. [a] -> [a] -> [a]
++ forall a. Show a => Int -> a -> ShowS
showsPrec Int
11 Range field (RangeType res field)
r String
s
in
if Int
prec forall a. Ord a => a -> a -> Bool
> Int
10 then String
"(" forall a. [a] -> [a] -> [a]
++ String
inner forall a. [a] -> [a] -> [a]
++ String
")" else String
inner
data Range (field :: Symbol) (a :: *) =
(KnownSymbol field, IsRangeType a) => Range
{ forall (field :: Symbol) a. Range field a -> Maybe a
rangeValue :: Maybe a
, forall (field :: Symbol) a. Range field a -> Int
rangeLimit :: Int
, forall (field :: Symbol) a. Range field a -> Int
rangeOffset :: Int
, forall (field :: Symbol) a. Range field a -> RangeOrder
rangeOrder :: RangeOrder
, forall (field :: Symbol) a. Range field a -> Proxy field
rangeField :: Proxy field
}
instance Eq (Range field a) where
(Range Maybe a
val0 Int
lim0 Int
off0 RangeOrder
ord0 Proxy field
_) == :: Range field a -> Range field a -> Bool
== (Range Maybe a
val1 Int
lim1 Int
off1 RangeOrder
ord1 Proxy field
_) =
Maybe a
val0 forall a. Eq a => a -> a -> Bool
== Maybe a
val1
Bool -> Bool -> Bool
&& Int
lim0 forall a. Eq a => a -> a -> Bool
== Int
lim1
Bool -> Bool -> Bool
&& Int
off0 forall a. Eq a => a -> a -> Bool
== Int
off1
Bool -> Bool -> Bool
&& RangeOrder
ord0 forall a. Eq a => a -> a -> Bool
== RangeOrder
ord1
instance Show (Range field a) where
showsPrec :: Int -> Range field a -> ShowS
showsPrec Int
prec Range{Int
Maybe a
Proxy field
RangeOrder
rangeField :: Proxy field
rangeOrder :: RangeOrder
rangeOffset :: Int
rangeLimit :: Int
rangeValue :: Maybe a
rangeField :: forall (field :: Symbol) a. Range field a -> Proxy field
rangeOrder :: forall (field :: Symbol) a. Range field a -> RangeOrder
rangeOffset :: forall (field :: Symbol) a. Range field a -> Int
rangeLimit :: forall (field :: Symbol) a. Range field a -> Int
rangeValue :: forall (field :: Symbol) a. Range field a -> Maybe a
..} =
let
inner :: String
inner = String
"Range {" forall a. [a] -> [a] -> [a]
++ String
args forall a. [a] -> [a] -> [a]
++ String
"}"
args :: String
args = forall a. [a] -> [[a]] -> [a]
intercalate String
", "
[ String
"rangeValue = " forall a. [a] -> [a] -> [a]
++ forall a. Show a => a -> String
show Maybe a
rangeValue
, String
"rangeLimit = " forall a. [a] -> [a] -> [a]
++ forall a. Show a => a -> String
show Int
rangeLimit
, String
"rangeOffset = " forall a. [a] -> [a] -> [a]
++ forall a. Show a => a -> String
show Int
rangeOffset
, String
"rangeOrder = " forall a. [a] -> [a] -> [a]
++ forall a. Show a => a -> String
show RangeOrder
rangeOrder
, String
"rangeField = " forall a. [a] -> [a] -> [a]
++ String
"\"" forall a. [a] -> [a] -> [a]
++ forall (n :: Symbol) (proxy :: Symbol -> *).
KnownSymbol n =>
proxy n -> String
symbolVal Proxy field
rangeField forall a. [a] -> [a] -> [a]
++ String
"\""
]
in
forall a b c. (a -> b -> c) -> b -> a -> c
flip forall a. Monoid a => a -> a -> a
mappend forall a b. (a -> b) -> a -> b
$ if Int
prec forall a. Ord a => a -> a -> Bool
> Int
10 then
String
"(" forall a. [a] -> [a] -> [a]
++ String
inner forall a. [a] -> [a] -> [a]
++ String
")"
else
String
inner
class (fields :: [Symbol]) (field :: Symbol) where
:: HasPagination resource field
=> Ranges fields resource
-> Maybe (Range field (RangeType resource field))
instance ExtractRange (field ': fields) field where
extractRange :: forall resource.
HasPagination resource field =>
Ranges (field : fields) resource
-> Maybe (Range field (RangeType resource field))
extractRange (Ranges Range field (RangeType resource field)
r) = forall a. a -> Maybe a
Just Range field (RangeType resource field)
r
extractRange (Lift Ranges fields resource
_) = forall a. Maybe a
Nothing
{-# INLINE extractRange #-}
instance {-# OVERLAPPABLE #-} ExtractRange fields field => ExtractRange (y ': fields) field where
extractRange :: forall resource.
HasPagination resource field =>
Ranges (y : fields) resource
-> Maybe (Range field (RangeType resource field))
extractRange (Ranges Range field (RangeType resource field)
_) = forall a. Maybe a
Nothing
extractRange (Lift Ranges fields resource
r) = forall (fields :: [Symbol]) (field :: Symbol) resource.
(ExtractRange fields field, HasPagination resource field) =>
Ranges fields resource
-> Maybe (Range field (RangeType resource field))
extractRange Ranges fields resource
r
{-# INLINE extractRange #-}
class PutRange (fields :: [Symbol]) (field :: Symbol) where
putRange
:: HasPagination resource field
=> Range field (RangeType resource field)
-> Ranges fields resource
instance PutRange (field ': fields) field where
putRange :: forall resource.
HasPagination resource field =>
Range field (RangeType resource field)
-> Ranges (field : fields) resource
putRange = forall resource (fields :: Symbol) (y :: [Symbol]).
HasPagination resource fields =>
Range fields (RangeType resource fields)
-> Ranges (fields : y) resource
Ranges
{-# INLINE putRange #-}
instance {-# OVERLAPPABLE #-} (PutRange fields field) => PutRange (y ': fields) field where
putRange :: forall resource.
HasPagination resource field =>
Range field (RangeType resource field)
-> Ranges (y : fields) resource
putRange = forall (fields :: [Symbol]) resource (y :: Symbol).
Ranges fields resource -> Ranges (y : fields) resource
Lift forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall (fields :: [Symbol]) (field :: Symbol) resource.
(PutRange fields field, HasPagination resource field) =>
Range field (RangeType resource field) -> Ranges fields resource
putRange
{-# INLINE putRange #-}
instance ToHttpApiData (Ranges fields resource) where
toUrlPiece :: Ranges fields resource -> Text
toUrlPiece (Lift Ranges fields resource
range) =
forall a. ToHttpApiData a => a -> Text
toUrlPiece Ranges fields resource
range
toUrlPiece (Ranges Range{Int
Maybe (RangeType resource field)
Proxy field
RangeOrder
rangeField :: Proxy field
rangeOrder :: RangeOrder
rangeOffset :: Int
rangeLimit :: Int
rangeValue :: Maybe (RangeType resource field)
rangeField :: forall (field :: Symbol) a. Range field a -> Proxy field
rangeOrder :: forall (field :: Symbol) a. Range field a -> RangeOrder
rangeOffset :: forall (field :: Symbol) a. Range field a -> Int
rangeLimit :: forall (field :: Symbol) a. Range field a -> Int
rangeValue :: forall (field :: Symbol) a. Range field a -> Maybe a
..}) =
String -> Text
Text.pack (forall (n :: Symbol) (proxy :: Symbol -> *).
KnownSymbol n =>
proxy n -> String
symbolVal Proxy field
rangeField)
forall a. Semigroup a => a -> a -> a
<> forall b a. b -> (a -> b) -> Maybe a -> b
maybe Text
"" (\RangeType resource field
v -> Text
" " forall a. Semigroup a => a -> a -> a
<> (Text -> Text
encodeText forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall a. ToHttpApiData a => a -> Text
toUrlPiece) RangeType resource field
v) Maybe (RangeType resource field)
rangeValue
forall a. Semigroup a => a -> a -> a
<> Text
";limit " forall a. Semigroup a => a -> a -> a
<> forall a. ToHttpApiData a => a -> Text
toUrlPiece Int
rangeLimit
forall a. Semigroup a => a -> a -> a
<> Text
";offset " forall a. Semigroup a => a -> a -> a
<> forall a. ToHttpApiData a => a -> Text
toUrlPiece Int
rangeOffset
forall a. Semigroup a => a -> a -> a
<> Text
";order " forall a. Semigroup a => a -> a -> a
<> forall a. ToHttpApiData a => a -> Text
toUrlPiece RangeOrder
rangeOrder
instance FromHttpApiData (Ranges '[] resource) where
parseUrlPiece :: Text -> Either Text (Ranges '[] resource)
parseUrlPiece Text
_ =
forall a b. a -> Either a b
Left Text
"Invalid Range"
instance
( FromHttpApiData (Ranges fields resource)
, HasPagination resource field
, KnownSymbol field
, IsRangeType (RangeType resource field)
) => FromHttpApiData (Ranges (field ': fields) resource) where
parseUrlPiece :: Text -> Either Text (Ranges (field : fields) resource)
parseUrlPiece Text
txt =
let
RangeOptions{Int
RangeOrder
defaultRangeOrder :: RangeOptions -> RangeOrder
defaultRangeOffset :: RangeOptions -> Int
defaultRangeLimit :: RangeOptions -> Int
defaultRangeOrder :: RangeOrder
defaultRangeOffset :: Int
defaultRangeLimit :: Int
..} = forall resource (field :: Symbol).
HasPagination resource field =>
Proxy field -> Proxy resource -> RangeOptions
getRangeOptions (forall {k} (t :: k). Proxy t
Proxy @field) (forall {k} (t :: k). Proxy t
Proxy @resource)
toTuples :: Text -> [Text]
toTuples =
forall a. (a -> Bool) -> [a] -> [a]
filter (forall a. Eq a => a -> a -> Bool
/= Text
"") forall b c a. (b -> c) -> (a -> b) -> a -> c
. Text -> Text -> [Text]
Text.splitOn (Char -> Text
Text.singleton Char
' ')
args :: [[Text]]
args =
forall a b. (a -> b) -> [a] -> [b]
map Text -> [Text]
toTuples forall a b. (a -> b) -> a -> b
$ Text -> Text -> [Text]
Text.splitOn (Char -> Text
Text.singleton Char
';') Text
txt
field :: Text
field =
String -> Text
Text.pack forall a b. (a -> b) -> a -> b
$ forall (n :: Symbol) (proxy :: Symbol -> *).
KnownSymbol n =>
proxy n -> String
symbolVal (forall {k} (t :: k). Proxy t
Proxy @field)
in
case [[Text]]
args of
(Text
field' : [Text]
value) : [[Text]]
rest | Text
field forall a. Eq a => a -> a -> Bool
== Text
field' -> do
[(Text, Text)]
opts <-
forall (t :: * -> *) (f :: * -> *) a b.
(Traversable t, Applicative f) =>
(a -> f b) -> t a -> f (t b)
traverse [Text] -> Either Text (Text, Text)
parseOpt [[Text]]
rest
Range field (RangeType resource field)
range <- forall (field :: Symbol) a.
(KnownSymbol field, IsRangeType a) =>
Maybe a -> Int -> Int -> RangeOrder -> Proxy field -> Range field a
Range
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> forall (t :: * -> *) (m :: * -> *) a b.
(Traversable t, Monad m) =>
(a -> m b) -> t a -> m (t b)
mapM (forall a. FromHttpApiData a => Text -> Either Text a
parseUrlPiece forall b c a. (b -> c) -> (a -> b) -> a -> c
. Text -> Text
decodeText) (forall a. [a] -> Maybe a
listToMaybe [Text]
value)
forall (f :: * -> *) a b. Applicative f => f (a -> b) -> f a -> f b
<*> (forall o.
FromHttpApiData o =>
Text -> o -> [(Text, Text)] -> Either Text o
ifOpt Text
"limit" Int
defaultRangeLimit [(Text, Text)]
opts forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= Int -> Either Text Int
checkLimit)
forall (f :: * -> *) a b. Applicative f => f (a -> b) -> f a -> f b
<*> forall o.
FromHttpApiData o =>
Text -> o -> [(Text, Text)] -> Either Text o
ifOpt Text
"offset" Int
defaultRangeOffset [(Text, Text)]
opts
forall (f :: * -> *) a b. Applicative f => f (a -> b) -> f a -> f b
<*> forall o.
FromHttpApiData o =>
Text -> o -> [(Text, Text)] -> Either Text o
ifOpt Text
"order" RangeOrder
defaultRangeOrder [(Text, Text)]
opts
forall (f :: * -> *) a b. Applicative f => f (a -> b) -> f a -> f b
<*> forall (f :: * -> *) a. Applicative f => a -> f a
pure (forall {k} (t :: k). Proxy t
Proxy @field)
forall (f :: * -> *) a. Applicative f => a -> f a
pure forall a b. (a -> b) -> a -> b
$ forall resource (fields :: Symbol) (y :: [Symbol]).
HasPagination resource fields =>
Range fields (RangeType resource fields)
-> Ranges (fields : y) resource
Ranges Range field (RangeType resource field)
range
[[Text]]
_ ->
forall (fields :: [Symbol]) resource (y :: Symbol).
Ranges fields resource -> Ranges (y : fields) resource
Lift forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> (forall a. FromHttpApiData a => Text -> Either Text a
parseUrlPiece Text
txt :: Either Text (Ranges fields resource))
where
parseOpt :: [Text] -> Either Text (Text, Text)
parseOpt :: [Text] -> Either Text (Text, Text)
parseOpt [Text]
piece =
case [Text]
piece of
[Text
opt, Text
arg] ->
forall (f :: * -> *) a. Applicative f => a -> f a
pure (Text
opt, Text
arg)
[Text]
_ ->
forall a b. a -> Either a b
Left Text
"Invalid Range Options"
ifOpt :: FromHttpApiData o => Text -> o -> [(Text, Text)] -> Either Text o
ifOpt :: forall o.
FromHttpApiData o =>
Text -> o -> [(Text, Text)] -> Either Text o
ifOpt Text
opt o
def =
forall b a. b -> (a -> b) -> Maybe a -> b
maybe (forall (f :: * -> *) a. Applicative f => a -> f a
pure o
def) (forall a. FromHttpApiData a => Text -> Either Text a
parseQueryParam forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall a b. (a, b) -> b
snd) forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall (t :: * -> *) a. Foldable t => (a -> Bool) -> t a -> Maybe a
find ((forall a. Eq a => a -> a -> Bool
== Text
opt) forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall a b. (a, b) -> a
fst)
checkLimit :: Int -> Either Text Int
checkLimit :: Int -> Either Text Int
checkLimit Int
n
| Int
n forall a. Ord a => a -> a -> Bool
< Int
0 = forall a b. a -> Either a b
Left Text
"Limit must be non-negative"
| Bool
otherwise = forall (m :: * -> *) a. Monad m => a -> m a
return Int
n
data RangeOrder
= RangeAsc
| RangeDesc
deriving (RangeOrder -> RangeOrder -> Bool
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
/= :: RangeOrder -> RangeOrder -> Bool
$c/= :: RangeOrder -> RangeOrder -> Bool
== :: RangeOrder -> RangeOrder -> Bool
$c== :: RangeOrder -> RangeOrder -> Bool
Eq, Int -> RangeOrder -> ShowS
[RangeOrder] -> ShowS
RangeOrder -> String
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
showList :: [RangeOrder] -> ShowS
$cshowList :: [RangeOrder] -> ShowS
show :: RangeOrder -> String
$cshow :: RangeOrder -> String
showsPrec :: Int -> RangeOrder -> ShowS
$cshowsPrec :: Int -> RangeOrder -> ShowS
Show, Eq RangeOrder
RangeOrder -> RangeOrder -> Bool
RangeOrder -> RangeOrder -> Ordering
RangeOrder -> RangeOrder -> RangeOrder
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
min :: RangeOrder -> RangeOrder -> RangeOrder
$cmin :: RangeOrder -> RangeOrder -> RangeOrder
max :: RangeOrder -> RangeOrder -> RangeOrder
$cmax :: RangeOrder -> RangeOrder -> RangeOrder
>= :: RangeOrder -> RangeOrder -> Bool
$c>= :: RangeOrder -> RangeOrder -> Bool
> :: RangeOrder -> RangeOrder -> Bool
$c> :: RangeOrder -> RangeOrder -> Bool
<= :: RangeOrder -> RangeOrder -> Bool
$c<= :: RangeOrder -> RangeOrder -> Bool
< :: RangeOrder -> RangeOrder -> Bool
$c< :: RangeOrder -> RangeOrder -> Bool
compare :: RangeOrder -> RangeOrder -> Ordering
$ccompare :: RangeOrder -> RangeOrder -> Ordering
Ord, forall x. Rep RangeOrder x -> RangeOrder
forall x. RangeOrder -> Rep RangeOrder x
forall a.
(forall x. a -> Rep a x) -> (forall x. Rep a x -> a) -> Generic a
$cto :: forall x. Rep RangeOrder x -> RangeOrder
$cfrom :: forall x. RangeOrder -> Rep RangeOrder x
Generic)
instance ToHttpApiData RangeOrder where
toUrlPiece :: RangeOrder -> Text
toUrlPiece RangeOrder
order =
case RangeOrder
order of
RangeOrder
RangeAsc -> Text
"asc"
RangeOrder
RangeDesc -> Text
"desc"
instance FromHttpApiData RangeOrder where
parseUrlPiece :: Text -> Either Text RangeOrder
parseUrlPiece Text
txt =
case Text
txt of
Text
"asc" -> forall (f :: * -> *) a. Applicative f => a -> f a
pure RangeOrder
RangeAsc
Text
"desc" -> forall (f :: * -> *) a. Applicative f => a -> f a
pure RangeOrder
RangeDesc
Text
_ -> forall a b. a -> Either a b
Left Text
"Invalid Range Order"
type (fields :: [Symbol]) (resource :: *) =
'[ Header "Accept-Ranges" (AcceptRanges fields)
, Header "Content-Range" (ContentRange fields resource)
, Header "Next-Range" (Ranges fields resource)
]
data AcceptRanges (fields :: [Symbol]) = AcceptRanges
instance (KnownSymbol field) => ToHttpApiData (AcceptRanges '[field]) where
toUrlPiece :: AcceptRanges '[field] -> Text
toUrlPiece AcceptRanges '[field]
AcceptRanges =
String -> Text
Text.pack (forall (n :: Symbol) (proxy :: Symbol -> *).
KnownSymbol n =>
proxy n -> String
symbolVal (forall {k} (t :: k). Proxy t
Proxy @field))
instance (ToHttpApiData (AcceptRanges (f ': fs)), KnownSymbol field) => ToHttpApiData (AcceptRanges (field ': f ': fs)) where
toUrlPiece :: AcceptRanges (field : f : fs) -> Text
toUrlPiece AcceptRanges (field : f : fs)
AcceptRanges =
String -> Text
Text.pack (forall (n :: Symbol) (proxy :: Symbol -> *).
KnownSymbol n =>
proxy n -> String
symbolVal (forall {k} (t :: k). Proxy t
Proxy @field)) forall a. Semigroup a => a -> a -> a
<> Text
"," forall a. Semigroup a => a -> a -> a
<> forall a. ToHttpApiData a => a -> Text
toUrlPiece (forall (fields :: [Symbol]). AcceptRanges fields
AcceptRanges @(f ': fs))
data ContentRange (fields :: [Symbol]) resource =
forall field. (KnownSymbol field, ToHttpApiData (RangeType resource field)) => ContentRange
{ ()
contentRangeStart :: RangeType resource field
, ()
contentRangeEnd :: RangeType resource field
, ()
contentRangeField :: Proxy field
}
instance ToHttpApiData (ContentRange fields res) where
toUrlPiece :: ContentRange fields res -> Text
toUrlPiece (ContentRange RangeType res field
start RangeType res field
end Proxy field
field) =
String -> Text
Text.pack (forall (n :: Symbol) (proxy :: Symbol -> *).
KnownSymbol n =>
proxy n -> String
symbolVal Proxy field
field) forall a. Semigroup a => a -> a -> a
<> Text
" " forall a. Semigroup a => a -> a -> a
<> (Text -> Text
encodeText forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall a. ToHttpApiData a => a -> Text
toUrlPiece) RangeType res field
start forall a. Semigroup a => a -> a -> a
<> Text
".." forall a. Semigroup a => a -> a -> a
<> (Text -> Text
encodeText forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall a. ToHttpApiData a => a -> Text
toUrlPiece) RangeType res field
end
class KnownSymbol field => resource field where
type RangeType resource field :: *
getFieldValue :: Proxy field -> resource -> RangeType resource field
getRangeOptions :: Proxy field -> Proxy resource -> RangeOptions
getRangeOptions Proxy field
_ Proxy resource
_ = RangeOptions
defaultOptions
getDefaultRange
:: IsRangeType (RangeType resource field)
=> Proxy resource
-> Range field (RangeType resource field)
getDefaultRange Proxy resource
_ =
let
RangeOptions{Int
RangeOrder
defaultRangeOrder :: RangeOrder
defaultRangeOffset :: Int
defaultRangeLimit :: Int
defaultRangeOrder :: RangeOptions -> RangeOrder
defaultRangeOffset :: RangeOptions -> Int
defaultRangeLimit :: RangeOptions -> Int
..} = forall resource (field :: Symbol).
HasPagination resource field =>
Proxy field -> Proxy resource -> RangeOptions
getRangeOptions (forall {k} (t :: k). Proxy t
Proxy @field) (forall {k} (t :: k). Proxy t
Proxy @resource)
in Range
{ rangeValue :: Maybe (RangeType resource field)
rangeValue = forall a. Maybe a
Nothing @(RangeType resource field)
, rangeLimit :: Int
rangeLimit = Int
defaultRangeLimit
, rangeOffset :: Int
rangeOffset = Int
defaultRangeOffset
, rangeOrder :: RangeOrder
rangeOrder = RangeOrder
defaultRangeOrder
, rangeField :: Proxy field
rangeField = forall {k} (t :: k). Proxy t
Proxy @field
}
addPageHeaders
:: ( ToHttpApiData (AcceptRanges fields)
, KnownSymbol field
, HasPagination resource field
, IsRangeType (RangeType resource field)
, PutRange fields field
)
=> Range field (RangeType resource field)
-> [resource]
-> Headers (PageHeaders fields resource) [resource]
Range{Int
Maybe (RangeType resource field)
Proxy field
RangeOrder
rangeField :: Proxy field
rangeOrder :: RangeOrder
rangeOffset :: Int
rangeLimit :: Int
rangeValue :: Maybe (RangeType resource field)
rangeField :: forall (field :: Symbol) a. Range field a -> Proxy field
rangeOrder :: forall (field :: Symbol) a. Range field a -> RangeOrder
rangeOffset :: forall (field :: Symbol) a. Range field a -> Int
rangeLimit :: forall (field :: Symbol) a. Range field a -> Int
rangeValue :: forall (field :: Symbol) a. Range field a -> Maybe a
..} [resource]
rs =
let boundaries :: Maybe (RangeType resource field, RangeType resource field)
boundaries = (,)
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap (forall resource (field :: Symbol).
HasPagination resource field =>
Proxy field -> resource -> RangeType resource field
getFieldValue Proxy field
rangeField) (forall a. [a] -> Maybe a
Safe.headMay [resource]
rs)
forall (f :: * -> *) a b. Applicative f => f (a -> b) -> f a -> f b
<*> forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap (forall resource (field :: Symbol).
HasPagination resource field =>
Proxy field -> resource -> RangeType resource field
getFieldValue Proxy field
rangeField) (forall a. [a] -> Maybe a
Safe.lastMay [resource]
rs)
in case Maybe (RangeType resource field, RangeType resource field)
boundaries of
Maybe (RangeType resource field, RangeType resource field)
Nothing ->
forall (h :: Symbol) v orig new.
AddHeader h v orig new =>
v -> orig -> new
addHeader forall (fields :: [Symbol]). AcceptRanges fields
AcceptRanges forall a b. (a -> b) -> a -> b
$ forall (h :: Symbol) v orig new.
AddHeader h v orig new =>
orig -> new
noHeader forall a b. (a -> b) -> a -> b
$ forall (h :: Symbol) v orig new.
AddHeader h v orig new =>
orig -> new
noHeader [resource]
rs
Just (RangeType resource field
start, RangeType resource field
end) -> do
let nextOffset :: Int
nextOffset | Maybe (RangeType resource field)
rangeValue forall a. Eq a => a -> a -> Bool
== forall a. a -> Maybe a
Just RangeType resource field
end = Int
rangeOffset forall a. Num a => a -> a -> a
+ forall (t :: * -> *) a. Foldable t => t a -> Int
length [resource]
rs
| Bool
otherwise = forall (t :: * -> *) a. Foldable t => t a -> Int
length forall a b. (a -> b) -> a -> b
$ forall a. (a -> Bool) -> [a] -> [a]
takeWhile (forall a. Eq a => a -> a -> Bool
(==) RangeType resource field
end forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall resource (field :: Symbol).
HasPagination resource field =>
Proxy field -> resource -> RangeType resource field
getFieldValue Proxy field
rangeField) (forall a. [a] -> [a]
reverse [resource]
rs)
let nextRange :: Ranges fields resource
nextRange = forall (fields :: [Symbol]) (field :: Symbol) resource.
(PutRange fields field, HasPagination resource field) =>
Range field (RangeType resource field) -> Ranges fields resource
putRange Range
{ rangeValue :: Maybe (RangeType resource field)
rangeValue = forall a. a -> Maybe a
Just RangeType resource field
end
, rangeLimit :: Int
rangeLimit = Int
rangeLimit
, rangeOffset :: Int
rangeOffset = Int
nextOffset
, rangeOrder :: RangeOrder
rangeOrder = RangeOrder
rangeOrder
, rangeField :: Proxy field
rangeField = Proxy field
rangeField
}
let contentRange :: ContentRange fields resource
contentRange = ContentRange
{ contentRangeStart :: RangeType resource field
contentRangeStart = RangeType resource field
start
, contentRangeEnd :: RangeType resource field
contentRangeEnd = RangeType resource field
end
, contentRangeField :: Proxy field
contentRangeField = Proxy field
rangeField
}
let addNextRange :: [resource]
-> Headers
'[Header "Next-Range" (Ranges fields resource)] [resource]
addNextRange | forall (t :: * -> *) a. Foldable t => t a -> Int
length [resource]
rs forall a. Ord a => a -> a -> Bool
< Int
rangeLimit = forall (h :: Symbol) v orig new.
AddHeader h v orig new =>
orig -> new
noHeader
| Bool
otherwise = forall (h :: Symbol) v orig new.
AddHeader h v orig new =>
v -> orig -> new
addHeader Ranges fields resource
nextRange
forall (h :: Symbol) v orig new.
AddHeader h v orig new =>
v -> orig -> new
addHeader forall (fields :: [Symbol]). AcceptRanges fields
AcceptRanges forall a b. (a -> b) -> a -> b
$ forall (h :: Symbol) v orig new.
AddHeader h v orig new =>
v -> orig -> new
addHeader ContentRange fields resource
contentRange forall a b. (a -> b) -> a -> b
$ [resource]
-> Headers
'[Header "Next-Range" (Ranges fields resource)] [resource]
addNextRange [resource]
rs
returnRange
:: ( Monad m
, ToHttpApiData (AcceptRanges fields)
, KnownSymbol field
, HasPagination resource field
, IsRangeType (RangeType resource field)
, PutRange fields field
)
=> Range field (RangeType resource field)
-> [resource]
-> m (Headers (PageHeaders fields resource) [resource])
returnRange :: forall (m :: * -> *) (fields :: [Symbol]) (field :: Symbol)
resource.
(Monad m, ToHttpApiData (AcceptRanges fields), KnownSymbol field,
HasPagination resource field,
IsRangeType (RangeType resource field), PutRange fields field) =>
Range field (RangeType resource field)
-> [resource]
-> m (Headers (PageHeaders fields resource) [resource])
returnRange Range field (RangeType resource field)
range [resource]
rs = forall (m :: * -> *) a. Monad m => a -> m a
return (forall (fields :: [Symbol]) (field :: Symbol) resource.
(ToHttpApiData (AcceptRanges fields), KnownSymbol field,
HasPagination resource field,
IsRangeType (RangeType resource field), PutRange fields field) =>
Range field (RangeType resource field)
-> [resource] -> Headers (PageHeaders fields resource) [resource]
addPageHeaders Range field (RangeType resource field)
range [resource]
rs)
data RangeOptions = RangeOptions
{ RangeOptions -> Int
defaultRangeLimit :: Int
, RangeOptions -> Int
defaultRangeOffset :: Int
, RangeOptions -> RangeOrder
defaultRangeOrder :: RangeOrder
} deriving (RangeOptions -> RangeOptions -> Bool
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
/= :: RangeOptions -> RangeOptions -> Bool
$c/= :: RangeOptions -> RangeOptions -> Bool
== :: RangeOptions -> RangeOptions -> Bool
$c== :: RangeOptions -> RangeOptions -> Bool
Eq, Int -> RangeOptions -> ShowS
[RangeOptions] -> ShowS
RangeOptions -> String
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
showList :: [RangeOptions] -> ShowS
$cshowList :: [RangeOptions] -> ShowS
show :: RangeOptions -> String
$cshow :: RangeOptions -> String
showsPrec :: Int -> RangeOptions -> ShowS
$cshowsPrec :: Int -> RangeOptions -> ShowS
Show)
defaultOptions :: RangeOptions
defaultOptions :: RangeOptions
defaultOptions =
Int -> Int -> RangeOrder -> RangeOptions
RangeOptions Int
100 Int
0 RangeOrder
RangeDesc
applyRange
:: HasPagination resource field
=> Range field (RangeType resource field)
-> [resource]
-> [resource]
applyRange :: forall resource (field :: Symbol).
HasPagination resource field =>
Range field (RangeType resource field) -> [resource] -> [resource]
applyRange Range{Int
Maybe (RangeType resource field)
Proxy field
RangeOrder
rangeField :: Proxy field
rangeOrder :: RangeOrder
rangeOffset :: Int
rangeLimit :: Int
rangeValue :: Maybe (RangeType resource field)
rangeField :: forall (field :: Symbol) a. Range field a -> Proxy field
rangeOrder :: forall (field :: Symbol) a. Range field a -> RangeOrder
rangeOffset :: forall (field :: Symbol) a. Range field a -> Int
rangeLimit :: forall (field :: Symbol) a. Range field a -> Int
rangeValue :: forall (field :: Symbol) a. Range field a -> Maybe a
..} =
let
sortRel :: resource -> resource -> Ordering
sortRel =
case RangeOrder
rangeOrder of
RangeOrder
RangeDesc ->
\resource
a resource
b -> forall a. Ord a => a -> a -> Ordering
compare (forall resource (field :: Symbol).
HasPagination resource field =>
Proxy field -> resource -> RangeType resource field
getFieldValue Proxy field
rangeField resource
b) (forall resource (field :: Symbol).
HasPagination resource field =>
Proxy field -> resource -> RangeType resource field
getFieldValue Proxy field
rangeField resource
a)
RangeOrder
RangeAsc ->
\resource
a resource
b -> forall a. Ord a => a -> a -> Ordering
compare (forall resource (field :: Symbol).
HasPagination resource field =>
Proxy field -> resource -> RangeType resource field
getFieldValue Proxy field
rangeField resource
a) (forall resource (field :: Symbol).
HasPagination resource field =>
Proxy field -> resource -> RangeType resource field
getFieldValue Proxy field
rangeField resource
b)
dropRel :: resource -> Bool
dropRel =
case (Maybe (RangeType resource field)
rangeValue, RangeOrder
rangeOrder) of
(Maybe (RangeType resource field)
Nothing, RangeOrder
_) ->
forall a b. a -> b -> a
const Bool
False
(Just RangeType resource field
a, RangeOrder
RangeDesc) ->
(forall a. Ord a => a -> a -> Bool
> RangeType resource field
a) forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall resource (field :: Symbol).
HasPagination resource field =>
Proxy field -> resource -> RangeType resource field
getFieldValue Proxy field
rangeField
(Just RangeType resource field
a, RangeOrder
RangeAsc) ->
(forall a. Ord a => a -> a -> Bool
< RangeType resource field
a) forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall resource (field :: Symbol).
HasPagination resource field =>
Proxy field -> resource -> RangeType resource field
getFieldValue Proxy field
rangeField
in
forall a. Int -> [a] -> [a]
List.take Int
rangeLimit
forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall a. Int -> [a] -> [a]
List.drop Int
rangeOffset
forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall a. (a -> Bool) -> [a] -> [a]
List.dropWhile resource -> Bool
dropRel
forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall a. (a -> a -> Ordering) -> [a] -> [a]
List.sortBy resource -> resource -> Ordering
sortRel