🐙 Templated web page generator for your git repositories
git clone https://github.com/m-col/gitja
Files | Refs | Readme | License

-rw-r--r-- src/Templates.hs


      1 {-# LANGUAGE LambdaCase #-}
      2 {-# LANGUAGE OverloadedStrings #-}
      3 
      4 module Templates (
      5     Template (..),
      6     loadTemplate,
      7     render,
      8 ) where
      9 
     10 import Data.IORef (modifyIORef', newIORef, readIORef)
     11 import qualified Data.Text as T
     12 import qualified Data.Text.Lazy as TL
     13 import qualified Data.Text.Lazy.Builder as TB
     14 import Path (Abs, File, Path, Rel, filename, toFilePath)
     15 import System.IO.Error (tryIOError)
     16 import qualified Text.Ginger.AST as G
     17 import Text.Ginger.GVal (GVal)
     18 import Text.Ginger.Html (Html, htmlSource)
     19 import Text.Ginger.Parse (ParserError (..), SourcePos, parseGingerFile)
     20 import Text.Ginger.Run
     21 
     22 import Types
     23 
     24 data Template = Template
     25     { templatePath :: Path Rel File
     26     , templateGinger :: G.Template SourcePos
     27     }
     28 
     29 {-
     30 This tries to load a `Template` from the given file path.
     31 -}
     32 loadTemplate :: Path Abs File -> IO (Maybe Template)
     33 loadTemplate path =
     34     parseGingerFile includeResolver (toFilePath path) >>= \case
     35         Right parsed -> return . Just . Template (filename path) $ parsed
     36         Left err -> do
     37             informError (toFilePath path) err
     38             return Nothing
     39   where
     40     -- An attempt at pretty printing the error message.
     41     informError p (ParserError msg Nothing) =
     42         putStr $ "Template error: " <> p <> "\n" <> indent msg
     43     informError p (ParserError msg (Just pos)) =
     44         putStrLn $ "Template error: " <> p <> "\n" <> indent (show pos <> "\n" <> msg)
     45     indent = unlines . map (mappend "    ") . lines
     46 
     47     -- This resolves template 'includes'.
     48     includeResolver :: FilePath -> IO (Maybe String)
     49     includeResolver p = either (const Nothing) Just <$> tryIOError (readFile p)
     50 
     51 {-
     52 This generator function uses Ginger to render templates into Text, using a provided
     53 lookup function to request data from calling code.
     54 -}
     55 render ::
     56     (T.Text -> RunRepo (GVal RunRepo)) ->
     57     Template ->
     58     IO TL.Text
     59 render scopeLookup template = do
     60     ioref <- newIORef . TB.fromText $ ""
     61 
     62     let emit :: Html -> IO ()
     63         emit = modifyIORef' ioref . flip mappend . TB.fromText . htmlSource
     64 
     65     runGingerT
     66         (makeContextHtmlM scopeLookup emit)
     67         (templateGinger template)
     68 
     69     TB.toLazyText <$> readIORef ioref
     70