use std::path::Path;
use anyhow::{Context, Result};
use minijinja::{AutoEscape, Environment};
const DEFAULT_PAGE: &str = include_str!("../../../examples/html_templates/functional/page.html");
const DEFAULT_HEADER: &str = include_str!("../../../examples/html_templates/theme/header.html");
const DEFAULT_FOOTER: &str = include_str!("../../../examples/html_templates/theme/footer.html");
const DEFAULT_CSS: &str = include_str!("../../../examples/html_templates/theme/theme.css");
const DEFAULT_README: &str = include_str!("../../../examples/html_templates/README.md");
pub struct SiteTemplates {
pub env: Environment<'static>,
pub css: String,
}
impl SiteTemplates {
pub fn render_page(&self, ctx: minijinja::Value) -> Result<String> {
let tmpl = self.env.get_template("page.html")?;
Ok(tmpl.render(ctx)?)
}
}
pub fn load(base: &Path) -> Result<SiteTemplates> {
let page = or_default(base, "functional/page.html", DEFAULT_PAGE);
let header = or_default(base, "theme/header.html", DEFAULT_HEADER);
let footer = or_default(base, "theme/footer.html", DEFAULT_FOOTER);
let css = or_default(base, "theme/theme.css", DEFAULT_CSS);
let mut env = Environment::new();
env.set_auto_escape_callback(|name| {
if name.ends_with(".html") {
AutoEscape::Html
} else {
AutoEscape::None
}
});
env.add_template_owned("page.html", page)?;
env.add_template_owned("header.html", header)?;
env.add_template_owned("footer.html", footer)?;
Ok(SiteTemplates { env, css })
}
pub fn eject(dir: &Path) -> Result<()> {
let functional = dir.join("functional");
let theme = dir.join("theme");
std::fs::create_dir_all(&functional).with_context(|| format!("create {}", functional.display()))?;
std::fs::create_dir_all(&theme).with_context(|| format!("create {}", theme.display()))?;
std::fs::write(functional.join("page.html"), DEFAULT_PAGE)?;
std::fs::write(theme.join("header.html"), DEFAULT_HEADER)?;
std::fs::write(theme.join("footer.html"), DEFAULT_FOOTER)?;
std::fs::write(theme.join("theme.css"), DEFAULT_CSS)?;
std::fs::write(dir.join("README.md"), DEFAULT_README)?;
Ok(())
}
fn or_default(base: &Path, rel: &str, default: &str) -> String {
let path = base.join(rel);
match std::fs::read_to_string(&path) {
Ok(s) => s,
Err(e) => {
if path.exists() {
tracing::warn!(
target: "inkhaven::html",
"html: template override `{}` unreadable ({e}) — using default",
path.display()
);
}
default.to_string()
}
}
}