-- Hoogle documentation, generated by Haddock
-- See Hoogle, http://www.haskell.org/hoogle/
-- | Lightweight library for building HTTP API
--
-- Please see the documentation at http://linnet.io
@package linnet
@version 0.2.0.0
module Linnet.ContentTypes
-- | Content-Type literal for text/html encoding
type TextHtml = "text/html"
-- | Content-Type literal for text/plain encoding
type TextPlain = "text/plain"
-- | Content-Type literal for application/json encoding
type ApplicationJson = "application/json"
module Linnet.Encode
-- | Encoding of some type a into payload of HTTP response Phantom
-- type ct guarantees that compiler checks support of encoding
-- of some a into content of given Content-Type by
-- looking for specific Encode instance.
class Encode (ct :: Symbol) a
encode :: Encode ct a => a -> ByteString
instance Linnet.Encode.Encode Linnet.ContentTypes.TextPlain Data.ByteString.Lazy.Internal.ByteString
instance Linnet.Encode.Encode Linnet.ContentTypes.TextPlain Data.ByteString.Internal.ByteString
instance Linnet.Encode.Encode Linnet.ContentTypes.TextPlain Data.Text.Internal.Text
instance Linnet.Encode.Encode Linnet.ContentTypes.TextPlain Data.Text.Internal.Lazy.Text
instance Linnet.Encode.Encode Linnet.ContentTypes.TextPlain GHC.Types.Int
instance Linnet.Encode.Encode Linnet.ContentTypes.TextPlain GHC.Integer.Type.Integer
instance Linnet.Encode.Encode Linnet.ContentTypes.TextPlain GHC.Types.Double
instance Linnet.Encode.Encode Linnet.ContentTypes.TextPlain GHC.Types.Float
module Linnet.Endpoints.Entity
data Entity
Param :: ByteString -> Entity
[entityName] :: Entity -> ByteString
Header :: ByteString -> Entity
[entityName] :: Entity -> ByteString
Body :: Entity
Cookie :: ByteString -> Entity
[entityName] :: Entity -> ByteString
instance GHC.Show.Show Linnet.Endpoints.Entity.Entity
instance GHC.Classes.Eq Linnet.Endpoints.Entity.Entity
module Linnet.Errors
data LinnetError
MissingEntity :: Entity -> LinnetError
[missingEntity] :: LinnetError -> Entity
EntityNotParsed :: Entity -> LinnetError -> LinnetError
[notParsedEntity] :: LinnetError -> Entity
[entityParsingError] :: LinnetError -> LinnetError
LinnetErrors :: NonEmpty LinnetError -> LinnetError
[linnetErrors] :: LinnetError -> NonEmpty LinnetError
DecodeError :: ByteString -> LinnetError
[decodeError] :: LinnetError -> ByteString
instance GHC.Show.Show Linnet.Errors.LinnetError
instance GHC.Classes.Eq Linnet.Errors.LinnetError
instance GHC.Base.Semigroup Linnet.Errors.LinnetError
instance GHC.Exception.Type.Exception Linnet.Errors.LinnetError
module Linnet.Decode
-- | Decoding of HTTP request payload into some type a. Phantom
-- type ct guarantees that compiler checks support of decoding
-- some a from content of given Content-Type by looking
-- for specific Decode instance.
class Decode (ct :: Symbol) a
decode :: Decode ct a => ByteString -> Either LinnetError a
class DecodePath a
decodePath :: DecodePath a => Text -> Maybe a
class DecodeEntity a
decodeEntity :: DecodeEntity a => Entity -> ByteString -> Either LinnetError a
decodeEntity :: (DecodeEntity a, FromByteString a) => Entity -> ByteString -> Either LinnetError a
instance Linnet.Decode.DecodeEntity Data.ByteString.Internal.ByteString
instance Linnet.Decode.DecodeEntity Data.Text.Internal.Text
instance Linnet.Decode.DecodeEntity GHC.Integer.Type.Integer
instance Linnet.Decode.DecodeEntity GHC.Types.Int
instance Linnet.Decode.DecodeEntity GHC.Types.Double
instance Linnet.Decode.DecodeEntity GHC.Types.Float
instance Linnet.Decode.DecodePath Data.Text.Internal.Text
instance Linnet.Decode.DecodePath Data.ByteString.Internal.ByteString
instance Linnet.Decode.DecodePath GHC.Integer.Type.Integer
instance Linnet.Decode.DecodePath GHC.Types.Int
instance Linnet.Decode.DecodePath GHC.Types.Double
instance Linnet.Decode.DecodePath GHC.Types.Float
module Linnet.Input
-- | Container for the reminder of the request path and request itself
data Input
Input :: [Text] -> Request -> Input
[reminder] :: Input -> [Text]
[request] :: Input -> Request
inputGet :: Text -> [QueryItem] -> Input
inputFromRequest :: Request -> Input
instance GHC.Show.Show Linnet.Input.Input
module Linnet.Internal.Coproduct
data Coproduct a b
[Inl] :: a -> Coproduct a b
[Inr] :: b -> Coproduct a b
data CNil
-- | Flatten nested coproduct
class AdjoinCoproduct cs c | cs -> c
adjoinCoproduct :: AdjoinCoproduct cs c => cs -> c
instance (GHC.Classes.Eq a, GHC.Classes.Eq b) => GHC.Classes.Eq (Linnet.Internal.Coproduct.Coproduct a b)
instance Linnet.Internal.Coproduct.AdjoinCoproduct' (Linnet.Internal.Coproduct.AdjoinCoproductT cs) cs c => Linnet.Internal.Coproduct.AdjoinCoproduct cs c
instance Linnet.Internal.Coproduct.AdjoinCoproduct' 'GHC.Types.False Linnet.Internal.Coproduct.CNil Linnet.Internal.Coproduct.CNil
instance (h Data.Type.Equality.~ h', Linnet.Internal.Coproduct.AdjoinCoproduct t out) => Linnet.Internal.Coproduct.AdjoinCoproduct' 'GHC.Types.False (Linnet.Internal.Coproduct.Coproduct h' t) (Linnet.Internal.Coproduct.Coproduct h out)
instance (Linnet.Internal.Coproduct.AdjoinCoproduct t out, Linnet.Internal.Coproduct.ExtendBy h out out') => Linnet.Internal.Coproduct.AdjoinCoproduct' 'GHC.Types.True (Linnet.Internal.Coproduct.Coproduct h t) out'
instance Linnet.Internal.Coproduct.Reverse' Linnet.Internal.Coproduct.CNil cs out => Linnet.Internal.Coproduct.Reverse cs out
instance Linnet.Internal.Coproduct.Reverse' acc Linnet.Internal.Coproduct.CNil acc
instance Linnet.Internal.Coproduct.Reverse' (Linnet.Internal.Coproduct.Coproduct a acc) b out => Linnet.Internal.Coproduct.Reverse' acc (Linnet.Internal.Coproduct.Coproduct a b) out
instance (Linnet.Internal.Coproduct.Reverse l revL, Linnet.Internal.Coproduct.ExtendLeftBy' revL r out) => Linnet.Internal.Coproduct.ExtendLeftBy l r out
instance Linnet.Internal.Coproduct.ExtendRight' (Linnet.Internal.Coproduct.ExtendRightT cs) cs t out => Linnet.Internal.Coproduct.ExtendRight cs t out
instance Linnet.Internal.Coproduct.ExtendRight' 'GHC.Types.True (Linnet.Internal.Coproduct.Coproduct h Linnet.Internal.Coproduct.CNil) a (Linnet.Internal.Coproduct.Coproduct h (Linnet.Internal.Coproduct.Coproduct a Linnet.Internal.Coproduct.CNil))
instance Linnet.Internal.Coproduct.ExtendRight t a out => Linnet.Internal.Coproduct.ExtendRight' 'GHC.Types.False (Linnet.Internal.Coproduct.Coproduct h t) a (Linnet.Internal.Coproduct.Coproduct h out)
instance (Linnet.Internal.Coproduct.ExtendRight l h out, Linnet.Internal.Coproduct.ExtendRightBy out t out') => Linnet.Internal.Coproduct.ExtendRightBy l (Linnet.Internal.Coproduct.Coproduct h t) out'
instance (Linnet.Internal.Coproduct.ExtendLeftBy l r out, Linnet.Internal.Coproduct.ExtendRightBy l r out) => Linnet.Internal.Coproduct.ExtendBy l r out
instance Linnet.Internal.Coproduct.ExtendRightBy l Linnet.Internal.Coproduct.CNil l
instance Linnet.Internal.Coproduct.ExtendLeftBy' Linnet.Internal.Coproduct.CNil a a
instance Linnet.Internal.Coproduct.ExtendLeftBy' t (Linnet.Internal.Coproduct.Coproduct h r) out => Linnet.Internal.Coproduct.ExtendLeftBy' (Linnet.Internal.Coproduct.Coproduct h t) r out
instance GHC.Classes.Eq Linnet.Internal.Coproduct.CNil
module Linnet.Internal.HList
data HList xs
[HNil] :: HList '[]
[:::] :: a -> HList as -> HList (a : as)
infixr 6 :::
class AdjoinHList ls l | ls -> l
adjoin :: AdjoinHList ls l => HList ls -> HList l
class FnToProduct fn ls out | fn ls -> out, ls out -> fn
fromFunction :: FnToProduct fn ls out => fn -> HList ls -> out
instance (v Data.Type.Equality.~ fn) => Linnet.Internal.HList.FnToProduct fn '[] v
instance Linnet.Internal.HList.FnToProduct fnOut tail out => Linnet.Internal.HList.FnToProduct (input -> fnOut) (input : tail) out
instance Linnet.Internal.HList.AdjoinHList' (Linnet.Internal.HList.NeedAdjoin ls) ls l => Linnet.Internal.HList.AdjoinHList ls l
instance Linnet.Internal.HList.AdjoinHList' 'GHC.Types.False '[] '[]
instance Linnet.Internal.HList.AdjoinHList t out => Linnet.Internal.HList.AdjoinHList' 'GHC.Types.False (h : t) (h : out)
instance (Linnet.Internal.HList.AdjoinHList t ao, Linnet.Internal.HList.Prepend a ao po) => Linnet.Internal.HList.AdjoinHList' 'GHC.Types.True (Linnet.Internal.HList.HList a : t) po
instance Linnet.Internal.HList.Prepend' (Linnet.Internal.HList.PrependT a b) a b ab => Linnet.Internal.HList.Prepend a b ab
instance Linnet.Internal.HList.Prepend' 'Linnet.Internal.HList.BothHNil '[] '[] '[]
instance Linnet.Internal.HList.Prepend' 'Linnet.Internal.HList.LeftHNil '[] b b
instance Linnet.Internal.HList.Prepend' 'Linnet.Internal.HList.RightHNil a '[] a
instance Linnet.Internal.HList.Prepend as (b : bs) cs => Linnet.Internal.HList.Prepend' 'Linnet.Internal.HList.NoneHNil (a : as) (b : bs) (a : cs)
instance GHC.Show.Show (Linnet.Internal.HList.HList '[])
instance (GHC.Show.Show a, GHC.Show.Show (Linnet.Internal.HList.HList as)) => GHC.Show.Show (Linnet.Internal.HList.HList (a : as))
instance GHC.Classes.Eq (Linnet.Internal.HList.HList '[])
instance (GHC.Classes.Eq a, GHC.Classes.Eq (Linnet.Internal.HList.HList as)) => GHC.Classes.Eq (Linnet.Internal.HList.HList (a : as))
module Linnet.NaturalTransformation
-- | Type class that defines transformation F a -> G a used by Linnet to
-- convert custom monads to WAI IO
class NaturalTransformation f g
mapK :: NaturalTransformation f g => f a -> g a
instance Linnet.NaturalTransformation.NaturalTransformation GHC.Types.IO GHC.Types.IO
module Linnet.ToResponse
-- | Type-class to convert a value of type a into Response with
-- Content-Type of ct
class ToResponse (ct :: Symbol) a
toResponse :: ToResponse ct a => a -> Response
instance Linnet.ToResponse.ToResponse' (Linnet.ToResponse.ValueT a) ct a => Linnet.ToResponse.ToResponse ct a
instance (Linnet.Encode.Encode ct a, GHC.TypeLits.KnownSymbol ct) => Linnet.ToResponse.ToResponse' 'Linnet.ToResponse.Value ct a
instance Linnet.ToResponse.ToResponse' 'Linnet.ToResponse.ResponseValue ct Network.Wai.Internal.Response
instance GHC.TypeLits.KnownSymbol ct => Linnet.ToResponse.ToResponse' 'Linnet.ToResponse.UnitValue ct ()
instance Linnet.ToResponse.ToResponse' 'Linnet.ToResponse.CNilValue ct Linnet.Internal.Coproduct.CNil
instance (Linnet.ToResponse.ToResponse ct a, Linnet.ToResponse.ToResponse ct b) => Linnet.ToResponse.ToResponse' 'Linnet.ToResponse.CoproductValue ct (Linnet.Internal.Coproduct.Coproduct a b)
module Linnet.Output
-- | Output of Endpoint that carries some Payload
-- a together with response status and headers
data Output a
Output :: Status -> Payload a -> [Header] -> Output a
[outputStatus] :: Output a -> Status
[outputPayload] :: Output a -> Payload a
[outputHeaders] :: Output a -> [Header]
-- | Payload of Output that could be:
data Payload a
-- | Payload with some value a
Payload :: a -> Payload a
-- | Represents empty response
NoPayload :: Payload a
-- | Failed payload with an exception inside
ErrorPayload :: e -> Payload a
-- | Create Output with Payload a and status OK
-- 200
ok :: a -> Output a
-- | Create Output with Payload a and status
-- CREATED 201
created :: a -> Output a
-- | Create Output with NoPayload and status ACCEPTED
-- 202
accepted :: Output a
-- | Create Output with NoPayload and status NO CONTENT
-- 202
noContent :: Output a
-- | Create Output with ErrorPayload e and status BAD
-- REQUEST 400
badRequest :: Exception e => e -> Output a
-- | Create Output with ErrorPayload e and status
-- UNAUTHORIZED 401
unauthorized :: Exception e => e -> Output a
-- | Create Output with ErrorPayload e and status
-- PAYMENT REQUIRED 402
paymentRequired :: Exception e => e -> Output a
-- | Create Output with ErrorPayload e and status
-- FORBIDDEN 403
forbidden :: Exception e => e -> Output a
-- | Create Output with ErrorPayload e and status NOT
-- FOUND 404
notFound :: Exception e => e -> Output a
-- | Create Output with ErrorPayload e and status
-- METHOD NOT ALLOWED 405
methodNotAllowed :: Exception e => e -> Output a
-- | Create Output with ErrorPayload e and status NOT
-- ACCEPTABLE 406
notAcceptable :: Exception e => e -> Output a
-- | Create Output with ErrorPayload e and status
-- CONFLICT 409
conflict :: Exception e => e -> Output a
-- | Create Output with ErrorPayload e and status GONE
-- 410
gone :: Exception e => e -> Output a
-- | Create Output with ErrorPayload e and status
-- LENGTH REQUIRED 411
lengthRequired :: Exception e => e -> Output a
-- | Create Output with ErrorPayload e and status
-- PRECONDITIONED FAILED 412
preconditionFailed :: Exception e => e -> Output a
-- | Create Output with ErrorPayload e and status
-- REQUEST ENTITY TOO LARGE 413
requestEntityTooLarge :: Exception e => e -> Output a
-- | Create Output with ErrorPayload e and status
-- UNPROCESSABLE ENTITY 422
unprocessableEntity :: Exception e => e -> Output a
-- | Create Output with ErrorPayload e and status TOO
-- MANY REQUESTS 422
tooManyRequests :: Exception e => e -> Output a
-- | Create Output with ErrorPayload e and status
-- INTERNAL SERVER ERROR 500
internalServerError :: Exception e => e -> Output a
-- | Create Output with ErrorPayload e and status NOT
-- IMPLEMENTED 501
notImplemented :: Exception e => e -> Output a
-- | Create Output with ErrorPayload e and status BAD
-- GATEWAY 502
badGateway :: Exception e => e -> Output a
-- | Create Output with ErrorPayload e and status
-- SERVICE UNAVAILABLE 503
serviceUnavailable :: Exception e => e -> Output a
-- | Create Output with ErrorPayload e and status
-- GATEWAY TIMEOUT 504
gatewayTimeout :: Exception e => e -> Output a
-- | Create successful Output with payload a and given
-- status
payloadOutput :: Status -> a -> Output a
-- | Create failed Output with exception e and given status
payloadError :: Exception e => Status -> e -> Output a
-- | Create empty Output with given status
payloadEmpty :: Status -> Output a
-- | Transform payload of output
transformM :: Applicative m => (a -> m (Output b)) -> Output a -> m (Output b)
-- | Add header to given Output
withHeader :: (ByteString, ByteString) -> Output a -> Output a
outputToResponse :: forall a ct. (KnownSymbol ct, ToResponse ct a, ToResponse ct SomeException) => Output a -> Response
instance GHC.Classes.Eq a => GHC.Classes.Eq (Linnet.Output.Output a)
instance GHC.Show.Show a => GHC.Show.Show (Linnet.Output.Payload a)
instance GHC.Show.Show a => GHC.Show.Show (Linnet.Output.Output a)
instance GHC.Base.Functor Linnet.Output.Output
instance GHC.Base.Applicative Linnet.Output.Output
instance GHC.Base.Monad Linnet.Output.Output
instance Control.Monad.Catch.MonadThrow Linnet.Output.Output
instance Data.Foldable.Foldable Linnet.Output.Output
instance Data.Traversable.Traversable Linnet.Output.Output
instance GHC.Classes.Eq a => GHC.Classes.Eq (Linnet.Output.Payload a)
module Linnet.Endpoint
-- | Result of returned by Endpoint that could be either:
--
--
-- - Matched containing reminder of the input together with
-- Output inside of monad m
-- - NotMatched in case endpoint doesn't match the input
--
data EndpointResult (m :: * -> *) a
Matched :: Input -> m (Output a) -> EndpointResult a
[matchedReminder] :: EndpointResult a -> Input
[matchedOutput] :: EndpointResult a -> m (Output a)
NotMatched :: EndpointResult a
-- | Basic Linnet data type that abstracts away operations over HTTP
-- communication. While WAI Application has type of Request ->
-- (Response -> IO ResponseReceived) -> IO ResponseReceived,
-- it's practical to treat web applications as functions of Request
-- -> BusinessLogic -> IO Response where
-- BusinessLogic is usually a function of a -> m b
-- where a and b are data to be decoded from the
-- request / encoded to response, m is some monad, and this is
-- the most interesting part of an application.
--
-- Endpoint's purpose is exactly to abstract details of encoding and
-- decoding, along with routing and the rest, and provide simple
-- interface to encapsulate BusinessLogic into a final web
-- application.
--
-- Business logic is encoded as transformation in fmap,
-- mapOutput, mapOutputM, mapM and the like.
-- Usual way to transform endpoint is to use ~> and
-- ~>> operators:
--
--
-- get (path @Text) ~> (\segment -> return $ ok segment)
--
--
-- Here, ~> is just an inverted alias for mapOutputM
-- function. Often, endpoint is a product of multiple endpoints, and here
-- ~>> proves to be very handy:
--
--
-- get (p' "sum" // path @Int // path @Int) ~>> (\i1 i2 -> return $ ok (i1 + i2) )
--
--
-- The trick is that // defines sequential AND
-- combination of endpoints that is represented as endpoint of
-- HList, so instead of dealing with heterogeneous list, it's
-- possible to use ~>> instead and map with a function of
-- multiple arguments.
--
-- Endpoints are also composable in terms of OR logic with
-- |+| operator that is useful for routing:
--
--
-- getUsers = get (p' "users") ~>> (ok <$> fetchUsers)
-- newUser = post (p' "users" // jsonBody @User) ~>> (\user -> ok <$> createUser user)
-- usersApi = getUsers |+| newUser
--
--
-- An endpoint might be converted into WAI Application using
-- bootstrap and @TypeApplications language pragma:
--
--
-- main = run 9000 app
-- where app = bootstrap @TextPlain usersApi & compile & toApp id
--
data Endpoint (m :: * -> *) a
Endpoint :: (Input -> EndpointResult m a) -> String -> Endpoint a
[runEndpoint] :: Endpoint a -> Input -> EndpointResult m a
[toString] :: Endpoint a -> String
isMatched :: EndpointResult m a -> Bool
maybeReminder :: EndpointResult m a -> Maybe Input
-- | Lift monadic value m a into Endpoint that always
-- matches
lift :: Functor m => m a -> Endpoint m a
-- | Lift monadic output m (Output a) into Endpoint that
-- always matches
liftOutputM :: m (Output a) -> Endpoint m a
-- | Map over the Output of endpoint with function returning new
-- value a lifted in monad m
mapM' :: Monad m => (a -> m b) -> Endpoint m a -> Endpoint m b
-- | Map over the value of Endpoint with function returning new
-- Output b
mapOutput :: Monad m => (a -> Output b) -> Endpoint m a -> Endpoint m b
-- | Map over the value of Endpoint with function returning new
-- m (Output b)
mapOutputM :: Monad m => (a -> m (Output b)) -> Endpoint m a -> Endpoint m b
-- | Handle exception in monad m of Endpoint result using provided
-- function that returns new Output
handle :: (MonadCatch m, Exception e) => (e -> m (Output a)) -> Endpoint m a -> Endpoint m a
-- | Handle all exceptions in monad m of Endpoint result
handleAll :: MonadCatch m => (SomeException -> m (Output a)) -> Endpoint m a -> Endpoint m a
-- | Lift an exception of type e into Either
try :: (Exception e, MonadCatch m) => Endpoint m a -> Endpoint m (Either e a)
transformOutput :: (m (Output a) -> m (Output b)) -> Endpoint m a -> Endpoint m b
transform :: Monad m => (m a -> m b) -> Endpoint m a -> Endpoint m b
-- | Inversed alias for mapOutputM
(~>) :: Monad m => Endpoint m a -> (a -> m (Output b)) -> Endpoint m b
infixl 0 ~>
-- | Advanced version of ~> operator that allows to map
-- Endpoint m (HList ls) over a function of arity N equal to N
-- elements of HList. General rule of thumb when to use this operator is
-- whenever there is an HList on the left side.
(~>>) :: (Monad m, FnToProduct fn ls (m (Output b))) => Endpoint m (HList ls) -> fn -> Endpoint m b
infixl 0 ~>>
-- | Create product of two Endpoints that sequentially match a
-- request. | If some of endpoints doesn't match a request, the final
-- result is also non-matching
productWith :: forall m a b c. MonadCatch m => Endpoint m a -> Endpoint m b -> (a -> b -> c) -> Endpoint m c
-- | Create product of two Endpoints that sequentially match a
-- request and values are adjoined into HList. If some of
-- endpoints doesn't match a request, the final result is also
-- non-matching
(//) :: (MonadCatch m, AdjoinHList (a : (b : '[])) out) => Endpoint m a -> Endpoint m b -> Endpoint m (HList out)
infixr 2 //
-- | Create new Endpoint of two endpoints, adjoining values into
-- Coproduct During request resolution the following logic is
-- applied:
--
--
-- - If none of endpoints match, resulting endpoint is also
-- non-matching
-- - If both endpoints match, the more specific one is selected (with
-- shorter reminder)
--
(|+|) :: forall m a b out. (MonadCatch m, AdjoinCoproduct (Coproduct a (Coproduct b CNil)) out) => Endpoint m a -> Endpoint m b -> Endpoint m out
infixl 2 |+|
-- | Endpoint that always matches and returns a request from Input
root :: Applicative m => Endpoint m Request
-- | Endpoint that always matches and doesn't change any reminder
zero :: Applicative m => Endpoint m (HList '[])
instance GHC.Show.Show (Linnet.Endpoint.Endpoint m a)
instance GHC.Base.Functor m => GHC.Base.Functor (Linnet.Endpoint.Endpoint m)
instance Control.Monad.Catch.MonadCatch m => GHC.Base.Applicative (Linnet.Endpoint.Endpoint m)
instance Control.Monad.Catch.MonadCatch m => GHC.Base.Alternative (Linnet.Endpoint.Endpoint m)
instance GHC.Show.Show (m (Linnet.Output.Output a)) => GHC.Show.Show (Linnet.Endpoint.EndpointResult m a)
instance GHC.Base.Functor m => GHC.Base.Functor (Linnet.Endpoint.EndpointResult m)
module Linnet.Endpoints.Paths
-- | Endpoint that tries to decode head of the current path reminder into
-- specific type. It consumes head of the reminder.
--
--
-- - If path is empty, Endpoint is not matched
-- - If decoding has failed, Endpoint is not matched
--
path :: forall a m. (DecodePath a, Applicative m, Typeable a) => Endpoint m a
-- | Endpoint that matches only if the head of current path reminder is
-- equal to some given constant value. It consumes head of the reminder.
--
--
-- - If value matches the provided constant, saves the tail of the path
-- as a reminder
-- - Otherwise, resulting endpoint is not matched
--
pathConst :: Applicative m => Text -> Endpoint m (HList '[])
-- | Short alias for pathConst
p' :: Applicative m => Text -> Endpoint m (HList '[])
-- | Endpoint that matches only against empty path reminder
pathEmpty :: Applicative m => Endpoint m (HList '[])
-- | Endpoint that consumes the rest of the path reminder and decode it
-- using provided DecodePath for some type a
paths :: forall a m. (DecodePath a, Applicative m, Typeable a) => Endpoint m [a]
-- | Endpoint that matches any path and discards reminder
pathAny :: Applicative m => Endpoint m (HList '[])
module Linnet.Endpoints.Params
-- | Endpoint that tries to decode parameter name from the request
-- query string. Always matches, but may throw an exception in case:
--
--
-- - Parameter is not presented in request query
-- - There was a parameter decoding error
--
param :: forall a m. (DecodeEntity a, MonadThrow m) => ByteString -> Endpoint m a
-- | Endpoint that tries to decode parameter name from the request
-- query string. Always matches, but may throw an exception in case:
--
--
-- - There was a parameter decoding error
--
paramMaybe :: forall a m. (DecodeEntity a, MonadThrow m) => ByteString -> Endpoint m (Maybe a)
-- | Endpoint that tries to decode all parameters name from the
-- request query string. Always matches, but may throw an exception in
-- case:
--
--
-- - There was a parameter decoding error of at least one parameter
-- value
--
params :: forall a m. (DecodeEntity a, MonadThrow m) => ByteString -> Endpoint m [a]
-- | Endpoint that tries to decode all parameters name from the
-- request query string. Always matches, but may throw an exception in
-- case:
--
--
-- - There was a parameter decoding error of at least one parameter
-- value
-- - All parameters are empty or missing in request query
--
paramsNel :: forall a m. (DecodeEntity a, MonadThrow m) => ByteString -> Endpoint m (NonEmpty a)
module Linnet.Endpoints.Methods
-- | Turn endpoint into one that matches only for GET requests
get :: Endpoint m a -> Endpoint m a
-- | Turn endpoint into one that matches only for POST requests
post :: Endpoint m a -> Endpoint m a
-- | Turn endpoint into one that matches only for PUT requests
put :: Endpoint m a -> Endpoint m a
-- | Turn endpoint into one that matches only for PATCH requests
patch :: Endpoint m a -> Endpoint m a
-- | Turn endpoint into one that matches only for DELETE requests
delete :: Endpoint m a -> Endpoint m a
-- | Turn endpoint into one that matches only for HEAD requests
head' :: Endpoint m a -> Endpoint m a
-- | Turn endpoint into one that matches only for TRACE requests
trace' :: Endpoint m a -> Endpoint m a
-- | Turn endpoint into one that matches only for CONNECT requests
connect :: Endpoint m a -> Endpoint m a
-- | Turn endpoint into one that matches only for OPTIONS requests
options :: Endpoint m a -> Endpoint m a
module Linnet.Endpoints.Headers
-- | Endpoint that tries to decode header name from a request.
-- Always matches, but may throw an exception in case:
--
--
-- - Headers is not presented in the request
-- - There was a header decoding error
--
header :: forall a m. (DecodeEntity a, MonadThrow m) => ByteString -> Endpoint m a
-- | Endpoint that tries to decode header name from a request.
-- Always matches, but may throw an exception in case:
--
--
-- - There was a header decoding error
--
headerMaybe :: forall a m. (DecodeEntity a, MonadThrow m) => ByteString -> Endpoint m (Maybe a)
module Linnet.Endpoints.Cookies
-- | Endpoint that tries to decode cookie name from a request.
-- Always matches, but may throw an exception in case:
--
--
-- - Cookie is not presented in the request
-- - There was a cookie decoding error
--
cookie :: forall a m. (DecodeEntity a, MonadThrow m) => ByteString -> Endpoint m a
-- | Endpoint that tries to decode cookie name from a request.
-- Always matches, but may throw an exception in case:
--
--
-- - There was a cookie decoding error
--
cookieMaybe :: forall a m. (DecodeEntity a, MonadThrow m) => ByteString -> Endpoint m (Maybe a)
module Linnet.Endpoints.Bodies
-- | Endpoint that tries to decode body of request into some type
-- a using corresponding Decode instance. Matches if body
-- isn't chunked. May throw an exception in case:
--
--
-- - Body is empty
-- - There was a body decoding error
--
body :: forall ct a m. (Decode ct a, MonadIO m, MonadThrow m) => Endpoint m a
-- | Endpoint that tries to decode body of request into some type
-- a using corresponding Decode instance. Matches if body
-- isn't chunked. May throw an exception in case:
--
--
-- - There was a body decoding error
--
bodyMaybe :: forall ct a m. (Decode ct a, MonadIO m, MonadThrow m) => Endpoint m (Maybe a)
-- | Alias for body @ApplicationJson
jsonBody :: (Decode ApplicationJson a, MonadIO m, MonadThrow m) => Endpoint m a
-- | Alias for bodyMaybe @ApplicationJson
jsonBodyMaybe :: (Decode ApplicationJson a, MonadIO m, MonadThrow m) => Endpoint m (Maybe a)
-- | Alias for body @TextPlain
textBody :: (Decode TextPlain a, MonadIO m, MonadThrow m) => Endpoint m a
-- | Alias for bodyMaybe @TextPlain
textBodyMaybe :: (Decode TextPlain a, MonadIO m, MonadThrow m) => Endpoint m (Maybe a)
module Linnet.Endpoints
-- | Endpoint that tries to decode cookie name from a request.
-- Always matches, but may throw an exception in case:
--
--
-- - Cookie is not presented in the request
-- - There was a cookie decoding error
--
cookie :: forall a m. (DecodeEntity a, MonadThrow m) => ByteString -> Endpoint m a
-- | Endpoint that tries to decode cookie name from a request.
-- Always matches, but may throw an exception in case:
--
--
-- - There was a cookie decoding error
--
cookieMaybe :: forall a m. (DecodeEntity a, MonadThrow m) => ByteString -> Endpoint m (Maybe a)
-- | Endpoint that tries to decode header name from a request.
-- Always matches, but may throw an exception in case:
--
--
-- - Headers is not presented in the request
-- - There was a header decoding error
--
header :: forall a m. (DecodeEntity a, MonadThrow m) => ByteString -> Endpoint m a
-- | Endpoint that tries to decode header name from a request.
-- Always matches, but may throw an exception in case:
--
--
-- - There was a header decoding error
--
headerMaybe :: forall a m. (DecodeEntity a, MonadThrow m) => ByteString -> Endpoint m (Maybe a)
-- | Turn endpoint into one that matches only for GET requests
get :: Endpoint m a -> Endpoint m a
-- | Turn endpoint into one that matches only for POST requests
post :: Endpoint m a -> Endpoint m a
-- | Turn endpoint into one that matches only for PUT requests
put :: Endpoint m a -> Endpoint m a
-- | Turn endpoint into one that matches only for PATCH requests
patch :: Endpoint m a -> Endpoint m a
-- | Turn endpoint into one that matches only for DELETE requests
delete :: Endpoint m a -> Endpoint m a
-- | Turn endpoint into one that matches only for HEAD requests
head' :: Endpoint m a -> Endpoint m a
-- | Turn endpoint into one that matches only for TRACE requests
trace' :: Endpoint m a -> Endpoint m a
-- | Turn endpoint into one that matches only for CONNECT requests
connect :: Endpoint m a -> Endpoint m a
-- | Turn endpoint into one that matches only for OPTIONS requests
options :: Endpoint m a -> Endpoint m a
-- | Endpoint that tries to decode parameter name from the request
-- query string. Always matches, but may throw an exception in case:
--
--
-- - Parameter is not presented in request query
-- - There was a parameter decoding error
--
param :: forall a m. (DecodeEntity a, MonadThrow m) => ByteString -> Endpoint m a
-- | Endpoint that tries to decode parameter name from the request
-- query string. Always matches, but may throw an exception in case:
--
--
-- - There was a parameter decoding error
--
paramMaybe :: forall a m. (DecodeEntity a, MonadThrow m) => ByteString -> Endpoint m (Maybe a)
-- | Endpoint that tries to decode all parameters name from the
-- request query string. Always matches, but may throw an exception in
-- case:
--
--
-- - There was a parameter decoding error of at least one parameter
-- value
--
params :: forall a m. (DecodeEntity a, MonadThrow m) => ByteString -> Endpoint m [a]
-- | Endpoint that tries to decode all parameters name from the
-- request query string. Always matches, but may throw an exception in
-- case:
--
--
-- - There was a parameter decoding error of at least one parameter
-- value
-- - All parameters are empty or missing in request query
--
paramsNel :: forall a m. (DecodeEntity a, MonadThrow m) => ByteString -> Endpoint m (NonEmpty a)
-- | Endpoint that tries to decode head of the current path reminder into
-- specific type. It consumes head of the reminder.
--
--
-- - If path is empty, Endpoint is not matched
-- - If decoding has failed, Endpoint is not matched
--
path :: forall a m. (DecodePath a, Applicative m, Typeable a) => Endpoint m a
-- | Endpoint that matches only if the head of current path reminder is
-- equal to some given constant value. It consumes head of the reminder.
--
--
-- - If value matches the provided constant, saves the tail of the path
-- as a reminder
-- - Otherwise, resulting endpoint is not matched
--
pathConst :: Applicative m => Text -> Endpoint m (HList '[])
-- | Short alias for pathConst
p' :: Applicative m => Text -> Endpoint m (HList '[])
-- | Endpoint that matches only against empty path reminder
pathEmpty :: Applicative m => Endpoint m (HList '[])
-- | Endpoint that consumes the rest of the path reminder and decode it
-- using provided DecodePath for some type a
paths :: forall a m. (DecodePath a, Applicative m, Typeable a) => Endpoint m [a]
-- | Endpoint that matches any path and discards reminder
pathAny :: Applicative m => Endpoint m (HList '[])
-- | Endpoint that tries to decode body of request into some type
-- a using corresponding Decode instance. Matches if body
-- isn't chunked. May throw an exception in case:
--
--
-- - Body is empty
-- - There was a body decoding error
--
body :: forall ct a m. (Decode ct a, MonadIO m, MonadThrow m) => Endpoint m a
-- | Endpoint that tries to decode body of request into some type
-- a using corresponding Decode instance. Matches if body
-- isn't chunked. May throw an exception in case:
--
--
-- - There was a body decoding error
--
bodyMaybe :: forall ct a m. (Decode ct a, MonadIO m, MonadThrow m) => Endpoint m (Maybe a)
-- | Alias for body @TextPlain
textBody :: (Decode TextPlain a, MonadIO m, MonadThrow m) => Endpoint m a
-- | Alias for bodyMaybe @TextPlain
textBodyMaybe :: (Decode TextPlain a, MonadIO m, MonadThrow m) => Endpoint m (Maybe a)
-- | Alias for body @ApplicationJson
jsonBody :: (Decode ApplicationJson a, MonadIO m, MonadThrow m) => Endpoint m a
-- | Alias for bodyMaybe @ApplicationJson
jsonBodyMaybe :: (Decode ApplicationJson a, MonadIO m, MonadThrow m) => Endpoint m (Maybe a)
module Linnet.Compile
class Compile cts m es
compile :: Compile cts m es => es -> ReaderT Request m Response
instance GHC.Base.Monad m => Linnet.Compile.Compile Linnet.Internal.Coproduct.CNil m (Linnet.Internal.HList.HList '[])
instance (GHC.TypeLits.KnownSymbol ct, Linnet.ToResponse.ToResponse ct a, Linnet.ToResponse.ToResponse ct GHC.Exception.Type.SomeException, Linnet.Compile.Compile cts m (Linnet.Internal.HList.HList es), Control.Monad.Catch.MonadCatch m) => Linnet.Compile.Compile (Linnet.Internal.Coproduct.Coproduct (Data.Proxy.Proxy ct) cts) m (Linnet.Internal.HList.HList (Linnet.Endpoint.Endpoint m a : es))
module Linnet.Bootstrap
-- | Create Bootstrap out of single Endpoint and some given
-- Content-Type:
--
--
-- bootstrap @TextPlain (pure "foo")
--
bootstrap :: forall (ct :: Symbol) m a. Endpoint m a -> Bootstrap m (Coproduct (Proxy ct) CNil) (HList '[Endpoint m a])
-- | Add another endpoint to Bootstrap for purpose of serving
-- multiple Content-Types with *different* endpoints
--
--
-- bootstrap @TextPlain (pure "foo") & server @ApplicationJson (pure "bar")
--
serve :: forall (ct :: Symbol) cts es m a. Endpoint m a -> Bootstrap m cts (HList es) -> Bootstrap m (Coproduct (Proxy ct) cts) (HList (Endpoint m a : es))
-- | Compile Bootstrap into ReaderT Request m Response for
-- further combinations. Might be useful to implement middleware in
-- context of the same monad m:
--
--
-- bootstrap @TextPlain (pure "foo") & compile
--
compile :: forall cts m es. Compile cts m es => Bootstrap m cts es -> ReaderT Request m Response
-- | Convert ReaderT Request m Response into WAI
-- Application
--
--
-- bootstrap @TextPlain (pure "foo") & compile & toApp id
--
--
-- The first parameter here is a natural transformation of
-- Endpoints monad m into IO. In case if
-- selected monad is IO already then id is just enough.
-- Otherwise, it's a good place to define how to "start" custom monad for
-- each request to come and convert it to IO.
--
-- As an example:
--
--
-- - ReaderT RequestContext IO could be used to pass some data
-- as local context for the request.
-- - Some monad for logging (i.e. co-log)
--
toApp :: forall m. NaturalTransformation m IO => ReaderT Request m Response -> Application
-- | Linnet [ˈlɪnɪt] is a lightweight Haskell library for building HTTP API
-- on top of WAI. Library design is heavily inspired by Scala
-- Finch.
--
-- See the detailed documentation on linnet.io.
module Linnet
-- | Basic Linnet data type that abstracts away operations over HTTP
-- communication. While WAI Application has type of Request ->
-- (Response -> IO ResponseReceived) -> IO ResponseReceived,
-- it's practical to treat web applications as functions of Request
-- -> BusinessLogic -> IO Response where
-- BusinessLogic is usually a function of a -> m b
-- where a and b are data to be decoded from the
-- request / encoded to response, m is some monad, and this is
-- the most interesting part of an application.
--
-- Endpoint's purpose is exactly to abstract details of encoding and
-- decoding, along with routing and the rest, and provide simple
-- interface to encapsulate BusinessLogic into a final web
-- application.
--
-- Business logic is encoded as transformation in fmap,
-- mapOutput, mapOutputM, mapM and the like.
-- Usual way to transform endpoint is to use ~> and
-- ~>> operators:
--
--
-- get (path @Text) ~> (\segment -> return $ ok segment)
--
--
-- Here, ~> is just an inverted alias for mapOutputM
-- function. Often, endpoint is a product of multiple endpoints, and here
-- ~>> proves to be very handy:
--
--
-- get (p' "sum" // path @Int // path @Int) ~>> (\i1 i2 -> return $ ok (i1 + i2) )
--
--
-- The trick is that // defines sequential AND
-- combination of endpoints that is represented as endpoint of
-- HList, so instead of dealing with heterogeneous list, it's
-- possible to use ~>> instead and map with a function of
-- multiple arguments.
--
-- Endpoints are also composable in terms of OR logic with
-- |+| operator that is useful for routing:
--
--
-- getUsers = get (p' "users") ~>> (ok <$> fetchUsers)
-- newUser = post (p' "users" // jsonBody @User) ~>> (\user -> ok <$> createUser user)
-- usersApi = getUsers |+| newUser
--
--
-- An endpoint might be converted into WAI Application using
-- bootstrap and @TypeApplications language pragma:
--
--
-- main = run 9000 app
-- where app = bootstrap @TextPlain usersApi & compile & toApp id
--
data Endpoint (m :: * -> *) a
Endpoint :: (Input -> EndpointResult m a) -> String -> Endpoint a
[runEndpoint] :: Endpoint a -> Input -> EndpointResult m a
[toString] :: Endpoint a -> String
-- | Inversed alias for mapOutputM
(~>) :: Monad m => Endpoint m a -> (a -> m (Output b)) -> Endpoint m b
infixl 0 ~>
-- | Advanced version of ~> operator that allows to map
-- Endpoint m (HList ls) over a function of arity N equal to N
-- elements of HList. General rule of thumb when to use this operator is
-- whenever there is an HList on the left side.
(~>>) :: (Monad m, FnToProduct fn ls (m (Output b))) => Endpoint m (HList ls) -> fn -> Endpoint m b
infixl 0 ~>>
-- | Create product of two Endpoints that sequentially match a
-- request and values are adjoined into HList. If some of
-- endpoints doesn't match a request, the final result is also
-- non-matching
(//) :: (MonadCatch m, AdjoinHList (a : (b : '[])) out) => Endpoint m a -> Endpoint m b -> Endpoint m (HList out)
infixr 2 //
-- | Create new Endpoint of two endpoints, adjoining values into
-- Coproduct During request resolution the following logic is
-- applied:
--
--
-- - If none of endpoints match, resulting endpoint is also
-- non-matching
-- - If both endpoints match, the more specific one is selected (with
-- shorter reminder)
--
(|+|) :: forall m a b out. (MonadCatch m, AdjoinCoproduct (Coproduct a (Coproduct b CNil)) out) => Endpoint m a -> Endpoint m b -> Endpoint m out
infixl 2 |+|
-- | Turn endpoint into one that matches only for GET requests
get :: Endpoint m a -> Endpoint m a
-- | Turn endpoint into one that matches only for POST requests
post :: Endpoint m a -> Endpoint m a
-- | Turn endpoint into one that matches only for PUT requests
put :: Endpoint m a -> Endpoint m a
-- | Turn endpoint into one that matches only for PATCH requests
patch :: Endpoint m a -> Endpoint m a
-- | Turn endpoint into one that matches only for DELETE requests
delete :: Endpoint m a -> Endpoint m a
-- | Turn endpoint into one that matches only for HEAD requests
head' :: Endpoint m a -> Endpoint m a
-- | Turn endpoint into one that matches only for TRACE requests
trace' :: Endpoint m a -> Endpoint m a
-- | Turn endpoint into one that matches only for CONNECT requests
connect :: Endpoint m a -> Endpoint m a
-- | Turn endpoint into one that matches only for OPTIONS requests
options :: Endpoint m a -> Endpoint m a
-- | Endpoint that tries to decode head of the current path reminder into
-- specific type. It consumes head of the reminder.
--
--
-- - If path is empty, Endpoint is not matched
-- - If decoding has failed, Endpoint is not matched
--
path :: forall a m. (DecodePath a, Applicative m, Typeable a) => Endpoint m a
-- | Endpoint that matches any path and discards reminder
pathAny :: Applicative m => Endpoint m (HList '[])
-- | Endpoint that matches only if the head of current path reminder is
-- equal to some given constant value. It consumes head of the reminder.
--
--
-- - If value matches the provided constant, saves the tail of the path
-- as a reminder
-- - Otherwise, resulting endpoint is not matched
--
pathConst :: Applicative m => Text -> Endpoint m (HList '[])
-- | Short alias for pathConst
p' :: Applicative m => Text -> Endpoint m (HList '[])
-- | Endpoint that matches only against empty path reminder
pathEmpty :: Applicative m => Endpoint m (HList '[])
-- | Endpoint that consumes the rest of the path reminder and decode it
-- using provided DecodePath for some type a
paths :: forall a m. (DecodePath a, Applicative m, Typeable a) => Endpoint m [a]
-- | Endpoint that tries to decode parameter name from the request
-- query string. Always matches, but may throw an exception in case:
--
--
-- - Parameter is not presented in request query
-- - There was a parameter decoding error
--
param :: forall a m. (DecodeEntity a, MonadThrow m) => ByteString -> Endpoint m a
-- | Endpoint that tries to decode parameter name from the request
-- query string. Always matches, but may throw an exception in case:
--
--
-- - There was a parameter decoding error
--
paramMaybe :: forall a m. (DecodeEntity a, MonadThrow m) => ByteString -> Endpoint m (Maybe a)
-- | Endpoint that tries to decode all parameters name from the
-- request query string. Always matches, but may throw an exception in
-- case:
--
--
-- - There was a parameter decoding error of at least one parameter
-- value
--
params :: forall a m. (DecodeEntity a, MonadThrow m) => ByteString -> Endpoint m [a]
-- | Endpoint that tries to decode all parameters name from the
-- request query string. Always matches, but may throw an exception in
-- case:
--
--
-- - There was a parameter decoding error of at least one parameter
-- value
-- - All parameters are empty or missing in request query
--
paramsNel :: forall a m. (DecodeEntity a, MonadThrow m) => ByteString -> Endpoint m (NonEmpty a)
-- | Endpoint that tries to decode body of request into some type
-- a using corresponding Decode instance. Matches if body
-- isn't chunked. May throw an exception in case:
--
--
-- - Body is empty
-- - There was a body decoding error
--
body :: forall ct a m. (Decode ct a, MonadIO m, MonadThrow m) => Endpoint m a
-- | Endpoint that tries to decode body of request into some type
-- a using corresponding Decode instance. Matches if body
-- isn't chunked. May throw an exception in case:
--
--
-- - There was a body decoding error
--
bodyMaybe :: forall ct a m. (Decode ct a, MonadIO m, MonadThrow m) => Endpoint m (Maybe a)
-- | Alias for body @TextPlain
textBody :: (Decode TextPlain a, MonadIO m, MonadThrow m) => Endpoint m a
-- | Alias for bodyMaybe @TextPlain
textBodyMaybe :: (Decode TextPlain a, MonadIO m, MonadThrow m) => Endpoint m (Maybe a)
-- | Alias for body @ApplicationJson
jsonBody :: (Decode ApplicationJson a, MonadIO m, MonadThrow m) => Endpoint m a
-- | Alias for bodyMaybe @ApplicationJson
jsonBodyMaybe :: (Decode ApplicationJson a, MonadIO m, MonadThrow m) => Endpoint m (Maybe a)
-- | Endpoint that tries to decode cookie name from a request.
-- Always matches, but may throw an exception in case:
--
--
-- - Cookie is not presented in the request
-- - There was a cookie decoding error
--
cookie :: forall a m. (DecodeEntity a, MonadThrow m) => ByteString -> Endpoint m a
-- | Endpoint that tries to decode cookie name from a request.
-- Always matches, but may throw an exception in case:
--
--
-- - There was a cookie decoding error
--
cookieMaybe :: forall a m. (DecodeEntity a, MonadThrow m) => ByteString -> Endpoint m (Maybe a)
-- | Endpoint that tries to decode header name from a request.
-- Always matches, but may throw an exception in case:
--
--
-- - Headers is not presented in the request
-- - There was a header decoding error
--
header :: forall a m. (DecodeEntity a, MonadThrow m) => ByteString -> Endpoint m a
-- | Endpoint that tries to decode header name from a request.
-- Always matches, but may throw an exception in case:
--
--
-- - There was a header decoding error
--
headerMaybe :: forall a m. (DecodeEntity a, MonadThrow m) => ByteString -> Endpoint m (Maybe a)
-- | Encoding of some type a into payload of HTTP response Phantom
-- type ct guarantees that compiler checks support of encoding
-- of some a into content of given Content-Type by
-- looking for specific Encode instance.
class Encode (ct :: Symbol) a
encode :: Encode ct a => a -> ByteString
-- | Decoding of HTTP request payload into some type a. Phantom
-- type ct guarantees that compiler checks support of decoding
-- some a from content of given Content-Type by looking
-- for specific Decode instance.
class Decode (ct :: Symbol) a
decode :: Decode ct a => ByteString -> Either LinnetError a
-- | Output of Endpoint that carries some Payload
-- a together with response status and headers
data Output a
Output :: Status -> Payload a -> [Header] -> Output a
[outputStatus] :: Output a -> Status
[outputPayload] :: Output a -> Payload a
[outputHeaders] :: Output a -> [Header]
-- | Create Output with Payload a and status OK
-- 200
ok :: a -> Output a
-- | Create Output with Payload a and status
-- CREATED 201
created :: a -> Output a
-- | Create Output with NoPayload and status ACCEPTED
-- 202
accepted :: Output a
-- | Create Output with NoPayload and status NO CONTENT
-- 202
noContent :: Output a
-- | Create Output with ErrorPayload e and status BAD
-- REQUEST 400
badRequest :: Exception e => e -> Output a
-- | Create Output with ErrorPayload e and status
-- UNAUTHORIZED 401
unauthorized :: Exception e => e -> Output a
-- | Create Output with ErrorPayload e and status
-- PAYMENT REQUIRED 402
paymentRequired :: Exception e => e -> Output a
-- | Create Output with ErrorPayload e and status
-- FORBIDDEN 403
forbidden :: Exception e => e -> Output a
-- | Create Output with ErrorPayload e and status NOT
-- FOUND 404
notFound :: Exception e => e -> Output a
-- | Create Output with ErrorPayload e and status
-- METHOD NOT ALLOWED 405
methodNotAllowed :: Exception e => e -> Output a
-- | Create Output with ErrorPayload e and status NOT
-- ACCEPTABLE 406
notAcceptable :: Exception e => e -> Output a
-- | Create Output with ErrorPayload e and status
-- CONFLICT 409
conflict :: Exception e => e -> Output a
-- | Create Output with ErrorPayload e and status GONE
-- 410
gone :: Exception e => e -> Output a
-- | Create Output with ErrorPayload e and status
-- LENGTH REQUIRED 411
lengthRequired :: Exception e => e -> Output a
-- | Create Output with ErrorPayload e and status
-- PRECONDITIONED FAILED 412
preconditionFailed :: Exception e => e -> Output a
-- | Create Output with ErrorPayload e and status
-- REQUEST ENTITY TOO LARGE 413
requestEntityTooLarge :: Exception e => e -> Output a
-- | Create Output with ErrorPayload e and status
-- UNPROCESSABLE ENTITY 422
unprocessableEntity :: Exception e => e -> Output a
-- | Create Output with ErrorPayload e and status TOO
-- MANY REQUESTS 422
tooManyRequests :: Exception e => e -> Output a
-- | Create Output with ErrorPayload e and status
-- INTERNAL SERVER ERROR 500
internalServerError :: Exception e => e -> Output a
-- | Create Output with ErrorPayload e and status NOT
-- IMPLEMENTED 501
notImplemented :: Exception e => e -> Output a
-- | Create Output with ErrorPayload e and status BAD
-- GATEWAY 502
badGateway :: Exception e => e -> Output a
-- | Create Output with ErrorPayload e and status
-- SERVICE UNAVAILABLE 503
serviceUnavailable :: Exception e => e -> Output a
-- | Create Output with ErrorPayload e and status
-- GATEWAY TIMEOUT 504
gatewayTimeout :: Exception e => e -> Output a
-- | Create Bootstrap out of single Endpoint and some given
-- Content-Type:
--
--
-- bootstrap @TextPlain (pure "foo")
--
bootstrap :: forall (ct :: Symbol) m a. Endpoint m a -> Bootstrap m (Coproduct (Proxy ct) CNil) (HList '[Endpoint m a])
-- | Add another endpoint to Bootstrap for purpose of serving
-- multiple Content-Types with *different* endpoints
--
--
-- bootstrap @TextPlain (pure "foo") & server @ApplicationJson (pure "bar")
--
serve :: forall (ct :: Symbol) cts es m a. Endpoint m a -> Bootstrap m cts (HList es) -> Bootstrap m (Coproduct (Proxy ct) cts) (HList (Endpoint m a : es))
-- | Compile Bootstrap into ReaderT Request m Response for
-- further combinations. Might be useful to implement middleware in
-- context of the same monad m:
--
--
-- bootstrap @TextPlain (pure "foo") & compile
--
compile :: forall cts m es. Compile cts m es => Bootstrap m cts es -> ReaderT Request m Response
-- | Convert ReaderT Request m Response into WAI
-- Application
--
--
-- bootstrap @TextPlain (pure "foo") & compile & toApp id
--
--
-- The first parameter here is a natural transformation of
-- Endpoints monad m into IO. In case if
-- selected monad is IO already then id is just enough.
-- Otherwise, it's a good place to define how to "start" custom monad for
-- each request to come and convert it to IO.
--
-- As an example:
--
--
-- - ReaderT RequestContext IO could be used to pass some data
-- as local context for the request.
-- - Some monad for logging (i.e. co-log)
--
toApp :: forall m. NaturalTransformation m IO => ReaderT Request m Response -> Application
-- | Run an Application on the given port. This calls
-- runSettings with defaultSettings.
run :: Port -> Application -> IO ()
-- | Content-Type literal for application/json encoding
type ApplicationJson = "application/json"
-- | Content-Type literal for text/html encoding
type TextHtml = "text/html"
-- | Content-Type literal for text/plain encoding
type TextPlain = "text/plain"