{-# LANGUAGE TemplateHaskell #-}
module Snap.Snaplet.Heist.Internal where
import Prelude
import Control.Lens
import Control.Monad.State
import qualified Data.ByteString as B
import Data.Char
import qualified Data.HashMap.Strict as Map
import Data.IORef
import Data.List
import Data.Monoid
import Data.Text (Text)
import qualified Data.Text as T
import Heist
import Heist.Splices.Cache
import System.FilePath.Posix
import Snap.Core
import Snap.Snaplet
data DefaultMode = Compiled | Interpreted
data Heist b = Configuring
{ _heistConfig :: IORef (HeistConfig (Handler b b), DefaultMode)
}
| Running
{ _masterConfig :: HeistConfig (Handler b b)
, _heistState :: HeistState (Handler b b)
, _heistCTS :: CacheTagState
, _defMode :: DefaultMode
}
makeLenses ''Heist
gHeistInit :: Handler b (Heist b) ()
-> FilePath
-> SnapletInit b (Heist b)
gHeistInit serve templateDir = do
makeSnaplet "heist" "" Nothing $ do
hs <- heistInitWorker templateDir defaultConfig
addRoutes [ ("", serve)
, ("heistReload", failIfNotLocal heistReloader)
]
return hs
where
sc = set scLoadTimeSplices defaultLoadTimeSplices mempty
defaultConfig = emptyHeistConfig & hcSpliceConfig .~ sc
& hcNamespace .~ ""
& hcErrorNotBound .~ True
heistInitWorker :: FilePath
-> HeistConfig (Handler b b)
-> Initializer b (Heist b) (Heist b)
heistInitWorker templateDir initialConfig = do
snapletPath <- getSnapletFilePath
let tDir = snapletPath </> templateDir
templates <- liftIO $ (loadTemplates tDir) >>=
either (error . concat) return
printInfo $ T.pack $ unwords
[ "...loaded"
, (show $ Map.size templates)
, "templates from"
, tDir
]
let config = initialConfig & hcTemplateLocations %~
(<> [loadTemplates tDir])
& hcCompiledTemplateFilter %~
(\f x -> f x && nsFilter x)
ref <- liftIO $ newIORef (config, Compiled)
addPostInitHook finalLoadHook
return $ Configuring ref
where
nsFilter = (/=) (fromIntegral $ ord '_') . B.head . head
finalLoadHook :: Heist b -> IO (Either Text (Heist b))
finalLoadHook (Configuring ref) = do
(hc,dm) <- readIORef ref
res <- liftM toTextErrors $ initHeistWithCacheTag hc
return $ case res of
Left e -> Left e
Right (hs,cts) -> Right $ Running hc hs cts dm
where
toTextErrors = mapBoth (T.pack . intercalate "\n") id
finalLoadHook (Running _ _ _ _) =
return $ Left "finalLoadHook called while running"
mapBoth :: (a -> c) -> (b -> d) -> Either a b -> Either c d
mapBoth f _ (Left x) = Left (f x)
mapBoth _ f (Right x) = Right (f x)
heistReloader :: Handler b (Heist b) ()
heistReloader = do
h <- get
ehs <- liftIO $ initHeist $ _masterConfig h
either (writeText . T.pack . unlines)
(\hs -> do writeText "Heist reloaded."
modifyMaster $ set heistState hs h)
ehs