-- | Simple templating using HTML5 as the template language. Templates are
-- specified by adding special attributes to tags. During substitution, these
-- attributes are stripped from the HTML. The following attributes are
-- recognized:
--
-- * @insert="identifier"@ - replace the tag's contents with the value
-- bound to @identifier@ in the substitution context.
-- * @replace="identifier"@ - replace the whole tag and its contents with
-- the value bound to @identifier@ in the substitution context.
-- * @when="identifier"@ - only render this tag if @identifier@ is set to
-- true in the substitution context.
-- * @unless="identifier"@ - the dual of @when@; only render this tag if
-- @identifier@ is set to false in the substitution context.
-- * @forall="identifier"@ - render this tag and its contents once for each
-- element in the list bound to @identifier@ in the substitution context.
-- The contents of the element may refer to the current iteration's value
-- of @identifier@ by that same name.
--
-- Substitution can also be performed on the attributes of tags. The
-- following attribute substitutions are recognized:
--
-- * @when:identifier:attr="value"@ - only include @attr@ is @identifier@
-- is set to true in the substitution context.
-- * @unless:identifier:attr="value"@ - only include @attr@ is @identifier@
-- is set to false in the substitution context.
-- * @insert:identifier:attr="value"@ - overwrite the value of @attr@ with
-- whatever @identifier@ is bound to in the substitution context.
--
-- Contexts can be nested, in which case nested keys are separated by
-- periods, as in @parent.child.grandchild@. Keys may be prefixed with a
-- question mark, in which case they are considered to be "weak keys".
-- If a weak key does not exist in the context, it will be replaced by a
-- sensible default value instead of causing an error. The defaults for the
-- different value types are as follows:
--
-- * bool: false
-- * string: ""
-- * array: []
-- * object: {}
--
-- As numbers are treated just like strings, they have an empty string as
-- their default value as well.
--
-- In general, values used as text must be declared text by the context and
-- so on, but the following coercions are permitted:
--
-- * bool to string
-- * array to bool
--
-- Coercion of array to bool, with the empty list being considered false and
-- all other list considered true, is permitted to allow templates to take
-- special action in the case of an empty list.
--
-- Contexts may be constructed programatically using the provided
-- combinators, converted from JSON objects or lists of key-value pairs, or
-- parsed from a YAML-formatted string using 'parseContext'.
module Text.Domplate (
Text, Monoid, Template, Context, Value (..), Key,
parseTemplate, replace,
add, remove, fromList, Text.Domplate.Context.lookup, empty, size, (<>),
parseContext,
compile
) where
import Control.Applicative hiding (empty)
import Data.Monoid
import Data.Text (Text)
import Data.Yaml (Value (..))
import Text.Domplate.Context
import Text.Domplate.Replace
import qualified Data.ByteString as BS (readFile, writeFile)
-- | Compile a template using a context parsed from a context file.
-- Throws an error if context parsing or substitution fails.
compile :: FilePath -- ^ Template file.
-> FilePath -- ^ Context file.
-> FilePath -- ^ Output file.
-> IO ()
compile template context outfile = do
t <- parseTemplate <$> BS.readFile template
ec <- parseContext <$> BS.readFile context
case fmapL show ec >>= replace t of
Right s -> BS.writeFile outfile s
Left e -> error e
fmapL :: (a -> b) -> Either a c -> Either b c
fmapL f (Left x) = Left (f x)
fmapL _ (Right x) = Right x