nargo-template 0.0.1

Nargo template engine management
Documentation
#![warn(missing_docs)]

use std::{collections::HashMap, path::Path};

use async_trait::async_trait;
use oak_core::Parser;
use oak_dejavu::{DejavuLanguage, DejavuLexer, DejavuParser};

use super::TemplateRenderer;

/// DejaVu 模板渲染器
pub struct DejaVuRenderer {
    templates: HashMap<String, String>,
    language: DejavuLanguage,
}

impl DejaVuRenderer {
    /// 创建新的 DejaVu 渲染器
    pub fn new() -> Self {
        // 使用默认的 DejaVu 语言配置
        let language = DejavuLanguage::default();

        Self { templates: HashMap::new(), language }
    }

    /// 使用自定义模板配置创建 DejaVu 渲染器
    pub fn with_template_config(template_config: oak_dejavu::language::TemplateConfig) -> Self {
        let language = DejavuLanguage { syntax_mode: oak_dejavu::language::SyntaxMode::Template, template: template_config };

        Self { templates: HashMap::new(), language }
    }

    /// 渲染模板内容
    fn render_content(&self, content: &str, context: &serde_json::Value) -> crate::TemplateResult<String> {
        // 使用真正的 DejaVu 解析器和分析器
        let lexer = DejavuLexer::new(&self.language);
        let parser = DejavuParser::new(&self.language);

        // 创建解析会话
        let mut session = oak_core::parser::ParseSession::<DejavuLanguage>::new(16);

        // 解析模板
        let _parse_result = parser.parse(content, &[], &mut session);

        // 这里应该根据解析结果生成 AST,然后根据上下文渲染
        // 由于 oak-dejavu 只是一个解析器,没有内置渲染功能
        // 我们需要实现基本的渲染逻辑

        // 暂时使用字符串替换作为简化实现
        let mut result = content.to_string();

        // 简单的变量替换,使用默认的定界词
        if let serde_json::Value::Object(map) = context {
            for (key, value) in map {
                let placeholder = format!("{{{{ {} }}}}", key);
                let value_str = match value {
                    serde_json::Value::String(s) => s.clone(),
                    serde_json::Value::Number(n) => n.to_string(),
                    serde_json::Value::Bool(b) => b.to_string(),
                    _ => value.to_string(),
                };
                result = result.replace(&placeholder, &value_str);
            }
        }

        Ok(result)
    }
}

impl Default for DejaVuRenderer {
    fn default() -> Self {
        Self::new()
    }
}

#[async_trait]
impl TemplateRenderer for DejaVuRenderer {
    fn render(&self, template_name: &str, context: &serde_json::Value) -> crate::TemplateResult<String> {
        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)))?;

        self.render_content(template_content, context)
    }

    fn register_template(&mut self, name: &str, content: &str) -> crate::TemplateResult<()> {
        self.templates.insert(name.to_string(), content.to_string());
        Ok(())
    }

    fn register_template_file(&mut self, name: &str, path: &Path) -> crate::TemplateResult<()> {
        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)))?;
        self.register_template(name, &content)
    }

    fn register_templates_from_dir(&mut self, dir: &Path, extension: Option<&str>) -> crate::TemplateResult<()> {
        let dir_path = dir;
        let ext = extension.unwrap_or("dejavu");

        for entry in walkdir::WalkDir::new(dir_path) {
            let entry = entry?;
            let path = entry.path();

            if path.is_file() {
                if let Some(file_ext) = path.extension() {
                    if file_ext == ext || file_ext == "doki" {
                        let relative_path = path.strip_prefix(dir_path).map_err(|e| std::io::Error::new(std::io::ErrorKind::Other, e))?;
                        let template_name = relative_path.with_extension("").to_string_lossy().replace(std::path::MAIN_SEPARATOR, "/");

                        self.register_template_file(&template_name, path)?;
                    }
                }
            }
        }

        Ok(())
    }
}