Skip to main content

nargo_template/
lib.rs

1#![warn(missing_docs)]
2
3use std::{
4    collections::HashMap,
5    path::{Path, PathBuf},
6    result::Result,
7};
8
9/// 模板引擎结果类型
10pub type TemplateResult<T> = Result<T, std::io::Error>;
11
12use async_trait::async_trait;
13use handlebars::Handlebars;
14use oak_core::Parser;
15use oak_dejavu::{DejavuLanguage, DejavuLexer, DejavuParser};
16use oak_jinja::{JinjaLanguage, JinjaLexer, JinjaParser};
17use serde::Serialize;
18use walkdir::WalkDir;
19
20// 可选依赖
21#[cfg(feature = "askama")]
22use askama::Template;
23
24#[cfg(feature = "tera")]
25use tera::{Context, Tera};
26
27#[cfg(feature = "liquid")]
28use liquid::{Object, ParserBuilder, Value};
29
30#[cfg(feature = "tinytemplate")]
31use tinytemplate::TinyTemplate;
32
33/// 模板引擎类型
34#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize)]
35pub enum TemplateEngine {
36    /// Handlebars 模板引擎
37    Handlebars,
38    /// DejaVu 模板引擎
39    DejaVu,
40    /// Askama 模板引擎
41    Askama,
42    /// Jinja2 模板引擎
43    Jinja2,
44    /// Tera 模板引擎
45    Tera,
46    /// Liquid 模板引擎
47    Liquid,
48    /// TinyTemplate 模板引擎
49    TinyTemplate,
50    /// 自定义模板引擎
51    Custom,
52}
53
54impl TemplateEngine {
55    /// 获取所有可用的模板引擎
56    pub fn all() -> &'static [Self] {
57        &[Self::Handlebars, Self::DejaVu, Self::Askama, Self::Jinja2, Self::Tera, Self::Liquid, Self::TinyTemplate, Self::Custom]
58    }
59
60    /// 获取模板引擎的显示名称
61    pub fn display_name(&self) -> &'static str {
62        match self {
63            Self::Handlebars => "Handlebars",
64            Self::DejaVu => "DejaVu",
65            Self::Askama => "Askama",
66            Self::Jinja2 => "Jinja2",
67            Self::Tera => "Tera",
68            Self::Liquid => "Liquid",
69            Self::TinyTemplate => "TinyTemplate",
70            Self::Custom => "自定义",
71        }
72    }
73
74    /// 获取模板引擎的描述
75    pub fn description(&self) -> &'static str {
76        match self {
77            Self::Handlebars => "Handlebars 模板引擎,流行的 JavaScript 模板语言",
78            Self::DejaVu => "DejaVu 模板引擎,编译期零成本抽象",
79            Self::Askama => "Askama 模板引擎,基于 Rust 的类型安全模板",
80            Self::Jinja2 => "Jinja2 模板引擎,Python 生态系统中流行的模板语言",
81            Self::Tera => "Tera 模板引擎,受 Jinja2 启发的 Rust 模板引擎",
82            Self::Liquid => "Liquid 模板引擎,Shopify 开发的模板语言",
83            Self::TinyTemplate => "TinyTemplate 模板引擎,轻量级的 Rust 模板引擎",
84            Self::Custom => "自定义模板引擎",
85        }
86    }
87
88    /// 获取默认的文件扩展名
89    pub fn default_extension(&self) -> &'static str {
90        match self {
91            Self::Handlebars => "hbs",
92            Self::DejaVu => "dejavu",
93            Self::Askama => "html",
94            Self::Jinja2 => "jinja2",
95            Self::Tera => "tera",
96            Self::Liquid => "liquid",
97            Self::TinyTemplate => "tt",
98            Self::Custom => "tpl",
99        }
100    }
101}
102
103/// 模板上下文 trait
104/// 定义模板数据的统一接口
105pub trait TemplateContext: std::fmt::Debug + Clone + std::any::Any {
106    /// 转换为 Any 类型,用于类型检查
107    fn as_any(&self) -> &dyn std::any::Any;
108}
109
110/// 模板渲染器 trait
111#[async_trait]
112pub trait TemplateRenderer: Send + Sync {
113    /// 渲染单个模板
114    fn render(&self, template_name: &str, context: &serde_json::Value) -> TemplateResult<String>;
115
116    /// 异步渲染单个模板
117    async fn render_async(&self, template_name: &str, context: &serde_json::Value) -> TemplateResult<String> {
118        Ok(self.render(template_name, context)?)
119    }
120
121    /// 注册模板
122    fn register_template(&mut self, name: &str, content: &str) -> TemplateResult<()>;
123
124    /// 注册模板文件
125    fn register_template_file(&mut self, name: &str, path: &Path) -> TemplateResult<()>;
126
127    /// 从目录注册所有模板
128    fn register_templates_from_dir(&mut self, dir: &Path, extension: Option<&str>) -> TemplateResult<()>;
129}
130
131/// Handlebars 模板渲染器
132pub struct HandlebarsRenderer {
133    handlebars: Handlebars<'static>,
134}
135
136impl HandlebarsRenderer {
137    /// 创建新的 Handlebars 渲染器
138    pub fn new() -> Self {
139        let mut handlebars = Handlebars::new();
140        handlebars.set_strict_mode(false);
141        Self { handlebars }
142    }
143
144    /// 获取底层的 Handlebars 实例
145    pub fn inner(&self) -> &Handlebars<'static> {
146        &self.handlebars
147    }
148
149    /// 获取可变的底层 Handlebars 实例
150    pub fn inner_mut(&mut self) -> &mut Handlebars<'static> {
151        &mut self.handlebars
152    }
153}
154
155impl Default for HandlebarsRenderer {
156    fn default() -> Self {
157        Self::new()
158    }
159}
160
161#[async_trait]
162impl TemplateRenderer for HandlebarsRenderer {
163    fn render(&self, template_name: &str, context: &serde_json::Value) -> TemplateResult<String> {
164        self.handlebars.render(template_name, context).map_err(|e| std::io::Error::new(std::io::ErrorKind::Other, format!("Failed to render template '{}': {}", template_name, e)))
165    }
166
167    fn register_template(&mut self, name: &str, content: &str) -> TemplateResult<()> {
168        self.handlebars.register_template_string(name, content).map_err(|e| std::io::Error::new(std::io::ErrorKind::Other, format!("Failed to register template '{}': {}", name, e)))?;
169        Ok(())
170    }
171
172    fn register_template_file(&mut self, name: &str, path: &Path) -> TemplateResult<()> {
173        self.handlebars.register_template_file(name, path).map_err(|e| std::io::Error::new(std::io::ErrorKind::Other, format!("Failed to register template file '{}': {}", path.display(), e)))?;
174        Ok(())
175    }
176
177    fn register_templates_from_dir(&mut self, dir: &Path, extension: Option<&str>) -> TemplateResult<()> {
178        let dir_path = dir;
179        let ext = extension.unwrap_or("hbs");
180
181        for entry in WalkDir::new(dir_path) {
182            let entry = entry?;
183            let path = entry.path();
184
185            if path.is_file() {
186                if let Some(file_ext) = path.extension() {
187                    if file_ext == ext {
188                        let relative_path = path.strip_prefix(dir_path).map_err(|e| std::io::Error::new(std::io::ErrorKind::Other, e))?;
189                        let template_name = relative_path.with_extension("").to_string_lossy().replace(std::path::MAIN_SEPARATOR, "/");
190
191                        self.register_template_file(&template_name, path)?;
192                    }
193                }
194            }
195        }
196
197        Ok(())
198    }
199}
200
201/// Jinja2 模板渲染器
202pub struct Jinja2Renderer {
203    templates: HashMap<String, String>,
204    language: JinjaLanguage,
205}
206
207impl Jinja2Renderer {
208    /// 创建新的 Jinja2 渲染器
209    pub fn new() -> Self {
210        Self { templates: HashMap::new(), language: JinjaLanguage::default() }
211    }
212
213    /// 渲染模板内容
214    fn render_content(&self, content: &str, context: &serde_json::Value) -> TemplateResult<String> {
215        // 这里使用 oak-jinja 解析模板,然后手动实现渲染逻辑
216        // 由于 oak-jinja 只是一个解析器,没有内置渲染功能
217        let _lexer = JinjaLexer::new(&self.language);
218        let parser = JinjaParser::new(&self.language);
219
220        // 创建解析会话
221        let mut session = oak_core::parser::ParseSession::<JinjaLanguage>::new(16);
222
223        // 解析模板
224        let _output = parser.parse(content, &[], &mut session);
225
226        // 简单实现:直接返回模板内容,替换变量
227        // 注意:这只是一个临时实现,需要根据 oak-jinja 的 AST 结构实现完整的渲染逻辑
228        let mut result = content.to_string();
229
230        // 简单的变量替换
231        if let serde_json::Value::Object(map) = context {
232            for (key, value) in map {
233                let placeholder = format!("{{{{ {} }}}}", key);
234                let value_str = match value {
235                    serde_json::Value::String(s) => s.clone(),
236                    serde_json::Value::Number(n) => n.to_string(),
237                    serde_json::Value::Bool(b) => b.to_string(),
238                    _ => value.to_string(),
239                };
240                result = result.replace(&placeholder, &value_str);
241            }
242        }
243
244        Ok(result)
245    }
246}
247
248impl Default for Jinja2Renderer {
249    fn default() -> Self {
250        Self::new()
251    }
252}
253
254#[async_trait]
255impl TemplateRenderer for Jinja2Renderer {
256    fn render(&self, template_name: &str, context: &serde_json::Value) -> TemplateResult<String> {
257        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)))?;
258
259        self.render_content(template_content, context)
260    }
261
262    fn register_template(&mut self, name: &str, content: &str) -> TemplateResult<()> {
263        self.templates.insert(name.to_string(), content.to_string());
264        Ok(())
265    }
266
267    fn register_template_file(&mut self, name: &str, path: &Path) -> TemplateResult<()> {
268        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)))?;
269        self.register_template(name, &content)
270    }
271
272    fn register_templates_from_dir(&mut self, dir: &Path, extension: Option<&str>) -> TemplateResult<()> {
273        let dir_path = dir;
274        let ext = extension.unwrap_or("jinja2");
275
276        for entry in WalkDir::new(dir_path) {
277            let entry = entry?;
278            let path = entry.path();
279
280            if path.is_file() {
281                if let Some(file_ext) = path.extension() {
282                    if file_ext == ext {
283                        let relative_path = path.strip_prefix(dir_path).map_err(|e| std::io::Error::new(std::io::ErrorKind::Other, e))?;
284                        let template_name = relative_path.with_extension("").to_string_lossy().replace(std::path::MAIN_SEPARATOR, "/");
285
286                        self.register_template_file(&template_name, path)?;
287                    }
288                }
289            }
290        }
291
292        Ok(())
293    }
294}
295
296/// DejaVu 模板渲染器
297pub struct DejaVuRenderer {
298    templates: HashMap<String, String>,
299    language: DejavuLanguage,
300}
301
302impl DejaVuRenderer {
303    /// 创建新的 DejaVu 渲染器
304    pub fn new() -> Self {
305        // 使用默认的 DejaVu 语言配置
306        let language = DejavuLanguage::default();
307
308        Self { templates: HashMap::new(), language }
309    }
310
311    /// 使用自定义模板配置创建 DejaVu 渲染器
312    pub fn with_template_config(template_config: oak_dejavu::language::TemplateConfig) -> Self {
313        let language = DejavuLanguage { syntax_mode: oak_dejavu::language::SyntaxMode::Template, template: template_config };
314
315        Self { templates: HashMap::new(), language }
316    }
317
318    /// 渲染模板内容
319    fn render_content(&self, content: &str, context: &serde_json::Value) -> TemplateResult<String> {
320        // 使用真正的 DejaVu 解析器和分析器
321        let lexer = DejavuLexer::new(&self.language);
322        let parser = DejavuParser::new(&self.language);
323
324        // 创建解析会话
325        let mut session = oak_core::parser::ParseSession::<DejavuLanguage>::new(16);
326
327        // 解析模板
328        let _parse_result = parser.parse(content, &[], &mut session);
329
330        // 这里应该根据解析结果生成 AST,然后根据上下文渲染
331        // 由于 oak-dejavu 只是一个解析器,没有内置渲染功能
332        // 我们需要实现基本的渲染逻辑
333
334        // 暂时使用字符串替换作为简化实现
335        let mut result = content.to_string();
336
337        // 简单的变量替换,使用默认的定界词
338        if let serde_json::Value::Object(map) = context {
339            for (key, value) in map {
340                let placeholder = format!("{{{{ {} }}}}", key);
341                let value_str = match value {
342                    serde_json::Value::String(s) => s.clone(),
343                    serde_json::Value::Number(n) => n.to_string(),
344                    serde_json::Value::Bool(b) => b.to_string(),
345                    _ => value.to_string(),
346                };
347                result = result.replace(&placeholder, &value_str);
348            }
349        }
350
351        Ok(result)
352    }
353}
354
355impl Default for DejaVuRenderer {
356    fn default() -> Self {
357        Self::new()
358    }
359}
360
361#[async_trait]
362impl TemplateRenderer for DejaVuRenderer {
363    fn render(&self, template_name: &str, context: &serde_json::Value) -> TemplateResult<String> {
364        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)))?;
365
366        self.render_content(template_content, context)
367    }
368
369    fn register_template(&mut self, name: &str, content: &str) -> TemplateResult<()> {
370        self.templates.insert(name.to_string(), content.to_string());
371        Ok(())
372    }
373
374    fn register_template_file(&mut self, name: &str, path: &Path) -> TemplateResult<()> {
375        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)))?;
376        self.register_template(name, &content)
377    }
378
379    fn register_templates_from_dir(&mut self, dir: &Path, extension: Option<&str>) -> TemplateResult<()> {
380        let dir_path = dir;
381        let ext = extension.unwrap_or("dejavu");
382
383        for entry in WalkDir::new(dir_path) {
384            let entry = entry?;
385            let path = entry.path();
386
387            if path.is_file() {
388                if let Some(file_ext) = path.extension() {
389                    if file_ext == ext || file_ext == "doki" {
390                        let relative_path = path.strip_prefix(dir_path).map_err(|e| std::io::Error::new(std::io::ErrorKind::Other, e))?;
391                        let template_name = relative_path.with_extension("").to_string_lossy().replace(std::path::MAIN_SEPARATOR, "/");
392
393                        self.register_template_file(&template_name, path)?;
394                    }
395                }
396            }
397        }
398
399        Ok(())
400    }
401}
402
403/// Tera 模板渲染器
404#[cfg(feature = "tera")]
405pub struct TeraRenderer {
406    tera: Tera,
407}
408
409#[cfg(feature = "tera")]
410impl TeraRenderer {
411    /// 创建新的 Tera 渲染器
412    pub fn new() -> Self {
413        let tera = Tera::default();
414        Self { tera }
415    }
416
417    /// 获取底层的 Tera 实例
418    pub fn inner(&self) -> &Tera {
419        &self.tera
420    }
421
422    /// 获取可变的底层 Tera 实例
423    pub fn inner_mut(&mut self) -> &mut Tera {
424        &mut self.tera
425    }
426}
427
428#[cfg(feature = "tera")]
429impl Default for TeraRenderer {
430    fn default() -> Self {
431        Self::new()
432    }
433}
434
435#[cfg(feature = "tera")]
436#[async_trait]
437impl TemplateRenderer for TeraRenderer {
438    fn render(&self, template_name: &str, context: &serde_json::Value) -> TemplateResult<String> {
439        let mut tera_context = Context::new();
440
441        // 转换 serde_json::Value 到 Tera 的 Context
442        if let serde_json::Value::Object(map) = context {
443            for (key, value) in map {
444                match value {
445                    serde_json::Value::String(s) => tera_context.insert(key, s),
446                    serde_json::Value::Number(n) => {
447                        if let Some(i) = n.as_i64() {
448                            tera_context.insert(key, &i);
449                        }
450                        else if let Some(f) = n.as_f64() {
451                            tera_context.insert(key, &f);
452                        }
453                    }
454                    serde_json::Value::Bool(b) => tera_context.insert(key, b),
455                    serde_json::Value::Array(arr) => {
456                        let mut vec = Vec::new();
457                        for item in arr {
458                            vec.push(item.clone());
459                        }
460                        tera_context.insert(key, &vec);
461                    }
462                    serde_json::Value::Object(obj) => {
463                        let mut map = std::collections::HashMap::new();
464                        for (k, v) in obj {
465                            map.insert(k.clone(), v.clone());
466                        }
467                        tera_context.insert(key, &map);
468                    }
469                    _ => {}
470                }
471            }
472        }
473
474        self.tera.render(template_name, &tera_context).map_err(|e| std::io::Error::new(std::io::ErrorKind::Other, format!("Failed to render template '{}': {}", template_name, e)))
475    }
476
477    fn register_template(&mut self, name: &str, content: &str) -> TemplateResult<()> {
478        self.tera.add_raw_template(name, content).map_err(|e| std::io::Error::new(std::io::ErrorKind::Other, format!("Failed to register template '{}': {}", name, e)))?;
479        Ok(())
480    }
481
482    fn register_template_file(&mut self, name: &str, path: &Path) -> TemplateResult<()> {
483        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)))?;
484        self.register_template(name, &content)
485    }
486
487    fn register_templates_from_dir(&mut self, dir: &Path, extension: Option<&str>) -> TemplateResult<()> {
488        let dir_path = dir;
489        let ext = extension.unwrap_or("tera");
490
491        for entry in WalkDir::new(dir_path) {
492            let entry = entry?;
493            let path = entry.path();
494
495            if path.is_file() {
496                if let Some(file_ext) = path.extension() {
497                    if file_ext == ext {
498                        let relative_path = path.strip_prefix(dir_path).map_err(|e| std::io::Error::new(std::io::ErrorKind::Other, e))?;
499                        let template_name = relative_path.with_extension("").to_string_lossy().replace(std::path::MAIN_SEPARATOR, "/");
500
501                        self.register_template_file(&template_name, path)?;
502                    }
503                }
504            }
505        }
506
507        Ok(())
508    }
509}
510
511/// Liquid 模板渲染器
512#[cfg(feature = "liquid")]
513pub struct LiquidRenderer {
514    parser: liquid::Parser,
515    templates: HashMap<String, String>,
516}
517
518#[cfg(feature = "liquid")]
519impl LiquidRenderer {
520    /// 创建新的 Liquid 渲染器
521    pub fn new() -> Self {
522        let parser = ParserBuilder::with_stdlib().build().unwrap();
523        Self { parser, templates: HashMap::new() }
524    }
525
526    /// 获取底层的 Liquid 解析器
527    pub fn inner(&self) -> &liquid::Parser {
528        &self.parser
529    }
530}
531
532#[cfg(feature = "liquid")]
533impl Default for LiquidRenderer {
534    fn default() -> Self {
535        Self::new()
536    }
537}
538
539#[cfg(feature = "liquid")]
540#[async_trait]
541impl TemplateRenderer for LiquidRenderer {
542    fn render(&self, template_name: &str, context: &serde_json::Value) -> TemplateResult<String> {
543        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)))?;
544
545        let template = self.parser.parse(template_content).map_err(|e| std::io::Error::new(std::io::ErrorKind::Other, format!("Failed to parse template '{}': {}", template_name, e)))?;
546
547        let liquid_context = self.convert_to_liquid_context(context);
548
549        template.render(&liquid_context).map_err(|e| std::io::Error::new(std::io::ErrorKind::Other, format!("Failed to render template '{}': {}", template_name, e)))
550    }
551
552    fn register_template(&mut self, name: &str, content: &str) -> TemplateResult<()> {
553        self.templates.insert(name.to_string(), content.to_string());
554        Ok(())
555    }
556
557    fn register_template_file(&mut self, name: &str, path: &Path) -> TemplateResult<()> {
558        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)))?;
559        self.register_template(name, &content)
560    }
561
562    fn register_templates_from_dir(&mut self, dir: &Path, extension: Option<&str>) -> TemplateResult<()> {
563        let dir_path = dir;
564        let ext = extension.unwrap_or("liquid");
565
566        for entry in WalkDir::new(dir_path) {
567            let entry = entry?;
568            let path = entry.path();
569
570            if path.is_file() {
571                if let Some(file_ext) = path.extension() {
572                    if file_ext == ext {
573                        let relative_path = path.strip_prefix(dir_path).map_err(|e| std::io::Error::new(std::io::ErrorKind::Other, e))?;
574                        let template_name = relative_path.with_extension("").to_string_lossy().replace(std::path::MAIN_SEPARATOR, "/");
575
576                        self.register_template_file(&template_name, path)?;
577                    }
578                }
579            }
580        }
581
582        Ok(())
583    }
584}
585
586#[cfg(feature = "liquid")]
587impl LiquidRenderer {
588    /// 转换 serde_json::Value 到 Liquid 的 Object
589    fn convert_to_liquid_context(&self, value: &serde_json::Value) -> Object {
590        let mut object = Object::new();
591
592        match value {
593            serde_json::Value::Object(map) => {
594                for (key, val) in map {
595                    object.insert(key.clone(), self.convert_to_liquid_value(val));
596                }
597            }
598            _ => {
599                // 如果根值不是对象,将其作为 "value" 键插入
600                object.insert("value".to_string(), self.convert_to_liquid_value(value));
601            }
602        }
603
604        object
605    }
606
607    /// 转换 serde_json::Value 到 Liquid 的 Value
608    fn convert_to_liquid_value(&self, value: &serde_json::Value) -> Value {
609        match value {
610            serde_json::Value::String(s) => Value::scalar(s.clone()),
611            serde_json::Value::Number(n) => {
612                if let Some(i) = n.as_i64() {
613                    Value::scalar(i)
614                }
615                else if let Some(f) = n.as_f64() {
616                    Value::scalar(f)
617                }
618                else {
619                    Value::Nil
620                }
621            }
622            serde_json::Value::Bool(b) => Value::scalar(*b),
623            serde_json::Value::Array(arr) => {
624                let mut vec = Vec::new();
625                for item in arr {
626                    vec.push(self.convert_to_liquid_value(item));
627                }
628                Value::array(vec)
629            }
630            serde_json::Value::Object(obj) => {
631                let mut map = Object::new();
632                for (k, v) in obj {
633                    map.insert(k.clone(), self.convert_to_liquid_value(v));
634                }
635                Value::Object(map)
636            }
637            serde_json::Value::Null => Value::Nil,
638        }
639    }
640}
641
642/// TinyTemplate 模板渲染器
643#[cfg(feature = "tinytemplate")]
644pub struct TinyTemplateRenderer {
645    tiny_template: TinyTemplate,
646}
647
648#[cfg(feature = "tinytemplate")]
649impl TinyTemplateRenderer {
650    /// 创建新的 TinyTemplate 渲染器
651    pub fn new() -> Self {
652        let mut tiny_template = TinyTemplate::new();
653        tiny_template.set_default_formatter(&tinytemplate::format_unescaped);
654        Self { tiny_template }
655    }
656
657    /// 获取底层的 TinyTemplate 实例
658    pub fn inner(&self) -> &TinyTemplate {
659        &self.tiny_template
660    }
661
662    /// 获取可变的底层 TinyTemplate 实例
663    pub fn inner_mut(&mut self) -> &mut TinyTemplate {
664        &mut self.tiny_template
665    }
666}
667
668#[cfg(feature = "tinytemplate")]
669impl Default for TinyTemplateRenderer {
670    fn default() -> Self {
671        Self::new()
672    }
673}
674
675#[cfg(feature = "tinytemplate")]
676#[async_trait]
677impl TemplateRenderer for TinyTemplateRenderer {
678    fn render(&self, template_name: &str, context: &serde_json::Value) -> TemplateResult<String> {
679        // TinyTemplate 直接使用 serde_json::Value 作为上下文
680        self.tiny_template.render(template_name, context).map_err(|e| std::io::Error::new(std::io::ErrorKind::Other, format!("Failed to render template '{}': {}", template_name, e)))
681    }
682
683    fn register_template(&mut self, name: &str, content: &str) -> TemplateResult<()> {
684        self.tiny_template.add_template(name, content).map_err(|e| std::io::Error::new(std::io::ErrorKind::Other, format!("Failed to register template '{}': {}", name, e)))?;
685        Ok(())
686    }
687
688    fn register_template_file(&mut self, name: &str, path: &Path) -> TemplateResult<()> {
689        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)))?;
690        self.register_template(name, &content)
691    }
692
693    fn register_templates_from_dir(&mut self, dir: &Path, extension: Option<&str>) -> TemplateResult<()> {
694        let dir_path = dir;
695        let ext = extension.unwrap_or("tt");
696
697        for entry in WalkDir::new(dir_path) {
698            let entry = entry?;
699            let path = entry.path();
700
701            if path.is_file() {
702                if let Some(file_ext) = path.extension() {
703                    if file_ext == ext {
704                        let relative_path = path.strip_prefix(dir_path).map_err(|e| std::io::Error::new(std::io::ErrorKind::Other, e))?;
705                        let template_name = relative_path.with_extension("").to_string_lossy().replace(std::path::MAIN_SEPARATOR, "/");
706
707                        self.register_template_file(&template_name, path)?;
708                    }
709                }
710            }
711        }
712
713        Ok(())
714    }
715}
716
717/// Askama 模板渲染器
718/// 注意:Askama 是基于宏的模板引擎,这里提供一个通用的包装器
719#[cfg(feature = "askama")]
720pub struct AskamaRenderer {
721    // Askama 不需要运行时模板注册,因为模板是在编译时处理的
722    // 这里只是为了符合 TemplateRenderer trait
723}
724
725#[cfg(feature = "askama")]
726impl AskamaRenderer {
727    /// 创建新的 Askama 渲染器
728    pub fn new() -> Self {
729        Self {}
730    }
731}
732
733#[cfg(feature = "askama")]
734impl Default for AskamaRenderer {
735    fn default() -> Self {
736        Self::new()
737    }
738}
739
740#[cfg(feature = "askama")]
741#[async_trait]
742impl TemplateRenderer for AskamaRenderer {
743    fn render(&self, template_name: &str, context: &serde_json::Value) -> TemplateResult<String> {
744        // Askama 模板是编译时定义的,无法在运行时动态渲染
745        // 这里返回一个错误,实际使用时应该直接使用 Askama 的模板实现
746        Err(std::io::Error::new(std::io::ErrorKind::Unsupported, "Askama renderer doesn't support dynamic template rendering"))
747    }
748
749    fn register_template(&mut self, name: &str, content: &str) -> TemplateResult<()> {
750        // Askama 不需要运行时模板注册
751        Ok(())
752    }
753
754    fn register_template_file(&mut self, name: &str, path: &Path) -> TemplateResult<()> {
755        // Askama 不需要运行时模板注册
756        Ok(())
757    }
758
759    fn register_templates_from_dir(&mut self, dir: &Path, extension: Option<&str>) -> TemplateResult<()> {
760        // Askama 不需要运行时模板注册
761        Ok(())
762    }
763}
764
765/// 模板管理器
766pub struct TemplateManager {
767    renderers: HashMap<TemplateEngine, Box<dyn TemplateRenderer>>,
768    template_dirs: Vec<PathBuf>,
769}
770
771impl TemplateManager {
772    /// 创建新的模板管理器
773    pub fn new() -> Self {
774        let mut renderers = HashMap::new();
775
776        renderers.insert(TemplateEngine::Handlebars, Box::new(HandlebarsRenderer::new()) as Box<dyn TemplateRenderer>);
777
778        renderers.insert(TemplateEngine::Jinja2, Box::new(Jinja2Renderer::new()) as Box<dyn TemplateRenderer>);
779
780        // 注册 DejaVu 模板引擎,使用默认配置
781        renderers.insert(TemplateEngine::DejaVu, Box::new(DejaVuRenderer::new()) as Box<dyn TemplateRenderer>);
782
783        // 注册 Tera 模板引擎(如果启用)
784        #[cfg(feature = "tera")]
785        {
786            renderers.insert(TemplateEngine::Tera, Box::new(TeraRenderer::new()) as Box<dyn TemplateRenderer>);
787        }
788
789        // 注册 Liquid 模板引擎(如果启用)
790        #[cfg(feature = "liquid")]
791        {
792            renderers.insert(TemplateEngine::Liquid, Box::new(LiquidRenderer::new()) as Box<dyn TemplateRenderer>);
793        }
794
795        // 注册 TinyTemplate 模板引擎(如果启用)
796        #[cfg(feature = "tinytemplate")]
797        {
798            renderers.insert(TemplateEngine::TinyTemplate, Box::new(TinyTemplateRenderer::new()) as Box<dyn TemplateRenderer>);
799        }
800
801        // 注册 Askama 模板引擎(如果启用)
802        #[cfg(feature = "askama")]
803        {
804            renderers.insert(TemplateEngine::Askama, Box::new(AskamaRenderer::new()) as Box<dyn TemplateRenderer>);
805        }
806
807        Self { renderers, template_dirs: Vec::new() }
808    }
809
810    /// 注册自定义模板渲染器
811    pub fn register_renderer(&mut self, engine: TemplateEngine, renderer: Box<dyn TemplateRenderer>) {
812        self.renderers.insert(engine, renderer);
813    }
814
815    /// 注册模板
816    pub fn register_template(&mut self, engine: TemplateEngine, name: &str, content: &str) -> TemplateResult<()> {
817        let renderer = self.renderers.get_mut(&engine).ok_or_else(|| std::io::Error::new(std::io::ErrorKind::NotFound, format!("Template engine '{:?}' not registered", engine)))?;
818        renderer.register_template(name, content)
819    }
820
821    /// 注册模板文件
822    pub fn register_template_file(&mut self, engine: TemplateEngine, name: &str, path: &Path) -> TemplateResult<()> {
823        let renderer = self.renderers.get_mut(&engine).ok_or_else(|| std::io::Error::new(std::io::ErrorKind::NotFound, format!("Template engine '{:?}' not registered", engine)))?;
824        renderer.register_template_file(name, path)
825    }
826
827    /// 添加模板目录
828    pub fn add_template_dir<P: AsRef<Path>>(&mut self, dir: P) {
829        self.template_dirs.push(dir.as_ref().to_path_buf());
830    }
831
832    /// 从所有已注册的模板目录加载模板
833    pub fn load_templates(&mut self, engine: TemplateEngine) -> TemplateResult<()> {
834        let ext = engine.default_extension();
835        let dirs: Vec<_> = self.template_dirs.clone();
836
837        if let Some(renderer) = self.renderers.get_mut(&engine) {
838            for dir in dirs {
839                if dir.exists() {
840                    renderer.register_templates_from_dir(dir.as_path(), Some(ext))?;
841                }
842            }
843        }
844        Ok(())
845    }
846
847    /// 使用指定引擎渲染模板
848    pub fn render(&self, engine: TemplateEngine, template_name: &str, context: &serde_json::Value) -> TemplateResult<String> {
849        let renderer = self.renderers.get(&engine).ok_or_else(|| std::io::Error::new(std::io::ErrorKind::NotFound, format!("Template engine '{:?}' not registered", engine)))?;
850
851        renderer.render(template_name, context)
852    }
853
854    /// 异步渲染模板
855    pub async fn render_async(&self, engine: TemplateEngine, template_name: &str, context: &serde_json::Value) -> TemplateResult<String> {
856        let renderer = self.renderers.get(&engine).ok_or_else(|| std::io::Error::new(std::io::ErrorKind::NotFound, format!("Template engine '{:?}' not registered", engine)))?;
857
858        renderer.render_async(template_name, context).await
859    }
860}
861
862impl Default for TemplateManager {
863    fn default() -> Self {
864        Self::new()
865    }
866}
867
868/// 将类型转换为 serde_json::Value 的 trait
869pub trait ToJsonValue {
870    /// 将自身转换为 serde_json::Value
871    fn to_json_value(&self) -> serde_json::Value;
872}
873
874impl ToJsonValue for String {
875    fn to_json_value(&self) -> serde_json::Value {
876        serde_json::Value::String(self.clone())
877    }
878}
879
880impl ToJsonValue for &str {
881    fn to_json_value(&self) -> serde_json::Value {
882        serde_json::Value::String(self.to_string())
883    }
884}
885
886impl ToJsonValue for i64 {
887    fn to_json_value(&self) -> serde_json::Value {
888        serde_json::Value::Number(serde_json::Number::from(*self))
889    }
890}
891
892impl ToJsonValue for i32 {
893    fn to_json_value(&self) -> serde_json::Value {
894        serde_json::Value::Number(serde_json::Number::from(*self))
895    }
896}
897
898impl ToJsonValue for f64 {
899    fn to_json_value(&self) -> serde_json::Value {
900        serde_json::Value::Number(serde_json::Number::from_f64(*self).unwrap())
901    }
902}
903
904impl ToJsonValue for bool {
905    fn to_json_value(&self) -> serde_json::Value {
906        serde_json::Value::Bool(*self)
907    }
908}
909
910impl ToJsonValue for usize {
911    fn to_json_value(&self) -> serde_json::Value {
912        serde_json::Value::Number(serde_json::Number::from(*self))
913    }
914}
915
916impl<T: ToJsonValue> ToJsonValue for Vec<T> {
917    fn to_json_value(&self) -> serde_json::Value {
918        serde_json::Value::Array(self.iter().map(|v| v.to_json_value()).collect())
919    }
920}
921
922impl<K: ToJsonValue, V: ToJsonValue> ToJsonValue for std::collections::HashMap<K, V> {
923    fn to_json_value(&self) -> serde_json::Value {
924        let mut map = serde_json::Map::new();
925        for (k, v) in self {
926            if let serde_json::Value::String(key) = k.to_json_value() {
927                map.insert(key, v.to_json_value());
928            }
929        }
930        serde_json::Value::Object(map)
931    }
932}
933
934impl<T: ToJsonValue> ToJsonValue for Option<T> {
935    fn to_json_value(&self) -> serde_json::Value {
936        match self {
937            Some(v) => v.to_json_value(),
938            None => serde_json::Value::Null,
939        }
940    }
941}
942
943impl ToJsonValue for PathBuf {
944    fn to_json_value(&self) -> serde_json::Value {
945        serde_json::Value::String(self.to_string_lossy().to_string())
946    }
947}