We now have the concept that either an entire route is overlap checked or not. This is essentially what we had before, except there was code littered everywhere on the mistaken assumption that just one component could be overlap checked. This also allows us to mark parent routes or multipiece components as non-overlapped checked. In addition, if you put a bang at the beginning of the pattern, the entire route is not overlap checked. The previous syntax is kept for backwards compatibility.
180 lines
6.9 KiB
Haskell
180 lines
6.9 KiB
Haskell
{-# LANGUAGE RecordWildCards, TemplateHaskell, ViewPatterns #-}
|
|
module Yesod.Routes.TH.Simple where
|
|
|
|
import Prelude hiding (exp)
|
|
import Yesod.Routes.TH
|
|
import Language.Haskell.TH.Syntax
|
|
import Web.PathPieces
|
|
import Data.Maybe (catMaybes)
|
|
import Control.Monad (forM)
|
|
import Data.List (foldl')
|
|
import Control.Arrow (second)
|
|
import System.Random (randomRIO)
|
|
|
|
data SDC = SDC
|
|
{ clause404 :: Clause
|
|
, extraParams :: [Exp]
|
|
, extraCons :: [Exp]
|
|
, envExp :: Exp
|
|
, reqExp :: Exp
|
|
}
|
|
|
|
-- | A simpler version of Yesod.Routes.TH.Dispatch.mkDispatchClause, based on
|
|
-- view patterns.
|
|
--
|
|
-- Since 1.2.1
|
|
mkSimpleDispatchClause :: MkDispatchSettings -> [ResourceTree a] -> Q Clause
|
|
mkSimpleDispatchClause MkDispatchSettings {..} resources = do
|
|
suffix <- qRunIO $ randomRIO (1000, 9999 :: Int)
|
|
envName <- newName $ "env" ++ show suffix
|
|
reqName <- newName $ "req" ++ show suffix
|
|
helperName <- newName $ "helper" ++ show suffix
|
|
|
|
let envE = VarE envName
|
|
reqE = VarE reqName
|
|
helperE = VarE helperName
|
|
|
|
clause404' <- mkClause404 envE reqE
|
|
getPathInfo <- mdsGetPathInfo
|
|
let pathInfo = getPathInfo `AppE` reqE
|
|
|
|
let sdc = SDC
|
|
{ clause404 = clause404'
|
|
, extraParams = []
|
|
, extraCons = []
|
|
, envExp = envE
|
|
, reqExp = reqE
|
|
}
|
|
clauses <- mapM (go sdc) resources
|
|
|
|
return $ Clause
|
|
[VarP envName, VarP reqName]
|
|
(NormalB $ helperE `AppE` pathInfo)
|
|
[FunD helperName $ clauses ++ [clause404']]
|
|
where
|
|
handlePiece :: Piece a -> Q (Pat, Maybe Exp)
|
|
handlePiece (Static str) = return (LitP $ StringL str, Nothing)
|
|
handlePiece (Dynamic _) = do
|
|
x <- newName "dyn"
|
|
let pat = ViewP (VarE 'fromPathPiece) (ConP 'Just [VarP x])
|
|
return (pat, Just $ VarE x)
|
|
|
|
handlePieces :: [Piece a] -> Q ([Pat], [Exp])
|
|
handlePieces = fmap (second catMaybes . unzip) . mapM handlePiece
|
|
|
|
mkCon :: String -> [Exp] -> Exp
|
|
mkCon name = foldl' AppE (ConE $ mkName name)
|
|
|
|
mkPathPat :: Pat -> [Pat] -> Pat
|
|
mkPathPat final =
|
|
foldr addPat final
|
|
where
|
|
addPat x y = ConP '(:) [x, y]
|
|
|
|
go :: SDC -> ResourceTree a -> Q Clause
|
|
go sdc (ResourceParent name _check pieces children) = do
|
|
(pats, dyns) <- handlePieces pieces
|
|
let sdc' = sdc
|
|
{ extraParams = extraParams sdc ++ dyns
|
|
, extraCons = extraCons sdc ++ [mkCon name dyns]
|
|
}
|
|
childClauses <- mapM (go sdc') children
|
|
|
|
restName <- newName "rest"
|
|
let restE = VarE restName
|
|
restP = VarP restName
|
|
|
|
helperName <- newName $ "helper" ++ name
|
|
let helperE = VarE helperName
|
|
|
|
return $ Clause
|
|
[mkPathPat restP pats]
|
|
(NormalB $ helperE `AppE` restE)
|
|
[FunD helperName $ childClauses ++ [clause404 sdc]]
|
|
go SDC {..} (ResourceLeaf (Resource name pieces dispatch _ _check)) = do
|
|
(pats, dyns) <- handlePieces pieces
|
|
|
|
(chooseMethod, finalPat) <- handleDispatch dispatch dyns
|
|
|
|
return $ Clause
|
|
[mkPathPat finalPat pats]
|
|
(NormalB chooseMethod)
|
|
[]
|
|
where
|
|
handleDispatch :: Dispatch a -> [Exp] -> Q (Exp, Pat)
|
|
handleDispatch dispatch dyns =
|
|
case dispatch of
|
|
Methods multi methods -> do
|
|
(finalPat, mfinalE) <-
|
|
case multi of
|
|
Nothing -> return (ConP '[] [], Nothing)
|
|
Just _ -> do
|
|
multiName <- newName "multi"
|
|
let pat = ViewP (VarE 'fromPathMultiPiece)
|
|
(ConP 'Just [VarP multiName])
|
|
return (pat, Just $ VarE multiName)
|
|
|
|
let dynsMulti =
|
|
case mfinalE of
|
|
Nothing -> dyns
|
|
Just e -> dyns ++ [e]
|
|
route' = foldl' AppE (ConE (mkName name)) dynsMulti
|
|
route = foldr AppE route' extraCons
|
|
jroute = ConE 'Just `AppE` route
|
|
allDyns = extraParams ++ dynsMulti
|
|
mkRunExp mmethod = do
|
|
runHandlerE <- mdsRunHandler
|
|
handlerE' <- mdsGetHandler mmethod name
|
|
let handlerE = foldl' AppE handlerE' allDyns
|
|
return $ runHandlerE
|
|
`AppE` handlerE
|
|
`AppE` envExp
|
|
`AppE` jroute
|
|
`AppE` reqExp
|
|
|
|
func <-
|
|
case methods of
|
|
[] -> mkRunExp Nothing
|
|
_ -> do
|
|
getMethod <- mdsMethod
|
|
let methodE = getMethod `AppE` reqExp
|
|
matches <- forM methods $ \method -> do
|
|
exp <- mkRunExp (Just method)
|
|
return $ Match (LitP $ StringL method) (NormalB exp) []
|
|
match405 <- do
|
|
runHandlerE <- mdsRunHandler
|
|
handlerE <- mds405
|
|
let exp = runHandlerE
|
|
`AppE` handlerE
|
|
`AppE` envExp
|
|
`AppE` jroute
|
|
`AppE` reqExp
|
|
return $ Match WildP (NormalB exp) []
|
|
return $ CaseE methodE $ matches ++ [match405]
|
|
|
|
return (func, finalPat)
|
|
Subsite _ getSub -> do
|
|
restPath <- newName "restPath"
|
|
setPathInfoE <- mdsSetPathInfo
|
|
subDispatcherE <- mdsSubDispatcher
|
|
runHandlerE <- mdsRunHandler
|
|
sub <- newName "sub"
|
|
let sub2 = LamE [VarP sub]
|
|
(foldl' (\a b -> a `AppE` b) (VarE (mkName getSub) `AppE` VarE sub) dyns)
|
|
let reqExp' = setPathInfoE `AppE` VarE restPath `AppE` reqExp
|
|
route' = foldl' AppE (ConE (mkName name)) dyns
|
|
route = foldr AppE route' extraCons
|
|
exp = subDispatcherE
|
|
`AppE` runHandlerE
|
|
`AppE` sub2
|
|
`AppE` route
|
|
`AppE` envExp
|
|
`AppE` reqExp'
|
|
return (exp, VarP restPath)
|
|
|
|
mkClause404 envE reqE = do
|
|
handler <- mds404
|
|
runHandler <- mdsRunHandler
|
|
let exp = runHandler `AppE` handler `AppE` envE `AppE` ConE 'Nothing `AppE` reqE
|
|
return $ Clause [WildP] (NormalB exp) []
|