A small, fast, static site generator
1use anyhow::Result;
2use minijinja::value::Value;
3use serde::Serialize;
4use std::{
5 fs,
6 path::{Path, PathBuf},
7};
8use walkdir::WalkDir;
9
10use crate::{
11 config::Config,
12 markdown::{FrontMatter, Page},
13};
14
15#[derive(Serialize)]
16pub struct TemplateContext<'a> {
17 pub pages: &'a [Page],
18 pub meta: &'a FrontMatter,
19 pub content: &'a String,
20 pub path: &'a PathBuf,
21}
22
23impl<'a> TemplateContext<'a> {
24 /// Create a new `TemplateContext` object given a page list
25 /// and current page.
26 pub fn new(pages: &'a Vec<Page>, page: &'a Page) -> Self {
27 Self {
28 pages,
29 meta: &page.meta,
30 content: &page.content,
31 path: &page.rel_path,
32 }
33 }
34}
35
36pub struct TemplateEnvironment<'a> {
37 env: minijinja::Environment<'a>,
38}
39
40impl<'a> TemplateEnvironment<'a> {
41 /// Returns a new, empty, template environment.
42 pub fn new() -> Self {
43 Self {
44 env: minijinja::Environment::new(),
45 }
46 }
47
48 /// Load all templates from the templates directory.
49 ///
50 /// This function succeeds if all templates are valid, or if the `templates`
51 /// directory doesn't exist.
52 pub fn load_templates(&mut self, config: &Config) -> Result<()> {
53 let tmpl_root = Path::new(&config.build.template_dir);
54
55 // If the templates directory doesn't exist, don't try to load anything.
56 if !tmpl_root.is_dir() {
57 return Ok(());
58 }
59
60 for entry in WalkDir::new(tmpl_root) {
61 let entry = entry?;
62 let path = entry.path();
63
64 // Get the path inside `templates`
65 let name = path.strip_prefix(tmpl_root)?.to_string_lossy().into_owned();
66
67 if path.is_file() {
68 let tmpl_str = fs::read_to_string(path)?;
69
70 self.env.add_template_owned(name.clone(), tmpl_str)?;
71
72 println!("Loaded template {name}");
73 }
74 }
75
76 self.env
77 .add_global("site", Value::from_serialize(&config.site));
78 self.env
79 .add_global("extra", Value::from_serialize(&config.extra));
80
81 Ok(())
82 }
83
84 /// Render a template given context and name.
85 pub fn render_template(&self, context: &TemplateContext, tmpl_name: &str) -> Result<String> {
86 let tmpl = self.env.get_template(tmpl_name)?;
87 let render_str = tmpl.render(context)?;
88 Ok(render_str)
89 }
90}