packages feed

seacat-1.0.0.0: src/Web/Seacat/Router.hs

-- |This is a port of (what I use of) Web.Routes.Wai to Wai 3.0, as I
-- got tired of waiting for upstream to update.
module Web.Seacat.Router ( handleWai
                         , handleWai_
                         , handleWaiError ) where

import Data.Text (Text, append)
import Data.Text.Encoding (decodeUtf8)
import Network.HTTP.Types (status404)
import Network.Wai (Application, rawPathInfo , responseLBS)
import Web.Routes.PathInfo (PathInfo(..), fromPathInfo, toPathInfoParams, stripOverlapBS)

import qualified Data.ByteString.Char8 as C
import qualified Data.ByteString.Lazy.Char8 as L

-- | A URL pretty printer. This is like MkUrl from
-- Web.Seacat.RequestHandler.Types, except that it uses a (Text, Maybe
-- Text) to stick close to web-routes-wai, easing migration back when
-- it is finally updated.
type MkUrl r = r -> [(Text, Maybe Text)] -> Text

-- | A URL parser. This turns a URL into either an error message, or a
-- parsed URL type, suitable for giving to the routing function.
type FromUrl r = C.ByteString -> Either String r

-- | A routing function type.
-- 
-- Takes a URL printing function and a parsed URL and returns the
-- appropriate Application for that URL.
type RoutingFunction r = MkUrl r -> r -> Application

-- |Convert a routing function into an Application by using PathInfo.
handleWai :: PathInfo r
          => C.ByteString -- ^ The application web route
          -> RoutingFunction r
          -> Application
handleWai = handleWai_ toPathInfoParams fromPathInfo

-------------------------

-- |Convert a URL parser, a URL printer, and a routing function into
-- an Application. If the URL parsing fails, this returns a 404.
handleWai_ :: MkUrl r
           -> FromUrl r
           -> C.ByteString -- ^ The application web route
           -> RoutingFunction r
           -> Application
handleWai_ fromUrl toUrl approot handler =
    handleWaiError fromUrl toUrl approot handleError handler
    where
      handleError parseError  = \wreq receiver -> handleError' parseError wreq >>= receiver
      handleError' parseError = \_ -> return $ responseLBS status404 [] (L.pack parseError)

-------------------------

-- |Convert a URL parser, a URL printer, an error handling function,
-- and a routing function into an application.
handleWaiError :: MkUrl r
               -> FromUrl r
               -> C.ByteString -- ^ The application web route
               -> (String -> Application) -- ^ Error handling function
               -> RoutingFunction r
               -> Application
handleWaiError fromUrl toUrl approot handleError handler = \req receiver -> do
  let fUrl = toUrl $ stripOverlapBS approot $ rawPathInfo req
  case fUrl of
    Left err  -> handleError err req receiver
    Right url -> handler (\url params -> (decodeUtf8 approot) `append` (fromUrl url params)) url req receiver