minion-0.1.0.0: src/Web/Minion/Response/Body.hs
module Web.Minion.Response.Body (
module Web.Minion.Response,
RespBody (..),
EncodeBody (..),
Encode (..),
handleBody,
) where
import Control.Exception qualified as Exc
import Control.Exception.Base (throw)
import Control.Monad.IO.Class (MonadIO)
import Control.Monad.IO.Class qualified as IO
import Data.ByteString qualified as Bytes
import Data.ByteString.Builder qualified as Bytes.Builder
import Data.ByteString.Lazy qualified as Bytes.Lazy
import Data.List.NonEmpty qualified as Nel
import Data.Maybe (isJust)
import Data.Text qualified as Text
import Data.Text.Encoding qualified as Text.Encode
import Data.Text.Lazy qualified as Text.Lazy
import Data.Text.Lazy.Encoding qualified as Text.Lazy.Encode
import Network.HTTP.Media qualified as Http
import Network.HTTP.Types qualified as Http
import Network.Wai qualified as Wai
import Web.Minion.Args
import Web.Minion.Error
import Web.Minion.Introspect qualified as I
import Web.Minion.Media
import Web.Minion.Media.PlainText (PlainText)
import Web.Minion.Response
import Web.Minion.Router.Internal
newtype RespBody cts a = RespBody a
instance (AllContentTypes cts) => CanRespond (RespBody cts a) where
canRespond [] = True
canRespond l = any (isJust . Http.matchAccept (allContentTypes @cts)) l
instance (MonadIO m) => ToResponse m (RespBody '[] a) where
toResponse _ (RespBody _) = IO.liftIO $ Exc.throwIO SomethingWentWrong
instance (EncodeBody (ct ': cts) a, Encode ct a, MonadIO m, ContentType ct) => ToResponse m (RespBody (ct ': cts) a) where
toResponse [] (RespBody a) = pure $ encode @ct a
toResponse (ct : _) (RespBody a) = pure $ encodeBody @(ct ': cts) ct a
class EncodeBody cts a where
encodeBody :: Bytes.ByteString -> a -> Wai.Response
instance EncodeBody '[] a where
encodeBody _ _ = throw SomethingWentWrong
instance (ContentType ct, Encode ct a, EncodeBody cts a) => EncodeBody (ct ': cts) a where
encodeBody ct a
| Just _ <- Http.matchAccept (Nel.toList $ media @ct) ct = encode @ct a
| otherwise = encodeBody @cts ct a
class Encode ct a where
encode :: a -> Wai.Response
plainTextHeader :: (Http.HeaderName, Bytes.ByteString)
plainTextHeader = (Http.hContentType, Http.renderHeader $ Nel.head $ media @PlainText)
instance Encode PlainText Text.Text where
encode a =
Wai.responseBuilder
Http.status200
[plainTextHeader]
( Bytes.Builder.lazyByteString
. Bytes.Lazy.fromStrict
. Text.Encode.encodeUtf8
$ a
)
instance Encode PlainText Text.Lazy.Text where
encode a =
Wai.responseBuilder
Http.status200
[plainTextHeader]
( Bytes.Builder.lazyByteString
. Text.Lazy.Encode.encodeUtf8
$ a
)
instance Encode PlainText String where
encode a =
Wai.responseBuilder
Http.status200
[plainTextHeader]
( Bytes.Builder.lazyByteString
. Text.Lazy.Encode.encodeUtf8
. Text.Lazy.pack
$ a
)
{- | Handles request with specified HTTP method and responds with specified Content-Type
@
... '/>' 'handleBody' GET \@'[PlainText] \@MyResponse someEndpoint
@
-}
{-# INLINE handleBody #-}
handleBody ::
forall cts o m ts i st.
(HandleArgs ts st m) =>
(IsResponse m (RespBody cts o)) =>
(I.Introspection i I.Response (RespBody cts o)) =>
-- | .
Http.Method ->
(DelayedArgs st ~> m o) ->
Router' i ts m
handleBody method = makeHandle @(RespBody cts) @o method RespBody