module Heist
(
loadTemplates
, reloadTemplates
, addTemplatePathPrefix
, initHeist
, initHeistWithCacheTag
, defaultInterpretedSplices
, defaultLoadTimeSplices
, emptyHeistConfig
, SpliceConfig
, HeistConfig
, TemplateRepo
, TemplateLocation
, Template
, TPath
, MIMEType
, DocumentFile(..)
, AttrSplice
, RuntimeSplice
, Chunk
, HeistState
, HeistT
, scInterpretedSplices
, scLoadTimeSplices
, scCompiledSplices
, scAttributeSplices
, scTemplateLocations
, hcSpliceConfig
, hcNamespace
, hcErrorNotBound
, hcInterpretedSplices
, hcLoadTimeSplices
, hcCompiledSplices
, hcAttributeSplices
, hcTemplateLocations
, templateNames
, compiledTemplateNames
, hasTemplate
, spliceNames
, compiledSpliceNames
, evalHeistT
, getParamNode
, getContext
, getTemplateFilePath
, localParamNode
, getsHS
, getHS
, putHS
, modifyHS
, restoreHS
, localHS
, getDoc
, getXMLDoc
, tellSpliceError
, orError
, module Heist.SpliceAPI
) where
import Control.Exception (SomeException)
import Control.Monad.CatchIO
import Control.Monad.State
import Control.Monad.Trans.Either
import Data.ByteString (ByteString)
import qualified Data.ByteString as B
import Data.Either
import qualified Data.Foldable as F
import Data.HashMap.Strict (HashMap)
import qualified Data.HashMap.Strict as Map
import qualified Data.HeterogeneousEnvironment as HE
import Data.Map.Syntax
import Data.Monoid
import Data.Text (Text)
import qualified Data.Text as T
import System.Directory.Tree
import qualified Text.XmlHtml as X
import Heist.Common
import qualified Heist.Compiled.Internal as C
import qualified Heist.Interpreted.Internal as I
import Heist.SpliceAPI
import Heist.Splices
import Heist.Internal.Types
defaultLoadTimeSplices :: MonadIO m => Splices (I.Splice m)
defaultLoadTimeSplices = do
defaultInterpretedSplices
"content" #! deprecatedContentCheck
defaultInterpretedSplices :: MonadIO m => Splices (I.Splice m)
defaultInterpretedSplices = do
applyTag ## applyImpl
bindTag ## bindImpl
ignoreTag ## ignoreImpl
markdownTag ## markdownSplice
emptyHeistConfig :: HeistConfig m
emptyHeistConfig = HeistConfig mempty "h" True
allErrors :: [Either String (TPath, v)]
-> EitherT [String] IO (HashMap TPath v)
allErrors tlist =
case errs of
[] -> right $ Map.fromList $ rights tlist
_ -> left errs
where
errs = lefts tlist
loadTemplates :: FilePath -> EitherT [String] IO TemplateRepo
loadTemplates dir = do
d <- lift $ readDirectoryWith (loadTemplate dir) dir
allErrors $ F.fold (free d)
reloadTemplates :: TemplateRepo -> EitherT [String] IO TemplateRepo
reloadTemplates repo = do
tlist <- lift $ mapM loadOrKeep $ Map.toList repo
allErrors tlist
where
loadOrKeep (p,df) =
case dfFile df of
Nothing -> return $ Right (p, df)
Just fp -> do
df' <- loadTemplate' fp
return $ fmap (p,) $ case df' of
[t] -> t
_ -> Left "Template repo has non-templates"
addTemplatePathPrefix :: ByteString -> TemplateRepo -> TemplateRepo
addTemplatePathPrefix dir ts
| B.null dir = ts
| otherwise = Map.fromList $
map (\(x,y) -> (f x, y)) $
Map.toList ts
where
f ps = ps++splitTemplatePath dir
emptyHS :: HE.KeyGen -> HeistState m
emptyHS kg = HeistState Map.empty Map.empty Map.empty Map.empty Map.empty
True [] 0 [] Nothing kg False Html "" [] False
initHeist :: Monad n
=> HeistConfig n
-> EitherT [String] IO (HeistState n)
initHeist hc = do
keyGen <- lift HE.newKeyGen
repos <- sequence $ _scTemplateLocations $ _hcSpliceConfig hc
initHeist' keyGen hc (Map.unions repos)
mkSplicePrefix :: Text -> Text
mkSplicePrefix ns
| T.null ns = ""
| otherwise = ns `mappend` ":"
initHeist' :: Monad n
=> HE.KeyGen
-> HeistConfig n
-> TemplateRepo
-> EitherT [String] IO (HeistState n)
initHeist' keyGen (HeistConfig sc ns enn) repo = do
let empty = emptyHS keyGen
let (SpliceConfig i lt c a _) = sc
tmap <- preproc keyGen lt repo ns
let prefix = mkSplicePrefix ns
is <- runHashMap $ mapK (prefix<>) i
cs <- runHashMap $ mapK (prefix<>) c
as <- runHashMap $ mapK (prefix<>) a
let hs1 = empty { _spliceMap = is
, _templateMap = tmap
, _compiledSpliceMap = cs
, _attrSpliceMap = as
, _splicePrefix = prefix
, _errorNotBound = enn
}
EitherT $ C.compileTemplates hs1
preproc :: HE.KeyGen
-> Splices (I.Splice IO)
-> TemplateRepo
-> Text
-> EitherT [String] IO TemplateRepo
preproc keyGen splices templates ns = do
sm <- runHashMap splices
let hs = (emptyHS keyGen) { _spliceMap = sm
, _templateMap = templates
, _preprocessingMode = True
, _splicePrefix = mkSplicePrefix ns }
let eval a = evalHeistT a (X.TextNode "") hs
tPairs <- lift $ mapM (eval . preprocess) $ Map.toList templates
let bad = lefts tPairs
if not (null bad)
then left bad
else right $ Map.fromList $ rights tPairs
preprocess :: (TPath, DocumentFile)
-> HeistT IO IO (Either String (TPath, DocumentFile))
preprocess (tpath, docFile) = do
let tname = tpathName tpath
!emdoc <- try $ I.evalWithDoctypes tname
:: HeistT IO IO (Either SomeException (Maybe X.Document))
let f !doc = (tpath, docFile { dfDoc = doc })
return $! either (Left . show) (Right . maybe die f) emdoc
where
die = error "Preprocess didn't succeed! This should never happen."
initHeistWithCacheTag :: MonadIO n
=> HeistConfig n
-> EitherT [String] IO (HeistState n, CacheTagState)
initHeistWithCacheTag (HeistConfig sc ns enn) = do
(ss, cts) <- liftIO mkCacheTag
let tag = "cache"
keyGen <- lift HE.newKeyGen
repos <- sequence $ _scTemplateLocations sc
rawWithCache <- preproc keyGen (tag ## ss) (Map.unions repos) ns
let sc' = SpliceConfig (tag #! cacheImpl cts) mempty
(tag #! cacheImplCompiled cts) mempty mempty
let hc = HeistConfig (mappend sc sc') ns enn
hs <- initHeist' keyGen hc rawWithCache
return (hs, cts)