{-# LANGUAGE FlexibleInstances     #-}
{-# LANGUAGE KindSignatures        #-}
{-# LANGUAGE LambdaCase            #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE OverloadedStrings     #-}

module Web.Eved.Options
    where

import qualified Data.ByteString    as B
import           Data.Coerce        (coerce)
import qualified Data.List          as List
import           Data.Text          (Text)
import           Network.HTTP.Types
import           Network.Wai
import           Web.Eved.Internal

provideOptions :: EvedOptions m a -> Middleware
provideOptions :: EvedOptions m a -> Middleware
provideOptions EvedOptions m a
api Application
app Request
req Response -> IO ResponseReceived
respond
  | Request -> Method
requestMethod Request
req Method -> Method -> Bool
forall a. Eq a => a -> a -> Bool
== Method
"OPTIONS" = Response -> IO ResponseReceived
respond (EvedOptions m a -> Request -> Response
forall (m :: * -> *) a. EvedOptions m a -> Request -> Response
getOptionsResponse EvedOptions m a
api Request
req)
  | Bool
otherwise = Application
app Request
req Response -> IO ResponseReceived
respond

getOptionsResponse :: EvedOptions m a -> Request -> Response
getOptionsResponse :: EvedOptions m a -> Request -> Response
getOptionsResponse EvedOptions m a
api Request
req =
    let methods :: [Method]
methods = StdMethod -> Method
renderStdMethod (StdMethod -> Method) -> [StdMethod] -> [Method]
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> EvedOptions m a -> [Text] -> [StdMethod]
forall (m :: * -> *) a. EvedOptions m a -> [Text] -> [StdMethod]
getAvailableMethods EvedOptions m a
api ((Text -> Bool) -> [Text] -> [Text]
forall a. (a -> Bool) -> [a] -> [a]
List.dropWhileEnd (Text -> Text -> Bool
forall a. Eq a => a -> a -> Bool
== Text
"") (Request -> [Text]
pathInfo Request
req))
        headers :: [(HeaderName, Method)]
headers = [ (HeaderName
"Allow", Method -> [Method] -> Method
B.intercalate Method
", " ([Method] -> Method) -> [Method] -> Method
forall a b. (a -> b) -> a -> b
$ Method
"OPTIONS"Method -> [Method] -> [Method]
forall a. a -> [a] -> [a]
:[Method]
methods) ]
    in Status -> [(HeaderName, Method)] -> Builder -> Response
responseBuilder Status
status200 [(HeaderName, Method)]
headers Builder
forall a. Monoid a => a
mempty

newtype EvedOptions (m :: * -> *) a = EvedOptions
    { EvedOptions m a -> [Text] -> [StdMethod]
getAvailableMethods :: [Text] -> [StdMethod]
    }

passthrough :: EvedOptions m a -> EvedOptions m b
passthrough :: EvedOptions m a -> EvedOptions m b
passthrough = EvedOptions m a -> EvedOptions m b
coerce

instance Eved (EvedOptions m) m where
    EvedOptions m a
left .<|> :: EvedOptions m a -> EvedOptions m b -> EvedOptions m (a :<|> b)
.<|> EvedOptions m b
right = ([Text] -> [StdMethod]) -> EvedOptions m (a :<|> b)
forall (m :: * -> *) a. ([Text] -> [StdMethod]) -> EvedOptions m a
EvedOptions (([Text] -> [StdMethod]) -> EvedOptions m (a :<|> b))
-> ([Text] -> [StdMethod]) -> EvedOptions m (a :<|> b)
forall a b. (a -> b) -> a -> b
$ \[Text]
path ->
        EvedOptions m a -> [Text] -> [StdMethod]
forall (m :: * -> *) a. EvedOptions m a -> [Text] -> [StdMethod]
getAvailableMethods EvedOptions m a
left [Text]
path [StdMethod] -> [StdMethod] -> [StdMethod]
forall a. Semigroup a => a -> a -> a
<> EvedOptions m b -> [Text] -> [StdMethod]
forall (m :: * -> *) a. EvedOptions m a -> [Text] -> [StdMethod]
getAvailableMethods EvedOptions m b
right [Text]
path

    lit :: Text -> EvedOptions m a -> EvedOptions m a
lit Text
t EvedOptions m a
next = ([Text] -> [StdMethod]) -> EvedOptions m a
forall (m :: * -> *) a. ([Text] -> [StdMethod]) -> EvedOptions m a
EvedOptions (([Text] -> [StdMethod]) -> EvedOptions m a)
-> ([Text] -> [StdMethod]) -> EvedOptions m a
forall a b. (a -> b) -> a -> b
$ \case
        Text
p:[Text]
rest | Text
p Text -> Text -> Bool
forall a. Eq a => a -> a -> Bool
== Text
t -> EvedOptions m a -> [Text] -> [StdMethod]
forall (m :: * -> *) a. EvedOptions m a -> [Text] -> [StdMethod]
getAvailableMethods EvedOptions m a
next [Text]
rest
        [Text]
_               -> [StdMethod]
forall a. Monoid a => a
mempty

    capture :: Text -> UrlElement a -> EvedOptions m b -> EvedOptions m (a -> b)
capture Text
_ UrlElement a
_ EvedOptions m b
next = ([Text] -> [StdMethod]) -> EvedOptions m (a -> b)
forall (m :: * -> *) a. ([Text] -> [StdMethod]) -> EvedOptions m a
EvedOptions (([Text] -> [StdMethod]) -> EvedOptions m (a -> b))
-> ([Text] -> [StdMethod]) -> EvedOptions m (a -> b)
forall a b. (a -> b) -> a -> b
$ \case
        Text
_:[Text]
rest -> EvedOptions m b -> [Text] -> [StdMethod]
forall (m :: * -> *) a. EvedOptions m a -> [Text] -> [StdMethod]
getAvailableMethods EvedOptions m b
next [Text]
rest
        [Text]
_      -> [StdMethod]
forall a. Monoid a => a
mempty

    reqBody :: NonEmpty (ContentType a)
-> EvedOptions m b -> EvedOptions m (a -> b)
reqBody NonEmpty (ContentType a)
_ = EvedOptions m b -> EvedOptions m (a -> b)
forall (m :: * -> *) a b. EvedOptions m a -> EvedOptions m b
passthrough
    queryParam :: Text -> QueryParam a -> EvedOptions m b -> EvedOptions m (a -> b)
queryParam Text
_ QueryParam a
_ = EvedOptions m b -> EvedOptions m (a -> b)
forall (m :: * -> *) a b. EvedOptions m a -> EvedOptions m b
passthrough
    header :: Text -> Header a -> EvedOptions m b -> EvedOptions m (a -> b)
header Text
_ Header a
_ = EvedOptions m b -> EvedOptions m (a -> b)
forall (m :: * -> *) a b. EvedOptions m a -> EvedOptions m b
passthrough
    verb :: StdMethod
-> Status -> NonEmpty (ContentType a) -> EvedOptions m (m a)
verb StdMethod
method Status
_ NonEmpty (ContentType a)
_ = ([Text] -> [StdMethod]) -> EvedOptions m (m a)
forall (m :: * -> *) a. ([Text] -> [StdMethod]) -> EvedOptions m a
EvedOptions (([Text] -> [StdMethod]) -> EvedOptions m (m a))
-> ([Text] -> [StdMethod]) -> EvedOptions m (m a)
forall a b. (a -> b) -> a -> b
$ \case
        [] -> [StdMethod
method]
        [Text]
_  -> [StdMethod]
forall a. Monoid a => a
mempty