eved-0.0.2.0: src/Web/Eved/ContentType.hs
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE TupleSections #-}
module Web.Eved.ContentType
where
import Control.Monad
import Data.Aeson
import Data.Bifunctor (first)
import qualified Data.ByteString as BS
import qualified Data.ByteString.Lazy as LBS
import Data.List.NonEmpty (NonEmpty)
import qualified Data.List.NonEmpty as NE
import Data.Text (Text)
import qualified Data.Text as T
import Network.HTTP.Media
import Network.HTTP.Types
data ContentType a = ContentType
{ toContentType :: a -> (RequestHeaders, LBS.ByteString)
, fromContentType :: (RequestHeaders, LBS.ByteString) -> Either Text a
, mediaTypes :: NonEmpty MediaType
}
json :: (FromJSON a, ToJSON a, Applicative f) => f (ContentType a)
json = pure $ ContentType
{ toContentType = (mempty,) . encode
, fromContentType = first T.pack . eitherDecode . snd
, mediaTypes = NE.fromList ["application" // "json"]
}
data WithHeaders a = WithHeaders RequestHeaders a
addHeaders :: RequestHeaders -> a -> WithHeaders a
addHeaders = WithHeaders
withHeaders :: Functor f => f (ContentType a) -> f (ContentType (WithHeaders a))
withHeaders = fmap $ \ctype ->
ContentType
{ toContentType = \(WithHeaders rHeaders val) -> (rHeaders, snd $ toContentType ctype val)
, fromContentType = \(rHeaders, rBody) -> fmap (WithHeaders rHeaders) (fromContentType ctype (mempty, rBody))
, mediaTypes = mediaTypes ctype
}
acceptHeader :: NonEmpty (ContentType a) -> Header
acceptHeader ctypes = (hAccept, renderHeader $ NE.toList $ mediaTypes =<< ctypes)
contentTypeHeader :: ContentType a -> Header
contentTypeHeader ctype = (hContentType, renderHeader $ NE.head $ mediaTypes ctype)
collectMediaTypes :: (ContentType a -> MediaType -> b) -> NonEmpty (ContentType a) -> [b]
collectMediaTypes f ctypes =
NE.toList $ ctypes >>= (\ctype ->
fmap (f ctype) (mediaTypes ctype)
)
chooseAcceptCType :: NonEmpty (ContentType a) -> BS.ByteString -> Maybe (MediaType, a -> (RequestHeaders, LBS.ByteString))
chooseAcceptCType ctypes =
mapAcceptMedia $ collectMediaTypes (\ctype x -> (x, (x, toContentType ctype))) ctypes
chooseContentCType :: NonEmpty (ContentType a) -> RequestHeaders -> BS.ByteString -> Maybe (LBS.ByteString -> Either Text a)
chooseContentCType ctypes rHeaders =
mapContentMedia $ collectMediaTypes (\ctype x -> (x, fromContentType ctype . (rHeaders,))) ctypes