nargo_template/renderers/
jinja.rs1#![warn(missing_docs)]
2
3use std::{collections::HashMap, path::Path};
4
5use async_trait::async_trait;
6use oak_core::Parser;
7use oak_jinja::{JinjaLanguage, JinjaLexer, JinjaParser};
8
9use super::TemplateRenderer;
10
11pub struct Jinja2Renderer {
13 templates: HashMap<String, String>,
14 language: JinjaLanguage,
15}
16
17impl Jinja2Renderer {
18 pub fn new() -> Self {
20 Self { templates: HashMap::new(), language: JinjaLanguage::default() }
21 }
22
23 fn render_content(&self, content: &str, context: &serde_json::Value) -> crate::TemplateResult<String> {
25 let _lexer = JinjaLexer::new(&self.language);
28 let parser = JinjaParser::new(&self.language);
29
30 let mut session = oak_core::parser::ParseSession::<JinjaLanguage>::new(16);
32
33 let _output = parser.parse(content, &[], &mut session);
35
36 let mut result = content.to_string();
39
40 if let serde_json::Value::Object(map) = context {
42 for (key, value) in map {
43 let placeholder = format!("{{{{ {} }}}}", key);
44 let value_str = match value {
45 serde_json::Value::String(s) => s.clone(),
46 serde_json::Value::Number(n) => n.to_string(),
47 serde_json::Value::Bool(b) => b.to_string(),
48 _ => value.to_string(),
49 };
50 result = result.replace(&placeholder, &value_str);
51 }
52 }
53
54 Ok(result)
55 }
56}
57
58impl Default for Jinja2Renderer {
59 fn default() -> Self {
60 Self::new()
61 }
62}
63
64#[async_trait]
65impl TemplateRenderer for Jinja2Renderer {
66 fn render(&self, template_name: &str, context: &serde_json::Value) -> crate::TemplateResult<String> {
67 let template_content = self.templates.get(template_name).ok_or_else(|| std::io::Error::new(std::io::ErrorKind::NotFound, format!("Template '{}' not found", template_name)))?;
68
69 self.render_content(template_content, context)
70 }
71
72 fn register_template(&mut self, name: &str, content: &str) -> crate::TemplateResult<()> {
73 self.templates.insert(name.to_string(), content.to_string());
74 Ok(())
75 }
76
77 fn register_template_file(&mut self, name: &str, path: &Path) -> crate::TemplateResult<()> {
78 let content = std::fs::read_to_string(path).map_err(|e| std::io::Error::new(std::io::ErrorKind::Other, format!("Failed to read template file '{}': {}", path.display(), e)))?;
79 self.register_template(name, &content)
80 }
81
82 fn register_templates_from_dir(&mut self, dir: &Path, extension: Option<&str>) -> crate::TemplateResult<()> {
83 let dir_path = dir;
84 let ext = extension.unwrap_or("jinja2");
85
86 for entry in walkdir::WalkDir::new(dir_path) {
87 let entry = entry?;
88 let path = entry.path();
89
90 if path.is_file() {
91 if let Some(file_ext) = path.extension() {
92 if file_ext == ext {
93 let relative_path = path.strip_prefix(dir_path).map_err(|e| std::io::Error::new(std::io::ErrorKind::Other, e))?;
94 let template_name = relative_path.with_extension("").to_string_lossy().replace(std::path::MAIN_SEPARATOR, "/");
95
96 self.register_template_file(&template_name, path)?;
97 }
98 }
99 }
100 }
101
102 Ok(())
103 }
104}