{-# LANGUAGE FlexibleContexts  #-}
{-# LANGUAGE OverloadedLists   #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE TypeApplications  #-}
{-# LANGUAGE TypeOperators     #-}

module Web.Eved
    ( I.Eved
    , (I.:<|>)(..)
    , (.</>)
    , (.<|>)
    , lit
    , capture
    , reqBody
    , queryParam
    , verb
    , get
    , post
    , put
    , patch
    , delete
    , runClientIO
    , runClient
    , noContext
    , withContext
    , ClientM
    , EvedServerT
    , server
    )
    where

import           Control.Applicative   (liftA2)
import           Control.Monad.Reader  (Reader, runReader)
import           Data.Function         ((&))
import           Data.Functor.Identity (Identity (..))
import           Data.List.NonEmpty    (NonEmpty)
import           Data.Text             (Text)
import           Network.HTTP.Types    (Status, StdMethod (..), status200)
import           Web.Eved.Client
import qualified Web.Eved.ContentType  as CT
import qualified Web.Eved.Internal     as I
import qualified Web.Eved.QueryParam   as QP
import           Web.Eved.Server
import qualified Web.Eved.UrlElement   as UE


-- |Unwrap an api that requires no context.
-- If none of the combinators that were used required any context use this function
-- to unwrap the api
noContext :: I.Eved api m => Identity (api a) -> api a
noContext :: Identity (api a) -> api a
noContext = Identity (api a) -> api a
forall a. Identity a -> a
runIdentity

withContext :: I.Eved api m => ctx -> (ctx -> api a) -> api a
withContext :: ctx -> (ctx -> api a) -> api a
withContext = ctx -> (ctx -> api a) -> api a
forall a b. a -> (a -> b) -> b
(&)

-- |Combine two sub-api's by trying the left api first and then the right api second.
(.<|>) :: (I.Eved api m, Applicative f) => f (api a) -> f (api b) -> f (api (a I.:<|> b))
.<|> :: f (api a) -> f (api b) -> f (api (a :<|> b))
(.<|>) =
    (api a -> api b -> api (a :<|> b))
-> f (api a) -> f (api b) -> f (api (a :<|> b))
forall (f :: * -> *) a b c.
Applicative f =>
(a -> b -> c) -> f a -> f b -> f c
liftA2 api a -> api b -> api (a :<|> b)
forall (api :: * -> *) (m :: * -> *) a b.
Eved api m =>
api a -> api b -> api (a :<|> b)
(I..<|>)

-- |Add a Literal string to the path of the api
lit :: (I.Eved api m, Applicative f) => Text -> f (api a) -> f (api a)
lit :: Text -> f (api a) -> f (api a)
lit Text
l = (api a -> api a) -> f (api a) -> f (api a)
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap (Text -> api a -> api a
forall (api :: * -> *) (m :: * -> *) a.
Eved api m =>
Text -> api a -> api a
I.lit Text
l)

-- |Add a url capture with a given name and UrlElement decoder/encoder
capture :: (I.Eved api m, Applicative f) => Text -> f (UE.UrlElement a) -> f (api b) -> f (api (a -> b))
capture :: Text -> f (UrlElement a) -> f (api b) -> f (api (a -> b))
capture Text
t f (UrlElement a)
u f (api b)
next = Text -> UrlElement a -> api b -> api (a -> b)
forall (api :: * -> *) (m :: * -> *) a b.
Eved api m =>
Text -> UrlElement a -> api b -> api (a -> b)
I.capture Text
t (UrlElement a -> api b -> api (a -> b))
-> f (UrlElement a) -> f (api b -> api (a -> b))
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> f (UrlElement a)
u f (api b -> api (a -> b)) -> f (api b) -> f (api (a -> b))
forall (f :: * -> *) a b. Applicative f => f (a -> b) -> f a -> f b
<*> f (api b)
next

-- |Add a request body parser for the given content types
-- The Content-Type header will be examined to assist in content negotiation.
reqBody :: (I.Eved api m, Applicative f) => NonEmpty (f (CT.ContentType a)) -> f (api b) -> f (api (a -> b))
reqBody :: NonEmpty (f (ContentType a)) -> f (api b) -> f (api (a -> b))
reqBody NonEmpty (f (ContentType a))
ctyps f (api b)
next = NonEmpty (ContentType a) -> api b -> api (a -> b)
forall (api :: * -> *) (m :: * -> *) a b.
Eved api m =>
NonEmpty (ContentType a) -> api b -> api (a -> b)
I.reqBody (NonEmpty (ContentType a) -> api b -> api (a -> b))
-> f (NonEmpty (ContentType a)) -> f (api b -> api (a -> b))
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> NonEmpty (f (ContentType a)) -> f (NonEmpty (ContentType a))
forall (t :: * -> *) (f :: * -> *) a.
(Traversable t, Applicative f) =>
t (f a) -> f (t a)
sequenceA NonEmpty (f (ContentType a))
ctyps f (api b -> api (a -> b)) -> f (api b) -> f (api (a -> b))
forall (f :: * -> *) a b. Applicative f => f (a -> b) -> f a -> f b
<*> f (api b)
next

-- |A single query param that is required to exist. If the argument is not required use QP.maybe
queryParam :: (I.Eved api m, Applicative f) => Text -> f (QP.QueryParam a) -> f (api b) -> f (api (a -> b))
queryParam :: Text -> f (QueryParam a) -> f (api b) -> f (api (a -> b))
queryParam Text
t f (QueryParam a)
q f (api b)
next = Text -> QueryParam a -> api b -> api (a -> b)
forall (api :: * -> *) (m :: * -> *) a b.
Eved api m =>
Text -> QueryParam a -> api b -> api (a -> b)
I.queryParam Text
t (QueryParam a -> api b -> api (a -> b))
-> f (QueryParam a) -> f (api b -> api (a -> b))
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> f (QueryParam a)
q f (api b -> api (a -> b)) -> f (api b) -> f (api (a -> b))
forall (f :: * -> *) a b. Applicative f => f (a -> b) -> f a -> f b
<*> f (api b)
next

-- |A list of query params with the same name (may return an empty list if the param is not specified ever)
queryParams :: (I.Eved api m, Applicative f) => Text -> f (QP.QueryParam a) -> f (api b) -> f (api ([a] -> b))
queryParams :: Text -> f (QueryParam a) -> f (api b) -> f (api ([a] -> b))
queryParams Text
t f (QueryParam a)
q f (api b)
next = Text -> QueryParam [a] -> api b -> api ([a] -> b)
forall (api :: * -> *) (m :: * -> *) a b.
Eved api m =>
Text -> QueryParam a -> api b -> api (a -> b)
I.queryParam Text
t (QueryParam [a] -> api b -> api ([a] -> b))
-> f (QueryParam [a]) -> f (api b -> api ([a] -> b))
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> f (QueryParam a) -> f (QueryParam [a])
forall (f :: * -> *) a.
Functor f =>
f (QueryParam a) -> f (QueryParam [a])
QP.list f (QueryParam a)
q f (api b -> api ([a] -> b)) -> f (api b) -> f (api ([a] -> b))
forall (f :: * -> *) a b. Applicative f => f (a -> b) -> f a -> f b
<*> f (api b)
next

-- |The leaf node of most routes, this will specify the HTTP Verb and Status along with a list of ContentType encoder/decoders.
-- The Allow header in the request will be examined to determine a suitable response Content-Type
verb :: (I.Eved api m, Applicative f) => StdMethod -> Status -> NonEmpty (f (CT.ContentType a)) -> f (api (m a))
verb :: StdMethod
-> Status -> NonEmpty (f (ContentType a)) -> f (api (m a))
verb StdMethod
m Status
s NonEmpty (f (ContentType a))
ctyps = (NonEmpty (ContentType a) -> api (m a))
-> f (NonEmpty (ContentType a)) -> f (api (m a))
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap (StdMethod -> Status -> NonEmpty (ContentType a) -> api (m a)
forall (api :: * -> *) (m :: * -> *) a.
Eved api m =>
StdMethod -> Status -> NonEmpty (ContentType a) -> api (m a)
I.verb StdMethod
m Status
s) (NonEmpty (f (ContentType a)) -> f (NonEmpty (ContentType a))
forall (t :: * -> *) (f :: * -> *) a.
(Traversable t, Applicative f) =>
t (f a) -> f (t a)
sequenceA NonEmpty (f (ContentType a))
ctyps)

get, post, put, patch, delete :: (I.Eved api m, Applicative f) => NonEmpty (f (CT.ContentType a)) -> f (api (m a))
-- | HTTP GET -- see verb for more info
get :: NonEmpty (f (ContentType a)) -> f (api (m a))
get = StdMethod
-> Status -> NonEmpty (f (ContentType a)) -> f (api (m a))
forall (api :: * -> *) (m :: * -> *) (f :: * -> *) a.
(Eved api m, Applicative f) =>
StdMethod
-> Status -> NonEmpty (f (ContentType a)) -> f (api (m a))
verb StdMethod
GET Status
status200
-- | HTTP POST -- see verb for more info
post :: NonEmpty (f (ContentType a)) -> f (api (m a))
post = StdMethod
-> Status -> NonEmpty (f (ContentType a)) -> f (api (m a))
forall (api :: * -> *) (m :: * -> *) (f :: * -> *) a.
(Eved api m, Applicative f) =>
StdMethod
-> Status -> NonEmpty (f (ContentType a)) -> f (api (m a))
verb StdMethod
POST Status
status200
-- | HTTP PUT -- see verb for more info
put :: NonEmpty (f (ContentType a)) -> f (api (m a))
put = StdMethod
-> Status -> NonEmpty (f (ContentType a)) -> f (api (m a))
forall (api :: * -> *) (m :: * -> *) (f :: * -> *) a.
(Eved api m, Applicative f) =>
StdMethod
-> Status -> NonEmpty (f (ContentType a)) -> f (api (m a))
verb StdMethod
PUT Status
status200
-- | HTTP PATCH -- see verb for more info
patch :: NonEmpty (f (ContentType a)) -> f (api (m a))
patch = StdMethod
-> Status -> NonEmpty (f (ContentType a)) -> f (api (m a))
forall (api :: * -> *) (m :: * -> *) (f :: * -> *) a.
(Eved api m, Applicative f) =>
StdMethod
-> Status -> NonEmpty (f (ContentType a)) -> f (api (m a))
verb StdMethod
PATCH Status
status200
-- | HTTP DELETE -- see verb for more info
delete :: NonEmpty (f (ContentType a)) -> f (api (m a))
delete = StdMethod
-> Status -> NonEmpty (f (ContentType a)) -> f (api (m a))
forall (api :: * -> *) (m :: * -> *) (f :: * -> *) a.
(Eved api m, Applicative f) =>
StdMethod
-> Status -> NonEmpty (f (ContentType a)) -> f (api (m a))
verb StdMethod
DELETE Status
status200

-- |A Segment seperator to be used between path segments akin to / in a url
-- e.g. lit "hello" .</> capture "name" UE.text .</> get [CT.json @Text]
(.</>) :: (Applicative f, I.Eved api m) =>  (f (api a) -> f (api b)) -> f (api a) -> f (api b)
.</> :: (f (api a) -> f (api b)) -> f (api a) -> f (api b)
(.</>) = (f (api a) -> f (api b)) -> f (api a) -> f (api b)
forall a b. (a -> b) -> a -> b
($)
infixr 5 .</>