1use crate::engine::TemplateEngine;
9use crate::tera_engine::TeraEngine;
10use doido_core::Result;
11use std::sync::{Arc, OnceLock};
12
13static ENGINE: OnceLock<Arc<dyn TemplateEngine>> = OnceLock::new();
14
15pub fn set_engine(engine: Arc<dyn TemplateEngine>) {
17 let _ = ENGINE.set(engine);
18}
19
20pub fn init(templates_dir: &str) -> Result<()> {
23 if ENGINE.get().is_some() {
24 return Ok(());
25 }
26 let engine = TeraEngine::new(templates_dir)?;
27 set_engine(Arc::new(engine));
28 Ok(())
29}
30
31pub fn try_engine() -> Option<Arc<dyn TemplateEngine>> {
33 ENGINE.get().cloned()
34}
35
36pub fn render(template: &str, context: &serde_json::Value) -> Result<String> {
39 let engine = ENGINE.get().ok_or_else(|| {
40 doido_core::anyhow::anyhow!(
41 "view engine not initialised; call doido_view::init(\"app/views\") at boot"
42 )
43 })?;
44 engine.render(template, context)
45}
46
47pub fn render_variant(
51 template: &str,
52 variant: &str,
53 context: &serde_json::Value,
54) -> Result<String> {
55 let engine = ENGINE.get().ok_or_else(|| {
56 doido_core::anyhow::anyhow!(
57 "view engine not initialised; call doido_view::init(\"app/views\") at boot"
58 )
59 })?;
60 engine.render_named(&format!("{template}.{variant}.tera"), context)
61}
62
63#[cfg(test)]
64mod tests {
65 use super::*;
66 use crate::engine::TemplateEngine;
67
68 struct StubEngine;
69 impl TemplateEngine for StubEngine {
70 fn render(&self, template: &str, _ctx: &serde_json::Value) -> Result<String> {
71 Ok(format!("stub:{template}"))
72 }
73 fn reload(&self) -> Result<()> {
74 Ok(())
75 }
76 }
77
78 #[test]
79 fn set_then_render_uses_installed_engine() {
80 set_engine(Arc::new(StubEngine));
81 assert_eq!(
82 render("posts/index", &serde_json::json!({})).unwrap(),
83 "stub:posts/index"
84 );
85 }
86}