{-# LANGUAGE BangPatterns      #-}
{-# LANGUAGE CPP               #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE TupleSections     #-}

{-|

This module defines the core data types used by Heist.  In practice you will
also want to import one or both of "Heist.Compiled" or "Heist.Interpreted" to
get functions needed for writing splices.

The Heist template system allows you to build custom HTML and XML based markup
languages.  With Heist you can define your own domain-specific tags
implemented with Haskell and use them in your templates.

-}

module Heist
  (
  -- * Primary Heist initialization functions
    loadTemplates
  , reloadTemplates
  , addTemplatePathPrefix
  , initHeist
  , initHeistWithCacheTag
  , defaultInterpretedSplices
  , defaultLoadTimeSplices
  , emptyHeistConfig

  -- * Core Heist data types
  , SpliceConfig
  , HeistConfig
  , TemplateRepo
  , TemplateLocation
  , Template
  , TPath
  , MIMEType
  , DocumentFile(..)
  , AttrSplice
  , RuntimeSplice
  , Chunk
  , HeistState
  , SpliceError(..)
  , CompileException(..)
  , HeistT

  -- * Lenses (can be used with lens or lens-family)
  , scInterpretedSplices
  , scLoadTimeSplices
  , scCompiledSplices
  , scAttributeSplices
  , scTemplateLocations
  , scCompiledTemplateFilter
  , hcSpliceConfig
  , hcNamespace
  , hcErrorNotBound
  , hcInterpretedSplices
  , hcLoadTimeSplices
  , hcCompiledSplices
  , hcAttributeSplices
  , hcTemplateLocations
  , hcCompiledTemplateFilter

  -- * HeistT functions
  , templateNames
  , compiledTemplateNames
  , hasTemplate
  , spliceNames
  , compiledSpliceNames
  , evalHeistT
  , getParamNode
  , getContext
  , getTemplateFilePath
  , localParamNode
  , getsHS
  , getHS
  , putHS
  , modifyHS
  , restoreHS
  , localHS
  , getDoc
  , getXMLDoc
  , tellSpliceError
  , spliceErrorText
  , orError
  , Splices
  ) where


------------------------------------------------------------------------------
import           Control.Exception.Lifted
import           Control.Monad.State
import           Data.ByteString               (ByteString)
import qualified Data.ByteString.Char8         as BC
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
#if !MIN_VERSION_base(4,11,0)
import           Data.Monoid
#endif
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.Splices
import           Heist.Internal.Types
------------------------------------------------------------------------------


------------------------------------------------------------------------------
-- | The built-in set of splices that you should use in compiled splice mode.
-- This list includes everything from 'defaultInterpretedSplices' plus a
-- splice for the content tag that errors out when it sees any instance of the
-- old content tag, which has now been moved to two separate tags called
-- apply-content and bind-content.
defaultLoadTimeSplices :: MonadIO m => Splices (I.Splice m)
defaultLoadTimeSplices :: Splices (Splice m)
defaultLoadTimeSplices = do
    -- To be removed in later versions
    Splices (Splice m)
forall (m :: * -> *). MonadIO m => Splices (Splice m)
defaultInterpretedSplices
    Text
"content" Text -> Splice m -> Splices (Splice m)
forall k v. k -> v -> MapSyntax k v
#! Splice m
forall (m :: * -> *). Monad m => Splice m
deprecatedContentCheck



------------------------------------------------------------------------------
-- | The built-in set of static splices.  All the splices that used to be
-- enabled by default are included here.  To get the normal Heist behavior you
-- should include these in the scLoadTimeSplices list in your SpliceConfig.
-- If you are using interpreted splice mode, then you might also want to bind
-- the 'deprecatedContentCheck' splice to the content tag as a load time
-- splice.
defaultInterpretedSplices :: MonadIO m => Splices (I.Splice m)
defaultInterpretedSplices :: Splices (Splice m)
defaultInterpretedSplices = do
    Text
applyTag Text -> Splice m -> Splices (Splice m)
forall k v. k -> v -> MapSyntax k v
## Splice m
forall (m :: * -> *). Monad m => Splice m
applyImpl
    Text
bindTag Text -> Splice m -> Splices (Splice m)
forall k v. k -> v -> MapSyntax k v
## Splice m
forall (m :: * -> *). Monad m => Splice m
bindImpl
    Text
ignoreTag Text -> Splice m -> Splices (Splice m)
forall k v. k -> v -> MapSyntax k v
## Splice m
forall (m :: * -> *). Monad m => Splice m
ignoreImpl
    Text
markdownTag Text -> Splice m -> Splices (Splice m)
forall k v. k -> v -> MapSyntax k v
## Splice m
forall (m :: * -> *). MonadIO m => Splice m
markdownSplice



------------------------------------------------------------------------------
-- | An empty HeistConfig that uses the \"h\" namespace with error checking
-- turned on.
emptyHeistConfig :: HeistConfig m
emptyHeistConfig :: HeistConfig m
emptyHeistConfig = SpliceConfig m -> Text -> Bool -> HeistConfig m
forall (m :: * -> *).
SpliceConfig m -> Text -> Bool -> HeistConfig m
HeistConfig SpliceConfig m
forall a. Monoid a => a
mempty Text
"h" Bool
True


allErrors :: [Either String (TPath, v)]
          -> Either [String] (HashMap TPath v)
allErrors :: [Either String (TPath, v)] -> Either [String] (HashMap TPath v)
allErrors [Either String (TPath, v)]
tlist =
    case [String]
errs of
        [] -> HashMap TPath v -> Either [String] (HashMap TPath v)
forall a b. b -> Either a b
Right (HashMap TPath v -> Either [String] (HashMap TPath v))
-> HashMap TPath v -> Either [String] (HashMap TPath v)
forall a b. (a -> b) -> a -> b
$ [(TPath, v)] -> HashMap TPath v
forall k v. (Eq k, Hashable k) => [(k, v)] -> HashMap k v
Map.fromList ([(TPath, v)] -> HashMap TPath v)
-> [(TPath, v)] -> HashMap TPath v
forall a b. (a -> b) -> a -> b
$ [Either String (TPath, v)] -> [(TPath, v)]
forall a b. [Either a b] -> [b]
rights [Either String (TPath, v)]
tlist
        [String]
_  -> [String] -> Either [String] (HashMap TPath v)
forall a b. a -> Either a b
Left [String]
errs
  where
    errs :: [String]
errs = [Either String (TPath, v)] -> [String]
forall a b. [Either a b] -> [a]
lefts [Either String (TPath, v)]
tlist


------------------------------------------------------------------------------
-- | Loads templates from disk.  This function returns just a template map so
-- you can load multiple directories and combine the maps before initializing
-- your HeistState.
loadTemplates :: FilePath -> IO (Either [String] TemplateRepo)
loadTemplates :: String -> IO (Either [String] TemplateRepo)
loadTemplates String
dir = do
    AnchoredDirTree [Either String (TPath, DocumentFile)]
d <- (String -> IO [Either String (TPath, DocumentFile)])
-> String
-> IO (AnchoredDirTree [Either String (TPath, DocumentFile)])
forall a. (String -> IO a) -> String -> IO (AnchoredDirTree a)
readDirectoryWith (String -> String -> IO [Either String (TPath, DocumentFile)]
loadTemplate String
dir) String
dir
#if MIN_VERSION_directory_tree(0,11,0)
    Either [String] TemplateRepo -> IO (Either [String] TemplateRepo)
forall (m :: * -> *) a. Monad m => a -> m a
return (Either [String] TemplateRepo -> IO (Either [String] TemplateRepo))
-> Either [String] TemplateRepo
-> IO (Either [String] TemplateRepo)
forall a b. (a -> b) -> a -> b
$ [Either String (TPath, DocumentFile)]
-> Either [String] TemplateRepo
forall v.
[Either String (TPath, v)] -> Either [String] (HashMap TPath v)
allErrors ([Either String (TPath, DocumentFile)]
 -> Either [String] TemplateRepo)
-> [Either String (TPath, DocumentFile)]
-> Either [String] TemplateRepo
forall a b. (a -> b) -> a -> b
$ DirTree [Either String (TPath, DocumentFile)]
-> [Either String (TPath, DocumentFile)]
forall (t :: * -> *) m. (Foldable t, Monoid m) => t m -> m
F.fold (AnchoredDirTree [Either String (TPath, DocumentFile)]
-> DirTree [Either String (TPath, DocumentFile)]
forall a. AnchoredDirTree a -> DirTree a
dirTree AnchoredDirTree [Either String (TPath, DocumentFile)]
d)
#else
    return $ allErrors $ F.fold (free d)
#endif


------------------------------------------------------------------------------
-- | Reloads all the templates an an existing TemplateRepo.
reloadTemplates :: TemplateRepo -> IO (Either [String] TemplateRepo)
reloadTemplates :: TemplateRepo -> IO (Either [String] TemplateRepo)
reloadTemplates TemplateRepo
repo = do
    [Either String (TPath, DocumentFile)]
tlist <- ((TPath, DocumentFile) -> IO (Either String (TPath, DocumentFile)))
-> [(TPath, DocumentFile)]
-> IO [Either String (TPath, DocumentFile)]
forall (t :: * -> *) (m :: * -> *) a b.
(Traversable t, Monad m) =>
(a -> m b) -> t a -> m (t b)
mapM (TPath, DocumentFile) -> IO (Either String (TPath, DocumentFile))
forall t. (t, DocumentFile) -> IO (Either String (t, DocumentFile))
loadOrKeep ([(TPath, DocumentFile)]
 -> IO [Either String (TPath, DocumentFile)])
-> [(TPath, DocumentFile)]
-> IO [Either String (TPath, DocumentFile)]
forall a b. (a -> b) -> a -> b
$ TemplateRepo -> [(TPath, DocumentFile)]
forall k v. HashMap k v -> [(k, v)]
Map.toList TemplateRepo
repo
    Either [String] TemplateRepo -> IO (Either [String] TemplateRepo)
forall (m :: * -> *) a. Monad m => a -> m a
return (Either [String] TemplateRepo -> IO (Either [String] TemplateRepo))
-> Either [String] TemplateRepo
-> IO (Either [String] TemplateRepo)
forall a b. (a -> b) -> a -> b
$ [Either String (TPath, DocumentFile)]
-> Either [String] TemplateRepo
forall v.
[Either String (TPath, v)] -> Either [String] (HashMap TPath v)
allErrors [Either String (TPath, DocumentFile)]
tlist
  where
    loadOrKeep :: (t, DocumentFile) -> IO (Either String (t, DocumentFile))
loadOrKeep (t
p,DocumentFile
df) =
      case DocumentFile -> Maybe String
dfFile DocumentFile
df of
        Maybe String
Nothing -> Either String (t, DocumentFile)
-> IO (Either String (t, DocumentFile))
forall (m :: * -> *) a. Monad m => a -> m a
return (Either String (t, DocumentFile)
 -> IO (Either String (t, DocumentFile)))
-> Either String (t, DocumentFile)
-> IO (Either String (t, DocumentFile))
forall a b. (a -> b) -> a -> b
$ (t, DocumentFile) -> Either String (t, DocumentFile)
forall a b. b -> Either a b
Right (t
p, DocumentFile
df)
        Just String
fp -> do
          [Either String DocumentFile]
df' <- String -> IO [Either String DocumentFile]
loadTemplate' String
fp
          Either String (t, DocumentFile)
-> IO (Either String (t, DocumentFile))
forall (m :: * -> *) a. Monad m => a -> m a
return (Either String (t, DocumentFile)
 -> IO (Either String (t, DocumentFile)))
-> Either String (t, DocumentFile)
-> IO (Either String (t, DocumentFile))
forall a b. (a -> b) -> a -> b
$ (DocumentFile -> (t, DocumentFile))
-> Either String DocumentFile -> Either String (t, DocumentFile)
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap (t
p,) (Either String DocumentFile -> Either String (t, DocumentFile))
-> Either String DocumentFile -> Either String (t, DocumentFile)
forall a b. (a -> b) -> a -> b
$ case [Either String DocumentFile]
df' of
            [Either String DocumentFile
t] -> Either String DocumentFile
t
            [Either String DocumentFile]
_ -> String -> Either String DocumentFile
forall a b. a -> Either a b
Left String
"Template repo has non-templates"


------------------------------------------------------------------------------
-- | Adds a path prefix to a templates in a map returned by loadTemplates.  If
-- you want to add multiple levels of directories, separate them with slashes
-- as in "foo/bar".  Using an empty string as a path prefix will leave the
-- map unchanged.
addTemplatePathPrefix :: ByteString -> TemplateRepo -> TemplateRepo
addTemplatePathPrefix :: ByteString -> TemplateRepo -> TemplateRepo
addTemplatePathPrefix ByteString
dir TemplateRepo
ts
  | ByteString -> Bool
B.null ByteString
dir = TemplateRepo
ts
  | Bool
otherwise  = [(TPath, DocumentFile)] -> TemplateRepo
forall k v. (Eq k, Hashable k) => [(k, v)] -> HashMap k v
Map.fromList ([(TPath, DocumentFile)] -> TemplateRepo)
-> [(TPath, DocumentFile)] -> TemplateRepo
forall a b. (a -> b) -> a -> b
$
                 ((TPath, DocumentFile) -> (TPath, DocumentFile))
-> [(TPath, DocumentFile)] -> [(TPath, DocumentFile)]
forall a b. (a -> b) -> [a] -> [b]
map (\(TPath
x,DocumentFile
y) -> (TPath -> TPath
f TPath
x, DocumentFile
y)) ([(TPath, DocumentFile)] -> [(TPath, DocumentFile)])
-> [(TPath, DocumentFile)] -> [(TPath, DocumentFile)]
forall a b. (a -> b) -> a -> b
$
                 TemplateRepo -> [(TPath, DocumentFile)]
forall k v. HashMap k v -> [(k, v)]
Map.toList TemplateRepo
ts
  where
    f :: TPath -> TPath
f TPath
ps = TPath
psTPath -> TPath -> TPath
forall a. [a] -> [a] -> [a]
++ByteString -> TPath
splitTemplatePath ByteString
dir


------------------------------------------------------------------------------
-- | Creates an empty HeistState.
emptyHS :: HE.KeyGen -> HeistState m
emptyHS :: KeyGen -> HeistState m
emptyHS KeyGen
kg = HashMap Text (HeistT m m Template)
-> TemplateRepo
-> HashMap Text (HeistT m IO (DList (Chunk m)))
-> HashMap TPath ([Chunk m], ByteString)
-> HashMap Text (AttrSplice m)
-> Bool
-> TPath
-> [(TPath, Maybe String, Text)]
-> Int
-> [DocType]
-> Maybe String
-> KeyGen
-> Bool
-> Markup
-> Text
-> [SpliceError]
-> Bool
-> Int
-> HeistState m
forall (m :: * -> *).
HashMap Text (HeistT m m Template)
-> TemplateRepo
-> HashMap Text (HeistT m IO (DList (Chunk m)))
-> HashMap TPath ([Chunk m], ByteString)
-> HashMap Text (AttrSplice m)
-> Bool
-> TPath
-> [(TPath, Maybe String, Text)]
-> Int
-> [DocType]
-> Maybe String
-> KeyGen
-> Bool
-> Markup
-> Text
-> [SpliceError]
-> Bool
-> Int
-> HeistState m
HeistState HashMap Text (HeistT m m Template)
forall k v. HashMap k v
Map.empty TemplateRepo
forall k v. HashMap k v
Map.empty HashMap Text (HeistT m IO (DList (Chunk m)))
forall k v. HashMap k v
Map.empty HashMap TPath ([Chunk m], ByteString)
forall k v. HashMap k v
Map.empty HashMap Text (AttrSplice m)
forall k v. HashMap k v
Map.empty
                        Bool
True [] [] Int
0 [] Maybe String
forall a. Maybe a
Nothing KeyGen
kg Bool
False Markup
Html Text
"" [] Bool
False Int
0


------------------------------------------------------------------------------
-- | This is the main Heist initialization function.  You pass in a map of all
-- templates and all of your splices and it constructs and returns a
-- HeistState.
--
-- We don't provide functions to add either type of loadtime splices to your
-- HeistState after initHeist because it doesn't make any sense unless you
-- re-initialize all templates with the new splices.  If you add any old-style
-- runtime heist splices after calling this function, they will still work
-- fine if you use Heist.Interpreted.renderTemplate.  If you add any templates
-- later, then those templates will be available for interpreted rendering,
-- but not for compiled rendering.
--
-- In the past you could add templates to your HeistState after initialization
-- using its Monoid instance.  Due to implementation details, this is no
-- longer possible.  All of your templates must be known when you call this
-- function.
initHeist :: Monad n
          => HeistConfig n
          -> IO (Either [String] (HeistState n))
initHeist :: HeistConfig n -> IO (Either [String] (HeistState n))
initHeist HeistConfig n
hc = do
    KeyGen
keyGen <- IO KeyGen
HE.newKeyGen
    [Either [String] TemplateRepo]
repos <- [IO (Either [String] TemplateRepo)]
-> IO [Either [String] TemplateRepo]
forall (t :: * -> *) (m :: * -> *) a.
(Traversable t, Monad m) =>
t (m a) -> m (t a)
sequence ([IO (Either [String] TemplateRepo)]
 -> IO [Either [String] TemplateRepo])
-> [IO (Either [String] TemplateRepo)]
-> IO [Either [String] TemplateRepo]
forall a b. (a -> b) -> a -> b
$ SpliceConfig n -> [IO (Either [String] TemplateRepo)]
forall (m :: * -> *).
SpliceConfig m -> [IO (Either [String] TemplateRepo)]
_scTemplateLocations (SpliceConfig n -> [IO (Either [String] TemplateRepo)])
-> SpliceConfig n -> [IO (Either [String] TemplateRepo)]
forall a b. (a -> b) -> a -> b
$ HeistConfig n -> SpliceConfig n
forall (m :: * -> *). HeistConfig m -> SpliceConfig m
_hcSpliceConfig HeistConfig n
hc
    case [Either [String] TemplateRepo] -> Either [String] [TemplateRepo]
forall (t :: * -> *) (m :: * -> *) a.
(Traversable t, Monad m) =>
t (m a) -> m (t a)
sequence [Either [String] TemplateRepo]
repos of
      Left [String]
es -> Either [String] (HeistState n)
-> IO (Either [String] (HeistState n))
forall (m :: * -> *) a. Monad m => a -> m a
return (Either [String] (HeistState n)
 -> IO (Either [String] (HeistState n)))
-> Either [String] (HeistState n)
-> IO (Either [String] (HeistState n))
forall a b. (a -> b) -> a -> b
$ [String] -> Either [String] (HeistState n)
forall a b. a -> Either a b
Left [String]
es
      Right [TemplateRepo]
rs -> KeyGen
-> HeistConfig n
-> TemplateRepo
-> IO (Either [String] (HeistState n))
forall (n :: * -> *).
Monad n =>
KeyGen
-> HeistConfig n
-> TemplateRepo
-> IO (Either [String] (HeistState n))
initHeist' KeyGen
keyGen HeistConfig n
hc ([TemplateRepo] -> TemplateRepo
forall k v. (Eq k, Hashable k) => [HashMap k v] -> HashMap k v
Map.unions [TemplateRepo]
rs)


------------------------------------------------------------------------------
mkSplicePrefix :: Text -> Text
mkSplicePrefix :: Text -> Text
mkSplicePrefix Text
ns
  | Text -> Bool
T.null Text
ns = Text
""
  | Bool
otherwise = Text
ns Text -> Text -> Text
forall a. Monoid a => a -> a -> a
`mappend` Text
":"


------------------------------------------------------------------------------
initHeist' :: Monad n
           => HE.KeyGen
           -> HeistConfig n
           -> TemplateRepo
           -> IO (Either [String] (HeistState n))
initHeist' :: KeyGen
-> HeistConfig n
-> TemplateRepo
-> IO (Either [String] (HeistState n))
initHeist' KeyGen
keyGen (HeistConfig SpliceConfig n
sc Text
ns Bool
enn) TemplateRepo
repo = do
    let empty :: HeistState m
empty = KeyGen -> HeistState m
forall (m :: * -> *). KeyGen -> HeistState m
emptyHS KeyGen
keyGen
    let (SpliceConfig Splices (Splice n)
i Splices (Splice IO)
lt Splices (Splice n)
c Splices (AttrSplice n)
a [IO (Either [String] TemplateRepo)]
_ TPath -> Bool
f) = SpliceConfig n
sc
    Either [String] TemplateRepo
etmap <- KeyGen
-> Splices (Splice IO)
-> TemplateRepo
-> Text
-> IO (Either [String] TemplateRepo)
preproc KeyGen
keyGen Splices (Splice IO)
lt TemplateRepo
repo Text
ns
    let prefix :: Text
prefix = Text -> Text
mkSplicePrefix Text
ns
    let eis :: Either [String] (HashMap Text (Splice n))
eis = Splices (Splice n) -> Either [String] (HashMap Text (Splice n))
forall s. Splices s -> Either [String] (HashMap Text s)
runHashMap (Splices (Splice n) -> Either [String] (HashMap Text (Splice n)))
-> Splices (Splice n) -> Either [String] (HashMap Text (Splice n))
forall a b. (a -> b) -> a -> b
$ (Text -> Text) -> Splices (Splice n) -> Splices (Splice n)
forall k1 k2 v a. (k1 -> k2) -> MapSyntaxM k1 v a -> MapSyntax k2 v
mapK (Text
prefixText -> Text -> Text
forall a. Semigroup a => a -> a -> a
<>) Splices (Splice n)
i
        ecs :: Either [String] (HashMap Text (Splice n))
ecs = Splices (Splice n) -> Either [String] (HashMap Text (Splice n))
forall s. Splices s -> Either [String] (HashMap Text s)
runHashMap (Splices (Splice n) -> Either [String] (HashMap Text (Splice n)))
-> Splices (Splice n) -> Either [String] (HashMap Text (Splice n))
forall a b. (a -> b) -> a -> b
$ (Text -> Text) -> Splices (Splice n) -> Splices (Splice n)
forall k1 k2 v a. (k1 -> k2) -> MapSyntaxM k1 v a -> MapSyntax k2 v
mapK (Text
prefixText -> Text -> Text
forall a. Semigroup a => a -> a -> a
<>) Splices (Splice n)
c
        eas :: Either [String] (HashMap Text (AttrSplice n))
eas = Splices (AttrSplice n)
-> Either [String] (HashMap Text (AttrSplice n))
forall s. Splices s -> Either [String] (HashMap Text s)
runHashMap (Splices (AttrSplice n)
 -> Either [String] (HashMap Text (AttrSplice n)))
-> Splices (AttrSplice n)
-> Either [String] (HashMap Text (AttrSplice n))
forall a b. (a -> b) -> a -> b
$ (Text -> Text) -> Splices (AttrSplice n) -> Splices (AttrSplice n)
forall k1 k2 v a. (k1 -> k2) -> MapSyntaxM k1 v a -> MapSyntax k2 v
mapK (Text
prefixText -> Text -> Text
forall a. Semigroup a => a -> a -> a
<>) Splices (AttrSplice n)
a
    let hs1 :: Either [String] (HeistState n)
hs1 = do
          TemplateRepo
tmap <- Either [String] TemplateRepo
etmap
          HashMap Text (Splice n)
is <- Either [String] (HashMap Text (Splice n))
eis
          HashMap Text (Splice n)
cs <- Either [String] (HashMap Text (Splice n))
ecs
          HashMap Text (AttrSplice n)
as <- Either [String] (HashMap Text (AttrSplice n))
eas
          HeistState n -> Either [String] (HeistState n)
forall (m :: * -> *) a. Monad m => a -> m a
return (HeistState n -> Either [String] (HeistState n))
-> HeistState n -> Either [String] (HeistState n)
forall a b. (a -> b) -> a -> b
$ HeistState n
forall (m :: * -> *). HeistState m
empty { _spliceMap :: HashMap Text (Splice n)
_spliceMap = HashMap Text (Splice n)
is
                         , _templateMap :: TemplateRepo
_templateMap = TemplateRepo
tmap
                         , _compiledSpliceMap :: HashMap Text (Splice n)
_compiledSpliceMap = HashMap Text (Splice n)
cs
                         , _attrSpliceMap :: HashMap Text (AttrSplice n)
_attrSpliceMap = HashMap Text (AttrSplice n)
as
                         , _splicePrefix :: Text
_splicePrefix = Text
prefix
                         , _errorNotBound :: Bool
_errorNotBound = Bool
enn
                         }
    ([String] -> IO (Either [String] (HeistState n)))
-> (HeistState n -> IO (Either [String] (HeistState n)))
-> Either [String] (HeistState n)
-> IO (Either [String] (HeistState n))
forall a c b. (a -> c) -> (b -> c) -> Either a b -> c
either (Either [String] (HeistState n)
-> IO (Either [String] (HeistState n))
forall (m :: * -> *) a. Monad m => a -> m a
return (Either [String] (HeistState n)
 -> IO (Either [String] (HeistState n)))
-> ([String] -> Either [String] (HeistState n))
-> [String]
-> IO (Either [String] (HeistState n))
forall b c a. (b -> c) -> (a -> b) -> a -> c
. [String] -> Either [String] (HeistState n)
forall a b. a -> Either a b
Left) ((TPath -> Bool)
-> HeistState n -> IO (Either [String] (HeistState n))
forall (n :: * -> *).
Monad n =>
(TPath -> Bool)
-> HeistState n -> IO (Either [String] (HeistState n))
C.compileTemplates TPath -> Bool
f) Either [String] (HeistState n)
hs1


------------------------------------------------------------------------------
-- | Runs preprocess on a TemplateRepo and returns the modified templates.
preproc :: HE.KeyGen
        -> Splices (I.Splice IO)
        -> TemplateRepo
        -> Text
        -> IO (Either [String] TemplateRepo)
preproc :: KeyGen
-> Splices (Splice IO)
-> TemplateRepo
-> Text
-> IO (Either [String] TemplateRepo)
preproc KeyGen
keyGen Splices (Splice IO)
splices TemplateRepo
templates Text
ns = do
    let esm :: Either [String] (HashMap Text (Splice IO))
esm = Splices (Splice IO) -> Either [String] (HashMap Text (Splice IO))
forall s. Splices s -> Either [String] (HashMap Text s)
runHashMap Splices (Splice IO)
splices
    case Either [String] (HashMap Text (Splice IO))
esm of
      Left [String]
errs -> Either [String] TemplateRepo -> IO (Either [String] TemplateRepo)
forall (m :: * -> *) a. Monad m => a -> m a
return (Either [String] TemplateRepo -> IO (Either [String] TemplateRepo))
-> Either [String] TemplateRepo
-> IO (Either [String] TemplateRepo)
forall a b. (a -> b) -> a -> b
$ [String] -> Either [String] TemplateRepo
forall a b. a -> Either a b
Left [String]
errs
      Right HashMap Text (Splice IO)
sm -> do
        let hs :: HeistState IO
hs = (KeyGen -> HeistState IO
forall (m :: * -> *). KeyGen -> HeistState m
emptyHS KeyGen
keyGen) { _spliceMap :: HashMap Text (Splice IO)
_spliceMap = HashMap Text (Splice IO)
sm
                                  , _templateMap :: TemplateRepo
_templateMap = TemplateRepo
templates
                                  , _preprocessingMode :: Bool
_preprocessingMode = Bool
True
                                  , _splicePrefix :: Text
_splicePrefix = Text -> Text
mkSplicePrefix Text
ns }
        let eval :: HeistT IO m a -> m a
eval HeistT IO m a
a = HeistT IO m a -> Node -> HeistState IO -> m a
forall (m :: * -> *) (n :: * -> *) a.
Monad m =>
HeistT n m a -> Node -> HeistState n -> m a
evalHeistT HeistT IO m a
a (Text -> Node
X.TextNode Text
"") HeistState IO
hs
        [Either String (TPath, DocumentFile)]
tPairs <- ((TPath, DocumentFile) -> IO (Either String (TPath, DocumentFile)))
-> [(TPath, DocumentFile)]
-> IO [Either String (TPath, DocumentFile)]
forall (t :: * -> *) (m :: * -> *) a b.
(Traversable t, Monad m) =>
(a -> m b) -> t a -> m (t b)
mapM (HeistT IO IO (Either String (TPath, DocumentFile))
-> IO (Either String (TPath, DocumentFile))
forall (m :: * -> *) a. Monad m => HeistT IO m a -> m a
eval (HeistT IO IO (Either String (TPath, DocumentFile))
 -> IO (Either String (TPath, DocumentFile)))
-> ((TPath, DocumentFile)
    -> HeistT IO IO (Either String (TPath, DocumentFile)))
-> (TPath, DocumentFile)
-> IO (Either String (TPath, DocumentFile))
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (TPath, DocumentFile)
-> HeistT IO IO (Either String (TPath, DocumentFile))
preprocess) ([(TPath, DocumentFile)]
 -> IO [Either String (TPath, DocumentFile)])
-> [(TPath, DocumentFile)]
-> IO [Either String (TPath, DocumentFile)]
forall a b. (a -> b) -> a -> b
$ TemplateRepo -> [(TPath, DocumentFile)]
forall k v. HashMap k v -> [(k, v)]
Map.toList TemplateRepo
templates
        let bad :: [String]
bad = [Either String (TPath, DocumentFile)] -> [String]
forall a b. [Either a b] -> [a]
lefts [Either String (TPath, DocumentFile)]
tPairs
        Either [String] TemplateRepo -> IO (Either [String] TemplateRepo)
forall (m :: * -> *) a. Monad m => a -> m a
return (Either [String] TemplateRepo -> IO (Either [String] TemplateRepo))
-> Either [String] TemplateRepo
-> IO (Either [String] TemplateRepo)
forall a b. (a -> b) -> a -> b
$ if Bool -> Bool
not ([String] -> Bool
forall (t :: * -> *) a. Foldable t => t a -> Bool
null [String]
bad)
                   then [String] -> Either [String] TemplateRepo
forall a b. a -> Either a b
Left [String]
bad
                   else TemplateRepo -> Either [String] TemplateRepo
forall a b. b -> Either a b
Right (TemplateRepo -> Either [String] TemplateRepo)
-> TemplateRepo -> Either [String] TemplateRepo
forall a b. (a -> b) -> a -> b
$ [(TPath, DocumentFile)] -> TemplateRepo
forall k v. (Eq k, Hashable k) => [(k, v)] -> HashMap k v
Map.fromList ([(TPath, DocumentFile)] -> TemplateRepo)
-> [(TPath, DocumentFile)] -> TemplateRepo
forall a b. (a -> b) -> a -> b
$ [Either String (TPath, DocumentFile)] -> [(TPath, DocumentFile)]
forall a b. [Either a b] -> [b]
rights [Either String (TPath, DocumentFile)]
tPairs


------------------------------------------------------------------------------
-- | Processes a single template, running load time splices.
preprocess :: (TPath, DocumentFile)
           -> HeistT IO IO (Either String (TPath, DocumentFile))
preprocess :: (TPath, DocumentFile)
-> HeistT IO IO (Either String (TPath, DocumentFile))
preprocess (TPath
tpath, DocumentFile
docFile) = do
    let tname :: ByteString
tname = TPath -> ByteString
tpathName TPath
tpath
        die :: a
die   = String -> a
forall a. HasCallStack => String -> a
error (String -> a) -> String -> a
forall a b. (a -> b) -> a -> b
$ String
"Preprocess failed because the template `"
                     String -> String -> String
forall a. [a] -> [a] -> [a]
++ ByteString -> String
BC.unpack ByteString
tname
                     String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
"` was not found in the template repository."
    !Either SomeException (Maybe Document)
emdoc <- HeistT IO IO (Maybe Document)
-> HeistT IO IO (Either SomeException (Maybe Document))
forall (m :: * -> *) e a.
(MonadBaseControl IO m, Exception e) =>
m a -> m (Either e a)
try (HeistT IO IO (Maybe Document)
 -> HeistT IO IO (Either SomeException (Maybe Document)))
-> HeistT IO IO (Maybe Document)
-> HeistT IO IO (Either SomeException (Maybe Document))
forall a b. (a -> b) -> a -> b
$ ByteString -> HeistT IO IO (Maybe Document)
forall (n :: * -> *).
Monad n =>
ByteString -> HeistT n n (Maybe Document)
I.evalWithDoctypes ByteString
tname
              :: HeistT IO IO (Either SomeException (Maybe X.Document))
    let f :: Document -> (TPath, DocumentFile)
f !Document
doc = (TPath
tpath, DocumentFile
docFile { dfDoc :: Document
dfDoc = Document
doc })
    Either String (TPath, DocumentFile)
-> HeistT IO IO (Either String (TPath, DocumentFile))
forall (m :: * -> *) a. Monad m => a -> m a
return (Either String (TPath, DocumentFile)
 -> HeistT IO IO (Either String (TPath, DocumentFile)))
-> Either String (TPath, DocumentFile)
-> HeistT IO IO (Either String (TPath, DocumentFile))
forall a b. (a -> b) -> a -> b
$! (SomeException -> Either String (TPath, DocumentFile))
-> (Maybe Document -> Either String (TPath, DocumentFile))
-> Either SomeException (Maybe Document)
-> Either String (TPath, DocumentFile)
forall a c b. (a -> c) -> (b -> c) -> Either a b -> c
either (String -> Either String (TPath, DocumentFile)
forall a b. a -> Either a b
Left (String -> Either String (TPath, DocumentFile))
-> (SomeException -> String)
-> SomeException
-> Either String (TPath, DocumentFile)
forall b c a. (b -> c) -> (a -> b) -> a -> c
. SomeException -> String
forall a. Show a => a -> String
show) ((TPath, DocumentFile) -> Either String (TPath, DocumentFile)
forall a b. b -> Either a b
Right ((TPath, DocumentFile) -> Either String (TPath, DocumentFile))
-> (Maybe Document -> (TPath, DocumentFile))
-> Maybe Document
-> Either String (TPath, DocumentFile)
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (TPath, DocumentFile)
-> (Document -> (TPath, DocumentFile))
-> Maybe Document
-> (TPath, DocumentFile)
forall b a. b -> (a -> b) -> Maybe a -> b
maybe (TPath, DocumentFile)
forall a. a
die Document -> (TPath, DocumentFile)
f) Either SomeException (Maybe Document)
emdoc


------------------------------------------------------------------------------
-- | Wrapper around initHeist that also sets up a cache tag.  It sets up both
-- compiled and interpreted versions of the cache tag splices.  If you need to
-- configure the cache tag differently than how this function does it, you
-- will still probably want to pattern your approach after this function's
-- implementation.
initHeistWithCacheTag :: MonadIO n
                      => HeistConfig n
                      -> IO (Either [String] (HeistState n, CacheTagState))
initHeistWithCacheTag :: HeistConfig n -> IO (Either [String] (HeistState n, CacheTagState))
initHeistWithCacheTag (HeistConfig SpliceConfig n
sc Text
ns Bool
enn) = do
    (Splice IO
ss, CacheTagState
cts) <- IO (Splice IO, CacheTagState) -> IO (Splice IO, CacheTagState)
forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO IO (Splice IO, CacheTagState)
mkCacheTag
    let tag :: p
tag = p
"cache"
    KeyGen
keyGen <- IO KeyGen
HE.newKeyGen

    [Either [String] TemplateRepo]
erepos <- [IO (Either [String] TemplateRepo)]
-> IO [Either [String] TemplateRepo]
forall (t :: * -> *) (m :: * -> *) a.
(Traversable t, Monad m) =>
t (m a) -> m (t a)
sequence ([IO (Either [String] TemplateRepo)]
 -> IO [Either [String] TemplateRepo])
-> [IO (Either [String] TemplateRepo)]
-> IO [Either [String] TemplateRepo]
forall a b. (a -> b) -> a -> b
$ SpliceConfig n -> [IO (Either [String] TemplateRepo)]
forall (m :: * -> *).
SpliceConfig m -> [IO (Either [String] TemplateRepo)]
_scTemplateLocations SpliceConfig n
sc
    case [Either [String] TemplateRepo] -> Either [String] [TemplateRepo]
forall (t :: * -> *) (m :: * -> *) a.
(Traversable t, Monad m) =>
t (m a) -> m (t a)
sequence [Either [String] TemplateRepo]
erepos of
      Left [String]
es -> Either [String] (HeistState n, CacheTagState)
-> IO (Either [String] (HeistState n, CacheTagState))
forall (m :: * -> *) a. Monad m => a -> m a
return (Either [String] (HeistState n, CacheTagState)
 -> IO (Either [String] (HeistState n, CacheTagState)))
-> Either [String] (HeistState n, CacheTagState)
-> IO (Either [String] (HeistState n, CacheTagState))
forall a b. (a -> b) -> a -> b
$ [String] -> Either [String] (HeistState n, CacheTagState)
forall a b. a -> Either a b
Left [String]
es
      Right [TemplateRepo]
repos -> do
        -- We have to do one preprocessing pass with the cache setup splice.  This
        -- has to happen for both interpreted and compiled templates, so we do it
        -- here by itself because interpreted templates don't get the same load
        -- time splices as compiled templates.
        Either [String] TemplateRepo
eRawWithCache <- KeyGen
-> Splices (Splice IO)
-> TemplateRepo
-> Text
-> IO (Either [String] TemplateRepo)
preproc KeyGen
keyGen (Text
forall p. IsString p => p
tag Text -> Splice IO -> Splices (Splice IO)
forall k v. k -> v -> MapSyntax k v
## Splice IO
ss) ([TemplateRepo] -> TemplateRepo
forall k v. (Eq k, Hashable k) => [HashMap k v] -> HashMap k v
Map.unions [TemplateRepo]
repos) Text
ns
        case Either [String] TemplateRepo
eRawWithCache of
          Left [String]
es -> Either [String] (HeistState n, CacheTagState)
-> IO (Either [String] (HeistState n, CacheTagState))
forall (m :: * -> *) a. Monad m => a -> m a
return (Either [String] (HeistState n, CacheTagState)
 -> IO (Either [String] (HeistState n, CacheTagState)))
-> Either [String] (HeistState n, CacheTagState)
-> IO (Either [String] (HeistState n, CacheTagState))
forall a b. (a -> b) -> a -> b
$ [String] -> Either [String] (HeistState n, CacheTagState)
forall a b. a -> Either a b
Left [String]
es
          Right TemplateRepo
rawWithCache -> do
            let sc' :: SpliceConfig m
sc' = Splices (Splice m)
-> Splices (Splice IO)
-> Splices (Splice m)
-> Splices (AttrSplice m)
-> [IO (Either [String] TemplateRepo)]
-> (TPath -> Bool)
-> SpliceConfig m
forall (m :: * -> *).
Splices (Splice m)
-> Splices (Splice IO)
-> Splices (Splice m)
-> Splices (AttrSplice m)
-> [IO (Either [String] TemplateRepo)]
-> (TPath -> Bool)
-> SpliceConfig m
SpliceConfig (Text
forall p. IsString p => p
tag Text -> Splice m -> Splices (Splice m)
forall k v. k -> v -> MapSyntax k v
#! CacheTagState -> Splice m
forall (n :: * -> *). MonadIO n => CacheTagState -> Splice n
cacheImpl CacheTagState
cts) Splices (Splice IO)
forall a. Monoid a => a
mempty
                                   (Text
forall p. IsString p => p
tag Text -> Splice m -> Splices (Splice m)
forall k v. k -> v -> MapSyntax k v
#! CacheTagState -> Splice m
forall (n :: * -> *). MonadIO n => CacheTagState -> Splice n
cacheImplCompiled CacheTagState
cts)
                                   Splices (AttrSplice m)
forall a. Monoid a => a
mempty [IO (Either [String] TemplateRepo)]
forall a. Monoid a => a
mempty (Bool -> TPath -> Bool
forall a b. a -> b -> a
const Bool
True)
            let hc :: HeistConfig n
hc = SpliceConfig n -> Text -> Bool -> HeistConfig n
forall (m :: * -> *).
SpliceConfig m -> Text -> Bool -> HeistConfig m
HeistConfig (SpliceConfig n -> SpliceConfig n -> SpliceConfig n
forall a. Monoid a => a -> a -> a
mappend SpliceConfig n
sc SpliceConfig n
forall (m :: * -> *). MonadIO m => SpliceConfig m
sc') Text
ns Bool
enn
            Either [String] (HeistState n)
hs <- KeyGen
-> HeistConfig n
-> TemplateRepo
-> IO (Either [String] (HeistState n))
forall (n :: * -> *).
Monad n =>
KeyGen
-> HeistConfig n
-> TemplateRepo
-> IO (Either [String] (HeistState n))
initHeist' KeyGen
keyGen HeistConfig n
hc TemplateRepo
rawWithCache
            Either [String] (HeistState n, CacheTagState)
-> IO (Either [String] (HeistState n, CacheTagState))
forall (m :: * -> *) a. Monad m => a -> m a
return (Either [String] (HeistState n, CacheTagState)
 -> IO (Either [String] (HeistState n, CacheTagState)))
-> Either [String] (HeistState n, CacheTagState)
-> IO (Either [String] (HeistState n, CacheTagState))
forall a b. (a -> b) -> a -> b
$ (HeistState n -> (HeistState n, CacheTagState))
-> Either [String] (HeistState n)
-> Either [String] (HeistState n, CacheTagState)
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap (,CacheTagState
cts) Either [String] (HeistState n)
hs