{-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE TypeFamilies #-} {-# LANGUAGE MultiParamTypeClasses #-} module Snap.Predicate.Header ( Header (..) , Hdr (..) , HdrOpt (..) , HdrDef (..) , HasHdr (..) ) where import Data.ByteString (ByteString) import Data.CaseInsensitive (mk) import Data.Maybe import Data.Monoid import Data.Typeable import Data.Predicate import Snap.Core hiding (headers) import Snap.Predicate.Error import Snap.Predicate.Internal import Snap.Util.Readable -- | The most generic request header predicate provided. -- It will get all request header values of '_name' and pass them on to -- the conversion function '_read', which might either yield an error -- message or a value. If the header is not present, an optional default may be -- returned instead, if nothing is provided, the error message will be used -- when construction the 400 status. data Header a = Header { _hdrName :: !ByteString -- ^ request header name , _hdrRead :: [ByteString] -> Either ByteString a -- ^ conversion function , _hdrDefault :: !(Maybe a) -- ^ (optional) default value } instance Typeable a => Predicate (Header a) Request where type FVal (Header a) = Error type TVal (Header a) = a apply (Header nme f def) = rqApply RqPred { _rqName = nme , _rqRead = f , _rqDef = def , _rqCachePref = "header:" , _rqVals = headers nme , _rqError = Just $ err 400 ("Missing header '" <> nme <> "'.") } instance Show (Header a) where show p = "Header: " ++ show (_hdrName p) -- | Specialisation of 'Header' which returns the first request -- header value which could be converted to the target type. -- Relies on 'Readable' type-class for the actual conversion. data Hdr a = Hdr ByteString instance (Typeable a, Readable a) => Predicate (Hdr a) Request where type FVal (Hdr a) = Error type TVal (Hdr a) = a apply (Hdr x) = apply (Header x readValues Nothing) instance Show (Hdr a) where show (Hdr x) = "Hdr: " ++ show x -- | Specialisation of 'Header' which returns the first request -- header value which could be converted to the target type. -- If the header is not present, the provided default will be used. -- Relies on 'Readable' type-class for the actual conversion. data HdrDef a = HdrDef ByteString a instance (Typeable a, Readable a) => Predicate (HdrDef a) Request where type FVal (HdrDef a) = Error type TVal (HdrDef a) = a apply (HdrDef x d) = apply (Header x readValues (Just d)) instance Show a => Show (HdrDef a) where show (HdrDef x d) = "HdrDef: " ++ show x ++ " [" ++ show d ++ "]" -- | Predicate which returns the first request header which could be -- converted to the target type wrapped in a Maybe. -- If the header is not present, 'Nothing' will be returned. -- Relies on 'Readable' type-class for the actual conversion. data HdrOpt a = HdrOpt ByteString instance (Typeable a, Readable a) => Predicate (HdrOpt a) Request where type FVal (HdrOpt a) = Error type TVal (HdrOpt a) = Maybe a apply (HdrOpt x) = rqApplyMaybe RqPred { _rqName = x , _rqRead = readValues , _rqDef = Nothing , _rqCachePref = "headeropt:" , _rqVals = headers x , _rqError = Nothing } instance Show (HdrOpt a) where show (HdrOpt x) = "HdrOpt: " ++ show x -- | Predicate which is true if the request has a header with the -- given name. data HasHdr = HasHdr ByteString instance Predicate HasHdr Request where type FVal HasHdr = Error type TVal HasHdr = () apply (HasHdr x) r = return $ if isJust (getHeaders (mk x) r) then T 0 () else F (err 400 ("Missing header '" <> x <> "'.")) instance Show HasHdr where show (HasHdr x) = "HasHdr: " ++ show x