module Yesod.Core.Internal.TH where
import Prelude hiding (exp)
import Yesod.Core.Handler
import Language.Haskell.TH
import Language.Haskell.TH.Syntax
import qualified Network.Wai as W
import Data.ByteString.Lazy.Char8 ()
import Data.List (foldl')
import Yesod.Routes.TH
import Yesod.Routes.Parse
import Yesod.Core.Types
import Yesod.Core.Content
import Yesod.Core.Class.Dispatch
import Yesod.Core.Internal.Run
mkYesod :: String
-> [ResourceTree String]
-> Q [Dec]
mkYesod name = fmap (uncurry (++)) . mkYesodGeneral name [] False
mkYesodData :: String -> [ResourceTree String] -> Q [Dec]
mkYesodData name res = mkYesodDataGeneral name False res
mkYesodSubData :: String -> [ResourceTree String] -> Q [Dec]
mkYesodSubData name res = mkYesodDataGeneral name True res
mkYesodDataGeneral :: String -> Bool -> [ResourceTree String] -> Q [Dec]
mkYesodDataGeneral name isSub res = do
let (name':rest) = words name
fmap fst $ mkYesodGeneral name' rest isSub res
mkYesodDispatch :: String -> [ResourceTree String] -> Q [Dec]
mkYesodDispatch name = fmap snd . mkYesodGeneral name [] False
masterTypeSyns :: Type -> [Dec]
masterTypeSyns site =
[ TySynD (mkName "Handler") []
$ ConT ''HandlerT `AppT` site `AppT` ConT ''IO
, TySynD (mkName "Widget") []
$ ConT ''WidgetT `AppT` site `AppT` ConT ''IO `AppT` ConT ''()
]
mkYesodGeneral :: String
-> [String]
-> Bool
-> [ResourceTree String]
-> Q([Dec],[Dec])
mkYesodGeneral name args isSub resS = do
renderRouteDec <- mkRenderRouteInstance site res
routeAttrsDec <- mkRouteAttrsInstance site res
dispatchDec <- mkDispatchInstance site res
parse <- mkParseRouteInstance site res
let rname = mkName $ "resources" ++ name
eres <- lift resS
let resourcesDec =
[ SigD rname $ ListT `AppT` (ConT ''ResourceTree `AppT` ConT ''String)
, FunD rname [Clause [] (NormalB eres) []]
]
let dataDec = concat
[ [parse]
, renderRouteDec
, [routeAttrsDec]
, resourcesDec
, if isSub then [] else masterTypeSyns site
]
return (dataDec, dispatchDec)
where site = foldl' AppT (ConT $ mkName name) (map (VarT . mkName) args)
res = map (fmap parseType) resS
mkMDS :: Q Exp -> MkDispatchSettings
mkMDS rh = MkDispatchSettings
{ mdsRunHandler = rh
, mdsSubDispatcher =
[|\parentRunner getSub toParent env -> yesodSubDispatch
YesodSubRunnerEnv
{ ysreParentRunner = parentRunner
, ysreGetSub = getSub
, ysreToParentRoute = toParent
, ysreParentEnv = env
}
|]
, mdsGetPathInfo = [|W.pathInfo|]
, mdsSetPathInfo = [|\p r -> r { W.pathInfo = p }|]
, mdsMethod = [|W.requestMethod|]
, mds404 = [|notFound >> return ()|]
, mds405 = [|badMethod >> return ()|]
, mdsGetHandler = defaultGetHandler
}
mkDispatchInstance :: Type
-> [ResourceTree a]
-> DecsQ
mkDispatchInstance master res = do
clause' <- mkDispatchClause (mkMDS [|yesodRunner|]) res
let thisDispatch = FunD 'yesodDispatch [clause']
return [InstanceD [] yDispatch [thisDispatch]]
where
yDispatch = ConT ''YesodDispatch `AppT` master
mkYesodSubDispatch :: [ResourceTree a] -> Q Exp
mkYesodSubDispatch res = do
clause' <- mkDispatchClause (mkMDS [|subHelper . fmap toTypedContent|]) res
inner <- newName "inner"
let innerFun = FunD inner [clause']
helper <- newName "helper"
let fun = FunD helper
[ Clause
[]
(NormalB $ VarE inner)
[innerFun]
]
return $ LetE [fun] (VarE helper)