{-# LANGUAGE FlexibleInstances #-} ------------------------------------------------------------------------------ module Snap.Snaplet.Rest.FromRequest.Internal ( FromRequest (..) , parseRead ) where ------------------------------------------------------------------------------ import qualified Data.ByteString as BS import qualified Data.ByteString.Lazy as LBS import qualified Data.ByteString.UTF8 as BS import qualified Data.Text as Text ------------------------------------------------------------------------------ import Control.Applicative import Control.Monad import Data.ByteString (ByteString) import Data.CaseInsensitive (CI, mk) import Snap.Core (Params) ------------------------------------------------------------------------------ import Snap.Snaplet.Rest.Proxy (Proxy) ------------------------------------------------------------------------------ -- | Instances of this class can be parsed from the remaining path information -- at the current route, and potentially also the URL parameters. class FromRequest id where -- | Parse a value from the remaining path information. A value of -- 'Nothing' indicates that the parse failed. fromPath :: ByteString -> Maybe id -- | Internal method that indicates if individual resources are disabled. pathEnabled :: Proxy id -> Bool pathEnabled _ = True -- | Internal method that allows standard types to provide a default -- implementation of 'fromParams'. defaultFromParams :: Maybe (Params -> Maybe id) defaultFromParams = Nothing -- This instance is useful for a singleton resource. Indicates that the -- resource can only be accessed at its root, and cannot be searched. instance FromRequest () where fromPath _ = Nothing pathEnabled _ = False -- This instance can be used to indicate that while query string searching is -- disabled, operations can still be performed on collections. instance FromRequest a => FromRequest (Maybe a) where fromPath = Just . fromPath defaultFromParams = Just $ const Nothing -- Useful for disparate path and query types. instance FromRequest a => FromRequest (Either a b) where fromPath = fmap Left . fromPath instance FromRequest Int where fromPath = parseRead instance FromRequest (CI String) where fromPath p = mk . BS.toString <$> checkSplit p instance FromRequest (CI Text.Text) where fromPath p = mk . Text.pack . BS.toString <$> checkSplit p instance FromRequest (CI ByteString) where fromPath p = mk <$> checkSplit p instance FromRequest (CI LBS.ByteString) where fromPath p = mk . LBS.fromStrict <$> checkSplit p instance FromRequest a => FromRequest [a] where fromPath = mapM (checkSplit >=> fromPath) . BS.split 47 instance (FromRequest a, FromRequest b) => FromRequest (a, b) where fromPath p = do let (a, b) = BS.breakByte 47 p a' <- fromPath a b' <- fromPath b return (a', b') instance (FromRequest a, FromRequest b, FromRequest c) => FromRequest (a, b, c) where fromPath p = do let (a, r) = BS.breakByte 47 p a' <- fromPath a (b, c) <- fromPath r return (a', b, c) instance (FromRequest a, FromRequest b, FromRequest c, FromRequest d) => FromRequest (a, b, c, d) where fromPath p = do let (a, r) = BS.breakByte 47 p a' <- fromPath a (b, c, d) <- fromPath r return (a', b, c, d) ------------------------------------------------------------------------------ -- | Ensures that the given 'ByteString' is neither empty nor containing a -- path split, evaluating to 'Nothing' in either case. checkSplit :: ByteString -> Maybe ByteString checkSplit bs = if length (BS.split 47 bs) /= 1 then Nothing else Just bs ------------------------------------------------------------------------------ -- | A convenient helper function that wraps a read failure into 'Nothing' -- instead of throwing an error. parseRead :: Read a => ByteString -> Maybe a parseRead = safeRead . reads . BS.toString where safeRead [(a, "")] = Just a safeRead _ = Nothing