    {-# 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

      -- * TPath functions
      , lookupTemplate
      , splitTemplatePath
      ) 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 :: forall (m :: * -> *). MonadIO m => 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 :: forall (m :: * -> *). MonadIO m => 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 :: forall (m :: * -> *). 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 :: forall v.
    [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 a. a -> IO a
    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 m. Monoid m => DirTree m -> m
    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)
    forall (m :: * -> *) a b. Monad m => (a -> m b) -> [a] -> m [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 a. a -> IO a
    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 a. a -> IO a
    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 a. a -> IO a
    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 a b. (a -> b) -> Either String a -> Either String b
    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 :: forall (m :: * -> *). 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 :: forall (n :: * -> *).
    Monad n =>
    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)
    forall (m :: * -> *) a. Monad m => [m a] -> m [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)
    forall (m :: * -> *) a. Monad m => [m a] -> m [a]
    sequence [Either [String] TemplateRepo]
    repos of
          Left [String]
    es -> Either [String] (HeistState n)
    -> IO (Either [String] (HeistState n))
    forall a. a -> IO a
    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' :: forall (n :: * -> *).
    Monad n =>
    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 a. a -> Either [String] a
    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 a. a -> IO a
    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 a. a -> IO a
    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)
    forall (m :: * -> *) a b. Monad m => (a -> m b) -> [a] -> m [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 a. a -> IO a
    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 a. [a] -> 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 a. a -> HeistT IO IO a
    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 :: forall (n :: * -> *).
    MonadIO n =>
    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 a. IO a -> IO a
    forall (m :: * -> *) a. MonadIO m => IO a -> m a
    liftIO IO (Splice IO, CacheTagState)
    mkCacheTag
        let tag :: a
    tag = a
    "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)
    forall (m :: * -> *) a. Monad m => [m a] -> m [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)
    forall (m :: * -> *) a. Monad m => [m a] -> m [a]
    sequence [Either [String] TemplateRepo]
    erepos of
          Left [String]
    es -> Either [String] (HeistState n, CacheTagState)
    -> IO (Either [String] (HeistState n, CacheTagState))
    forall a. a -> IO a
    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 {a}. IsString a => a
    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 a. a -> IO a
    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 {a}. IsString a => a
    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 {a}. IsString a => a
    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 a. a -> IO a
    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 a b. (a -> b) -> Either [String] a -> Either [String] b
    forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
    fmap (,CacheTagState
    cts) Either [String] (HeistState n)
    hs