{-# LANGUAGE CPP #-} -- | Helpers to bind 'digestive-functors' onto a 'wai' request module Network.Wai.Digestive (queryFormEnv, bodyFormEnv, bodyFormEnv_, requestFormEnv, requestFormEnv_) where import Control.Arrow (second) #if MIN_VERSION_wai(2,0,0) import Control.Monad.IO.Class (MonadIO, liftIO) import Control.Monad.Trans.Resource (ResourceT, getInternalState) #else import Control.Monad.Trans.Resource (ResourceT) #endif import Text.Digestive (Env, FormInput(..)) import Network.HTTP.Types.QueryLike (QueryLike, toQuery, toQueryValue) import Network.Wai (Request, queryString) import Network.Wai.Util (queryLookupAll) import Network.Wai.Parse (parseRequestBody, BackEnd, File, fileContent, tempFileBackEnd) import Data.Text (Text) import qualified Data.Text as T newtype FileQuery = FileQuery [File FilePath] instance QueryLike FileQuery where toQuery (FileQuery files) = map (second (toQueryValue . fileContent)) files -- Manual currying for performance -- | Build an 'Text.Digestive.Types.Env' from a query queryFormEnv :: (QueryLike q, Monad m) => q -> Env m queryFormEnv qs = \pth -> return $ map TextInput $ queryLookupAll (pathToText pth) qs' where qs' = toQuery qs -- | Build an 'Text.Digestive.Types.Env' from a request body #if MIN_VERSION_wai(2,0,0) bodyFormEnv :: (Monad m, MonadIO io) => BackEnd FilePath -> Request -> io (Env m) bodyFormEnv backend req = liftIO $ do #else bodyFormEnv :: (Monad m) => BackEnd FilePath -> Request -> ResourceT IO (Env m) bodyFormEnv backend req = do #endif (query, files) <- parseRequestBody backend req return $ queryFormEnv (toQuery query ++ toQuery (FileQuery files)) -- | Build an 'Text.Digestive.Types.Env' from a request body -- -- Uses a default temporary file 'Network.Wai.Parse.BackEnd' #if MIN_VERSION_wai(2,0,0) bodyFormEnv_ :: (Monad m, MonadIO io) => Request -> ResourceT io (Env m) bodyFormEnv_ req = do st <- getInternalState bodyFormEnv (tempFileBackEnd st) req #else bodyFormEnv_ :: (Monad m) => Request -> ResourceT IO (Env m) bodyFormEnv_ = bodyFormEnv tempFileBackEnd #endif -- | Build an 'Text.Digestive.Types.Env' from request body and query string #if MIN_VERSION_wai(2,0,0) requestFormEnv :: (Monad m, MonadIO io) => BackEnd FilePath -> Request -> io (Env m) requestFormEnv backend req = liftIO $ do #else requestFormEnv :: (Monad m) => BackEnd FilePath -> Request -> ResourceT IO (Env m) requestFormEnv backend req = do #endif (query, files) <- parseRequestBody backend req return $ queryFormEnv (toQuery query ++ toQuery (FileQuery files) ++ queryString req) -- | Build an 'Text.Digestive.Types.Env' from request body and query string -- -- Uses a default temporary file 'Network.Wai.Parse.BackEnd' #if MIN_VERSION_wai(2,0,0) requestFormEnv_ :: (Monad m, MonadIO io) => Request -> ResourceT io (Env m) requestFormEnv_ req = do st <- getInternalState requestFormEnv (tempFileBackEnd st) req #else requestFormEnv_ :: (Monad m) => Request -> ResourceT IO (Env m) requestFormEnv_ = requestFormEnv tempFileBackEnd #endif -- | Format form paths just like PHP/Rails pathToText :: [Text] -> Text pathToText [] = T.empty pathToText [p] = p pathToText (p:ps) | T.null p = pathToText ps -- Eat empties off the front | otherwise = T.concat (p : concatMap fragment ps) where fragment n = [ T.singleton '[', n, T.singleton ']' ]