module Text.Mustache.Compile where
import Control.Applicative
import Control.Monad
import Control.Monad.Except
import Control.Monad.Trans.Either
import Data.Bool
import Data.Foldable (fold)
import Data.List
import Data.Monoid
import Data.Text hiding (concat, find, map, uncons)
import qualified Data.Text.IO as TIO
import System.Directory
import System.FilePath
import Text.Mustache.Types
import Text.Mustache.Parser
import Text.Parsec.Error
import Text.Parsec.Pos
import Text.Printf
compileTemplate ∷ [FilePath] → FilePath → IO (Either ParseError MustacheTemplate)
compileTemplate searchSpace = compileTemplateWithCache searchSpace mempty
compileTemplateWithCache ∷ [FilePath] → [MustacheTemplate] → FilePath → IO (Either ParseError MustacheTemplate)
compileTemplateWithCache searchSpace = (runEitherT .) . compile'
where
compile' templates name' =
case find ((== name') . name) templates of
Just template → return template
Nothing → do
rawSource ← getFile searchSpace name'
compiled@(MustacheTemplate { ast = mast }) ←
hoistEither $ parseTemplate name' rawSource
foldM
(\st@(MustacheTemplate { partials = p }) partialName →
compile' (p <> templates) partialName >>=
\nt → return (st { partials = nt : p })
)
compiled
(getPartials mast)
parseTemplate ∷ String → Text → Either ParseError MustacheTemplate
parseTemplate name' = fmap (flip (MustacheTemplate name') mempty) . parse name'
getPartials ∷ MustacheAST → [FilePath]
getPartials = join . fmap getPartials'
getPartials' ∷ MustacheNode Text → [FilePath]
getPartials' (MustachePartial p) = return p
getPartials' (MustacheSection _ n) = getPartials n
getPartials' _ = mempty
getFile ∷ [FilePath] → FilePath → EitherT ParseError IO Text
getFile [] fp = throwError $ fileNotFound fp
getFile (templateDir : xs) fp =
lift (doesFileExist filePath) >>=
bool
(getFile xs fp)
(lift $ TIO.readFile filePath)
where
filePath = templateDir </> fp
fileNotFound ∷ FilePath → ParseError
fileNotFound fp = newErrorMessage (Message $ printf "Template file '%s' not found" fp) (initialPos fp)