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