vika_cli/templates/
engine.rs1use crate::error::{Result, TemplateError};
2use crate::templates::registry::TemplateId;
3use crate::templates::resolver::TemplateResolver;
4use serde::Serialize;
5use std::path::Path;
6use tera::{Context, Tera};
7
8pub struct TemplateEngine {
10 tera: Tera,
11 resolver: TemplateResolver,
12}
13
14impl TemplateEngine {
15 pub fn new(project_root: Option<&Path>) -> Result<Self> {
19 let resolver = TemplateResolver::new(project_root);
20 let mut tera = Tera::default();
21
22 for template_id in TemplateId::all() {
24 let template_content = resolver.resolve(template_id)?;
25 let template_name = template_id.filename();
26
27 tera.add_raw_template(&template_name, &template_content)
28 .map_err(|e| {
29 crate::error::GenerationError::Template(TemplateError::InvalidSyntax {
30 name: template_name.to_string(),
31 message: e.to_string(),
32 })
33 })?;
34 }
35
36 Ok(Self { tera, resolver })
37 }
38
39 pub fn render<T: Serialize>(
41 &self,
42 template_id: TemplateId,
43 context: &T,
44 ) -> Result<String> {
45 let template_name = template_id.filename();
46
47 let json_value = serde_json::to_value(context)
48 .map_err(|e| {
49 crate::error::GenerationError::Template(TemplateError::RenderFailed {
50 name: template_name.to_string(),
51 message: format!("Failed to serialize context: {}", e),
52 })
53 })?;
54 let tera_context = Context::from_serialize(&json_value)
55 .map_err(|e| {
56 crate::error::GenerationError::Template(TemplateError::RenderFailed {
57 name: template_name.to_string(),
58 message: format!("Failed to create Tera context: {}", e),
59 })
60 })?;
61
62 self.tera
63 .render(&template_name, &tera_context)
64 .map_err(|e| {
65 crate::error::GenerationError::Template(TemplateError::RenderFailed {
66 name: template_name.to_string(),
67 message: e.to_string(),
68 })
69 })
70 .map_err(Into::into)
71 }
72
73 pub fn is_overridden(&self, template_id: TemplateId) -> bool {
75 self.resolver.is_overridden(template_id)
76 }
77
78 pub fn list_templates(&self) -> Result<Vec<(String, bool)>> {
80 self.resolver.list_templates()
81 }
82}
83
84#[cfg(test)]
85mod tests {
86 use super::*;
87 use crate::templates::context::TypeContext;
88 use tempfile::TempDir;
89
90 #[test]
91 fn test_template_engine_new() {
92 let engine = TemplateEngine::new(None);
93 assert!(engine.is_ok());
94 }
95
96 #[test]
97 fn test_template_engine_render_enum() {
98 let engine = TemplateEngine::new(None).unwrap();
99 let context = TypeContext::enum_type("TestEnum".to_string(), vec!["A".to_string(), "B".to_string()]);
100 let result = engine.render(TemplateId::TypeEnum, &context);
101 assert!(result.is_ok());
102 let output = result.unwrap();
103 assert!(output.contains("TestEnum"));
104 assert!(output.contains("A"));
105 assert!(output.contains("B"));
106 }
107}
108