summaryrefslogtreecommitdiff
path: root/src/Text/Hakyll/Internal/Template.hs
blob: bd9121b8f2f300f56d665806a904cf9ebb02dcbf (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
module Text.Hakyll.Internal.Template
    ( Template (..)
    , fromString
    , readTemplate
    , substitute
    , regularSubstitute
    , finalSubstitute
    ) where

import Control.Applicative ((<$>))
import Data.List (isPrefixOf)
import Data.Char (isAlphaNum)
import Data.Maybe (fromMaybe)
import System.FilePath ((</>))
import qualified Data.Map as M

import Text.Hakyll.Context (Context (..))
import Text.Hakyll.HakyllMonad (Hakyll)
import Text.Hakyll.Internal.Cache
import Text.Hakyll.Internal.Page
import Text.Hakyll.Internal.Template.Template
import Text.Hakyll.Internal.Template.Hamlet

-- | Construct a @Template@ from a string.
--
fromString :: String -> Template
fromString = Template . fromString'
  where
    fromString' [] = []
    fromString' string
        | "$$" `isPrefixOf` string =
            EscapeCharacter : (fromString' $ tail tail')
        | "$" `isPrefixOf` string =
            let (key, rest) = span isAlphaNum tail'
            in Identifier key : fromString' rest
        | otherwise =
            let (chunk, rest) = break (== '$') string
            in Chunk chunk : fromString' rest
      where
        tail' = tail string

-- | Read a @Template@ from a file. This function might fetch the @Template@
--   from the cache, if available.
readTemplate :: FilePath -> Hakyll Template
readTemplate path = do
    isCacheMoreRecent' <- isCacheMoreRecent fileName [path]
    if isCacheMoreRecent'
        then getFromCache fileName
        else do
            template <- if isHamletRTFile path
                            then readHamletTemplate
                            else readDefaultTemplate
            storeInCache template fileName
            return template
  where 
    fileName = "templates" </> path
    readDefaultTemplate = do
        page <- unContext <$> readPage path
        let body = fromMaybe (error $ "No body in template " ++ fileName)
                             (M.lookup "body" page)
        return $ fromString body

    readHamletTemplate = fromHamletRT <$> readHamletRT path

-- | Substitutes @$identifiers@ in the given @Template@ by values from the given
--   "Context". When a key is not found, it is left as it is. You can specify
--   the characters used to replace escaped dollars (@$$@) here.
substitute :: String -> Template -> Context -> String 
substitute escaper template context = substitute' =<< unTemplate template
  where
    substitute' (Chunk chunk) = chunk
    substitute' (Identifier key) =
        fromMaybe ('$' : key) $ M.lookup key $ unContext context
    substitute' (EscapeCharacter) = escaper

-- | @substitute@ for use during a chain. This will leave escaped characters as
--   they are.
regularSubstitute :: Template -> Context -> String
regularSubstitute = substitute "$$"

-- | @substitute@ for the end of a chain (just before writing). This renders
--   escaped characters.
finalSubstitute :: Template -> Context -> String
finalSubstitute = substitute "$"