Skip to main content

nargo_template/renderers/
dejavu.rs

1#![warn(missing_docs)]
2
3use std::{collections::HashMap, path::Path};
4
5use async_trait::async_trait;
6use oak_core::Parser;
7use oak_dejavu::{DejavuLanguage, DejavuLexer, DejavuParser};
8
9use super::TemplateRenderer;
10
11/// DejaVu 模板渲染器
12pub struct DejaVuRenderer {
13    templates: HashMap<String, String>,
14    language: DejavuLanguage,
15}
16
17impl DejaVuRenderer {
18    /// 创建新的 DejaVu 渲染器
19    pub fn new() -> Self {
20        // 使用默认的 DejaVu 语言配置
21        let language = DejavuLanguage::default();
22
23        Self { templates: HashMap::new(), language }
24    }
25
26    /// 使用自定义模板配置创建 DejaVu 渲染器
27    pub fn with_template_config(template_config: oak_dejavu::language::TemplateConfig) -> Self {
28        let language = DejavuLanguage { syntax_mode: oak_dejavu::language::SyntaxMode::Template, template: template_config };
29
30        Self { templates: HashMap::new(), language }
31    }
32
33    /// 渲染模板内容
34    fn render_content(&self, content: &str, context: &serde_json::Value) -> crate::TemplateResult<String> {
35        // 使用真正的 DejaVu 解析器和分析器
36        let lexer = DejavuLexer::new(&self.language);
37        let parser = DejavuParser::new(&self.language);
38
39        // 创建解析会话
40        let mut session = oak_core::parser::ParseSession::<DejavuLanguage>::new(16);
41
42        // 解析模板
43        let _parse_result = parser.parse(content, &[], &mut session);
44
45        // 这里应该根据解析结果生成 AST,然后根据上下文渲染
46        // 由于 oak-dejavu 只是一个解析器,没有内置渲染功能
47        // 我们需要实现基本的渲染逻辑
48
49        // 暂时使用字符串替换作为简化实现
50        let mut result = content.to_string();
51
52        // 简单的变量替换,使用默认的定界词
53        if let serde_json::Value::Object(map) = context {
54            for (key, value) in map {
55                let placeholder = format!("{{{{ {} }}}}", key);
56                let value_str = match value {
57                    serde_json::Value::String(s) => s.clone(),
58                    serde_json::Value::Number(n) => n.to_string(),
59                    serde_json::Value::Bool(b) => b.to_string(),
60                    _ => value.to_string(),
61                };
62                result = result.replace(&placeholder, &value_str);
63            }
64        }
65
66        Ok(result)
67    }
68}
69
70impl Default for DejaVuRenderer {
71    fn default() -> Self {
72        Self::new()
73    }
74}
75
76#[async_trait]
77impl TemplateRenderer for DejaVuRenderer {
78    fn render(&self, template_name: &str, context: &serde_json::Value) -> crate::TemplateResult<String> {
79        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)))?;
80
81        self.render_content(template_content, context)
82    }
83
84    fn register_template(&mut self, name: &str, content: &str) -> crate::TemplateResult<()> {
85        self.templates.insert(name.to_string(), content.to_string());
86        Ok(())
87    }
88
89    fn register_template_file(&mut self, name: &str, path: &Path) -> crate::TemplateResult<()> {
90        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)))?;
91        self.register_template(name, &content)
92    }
93
94    fn register_templates_from_dir(&mut self, dir: &Path, extension: Option<&str>) -> crate::TemplateResult<()> {
95        let dir_path = dir;
96        let ext = extension.unwrap_or("dejavu");
97
98        for entry in walkdir::WalkDir::new(dir_path) {
99            let entry = entry?;
100            let path = entry.path();
101
102            if path.is_file() {
103                if let Some(file_ext) = path.extension() {
104                    if file_ext == ext || file_ext == "doki" {
105                        let relative_path = path.strip_prefix(dir_path).map_err(|e| std::io::Error::new(std::io::ErrorKind::Other, e))?;
106                        let template_name = relative_path.with_extension("").to_string_lossy().replace(std::path::MAIN_SEPARATOR, "/");
107
108                        self.register_template_file(&template_name, path)?;
109                    }
110                }
111            }
112        }
113
114        Ok(())
115    }
116}