cli_testing_specialist/generator/
templates.rs1use crate::error::{Error, Result};
2use std::collections::HashMap;
3
4const BATS_TEST_TEMPLATE: &str = include_str!("../../templates/bats-test.template");
6const CONCURRENCY_TEST: &str = include_str!("../../templates/concurrency-test.fragment");
7const DESTRUCTIVE_OPS: &str = include_str!("../../templates/destructive-ops.fragment");
8const DIRECTORY_TRAVERSAL_LIMITS: &str =
9 include_str!("../../templates/directory-traversal-limits.fragment");
10const INPUT_VALIDATION: &str = include_str!("../../templates/input-validation.fragment");
11const PERFORMANCE_TEST: &str = include_str!("../../templates/performance-test.fragment");
12const SUBCOMMAND_HELP: &str = include_str!("../../templates/subcommand-help.fragment");
13
14pub struct TemplateEngine {
16 templates: HashMap<String, String>,
18}
19
20impl TemplateEngine {
21 pub fn new() -> Result<Self> {
23 Ok(Self {
24 templates: HashMap::new(),
25 })
26 }
27
28 pub fn load_templates(&mut self) -> Result<()> {
30 log::info!("Loading embedded templates");
31
32 self.templates
34 .insert("bats-test".to_string(), BATS_TEST_TEMPLATE.to_string());
35 self.templates
36 .insert("concurrency-test".to_string(), CONCURRENCY_TEST.to_string());
37 self.templates
38 .insert("destructive-ops".to_string(), DESTRUCTIVE_OPS.to_string());
39 self.templates.insert(
40 "directory-traversal-limits".to_string(),
41 DIRECTORY_TRAVERSAL_LIMITS.to_string(),
42 );
43 self.templates
44 .insert("input-validation".to_string(), INPUT_VALIDATION.to_string());
45 self.templates
46 .insert("performance-test".to_string(), PERFORMANCE_TEST.to_string());
47 self.templates
48 .insert("subcommand-help".to_string(), SUBCOMMAND_HELP.to_string());
49
50 log::info!("Loaded {} templates", self.templates.len());
51
52 Ok(())
53 }
54
55 pub fn get_template(&self, name: &str) -> Result<&str> {
57 self.templates
58 .get(name)
59 .map(|s| s.as_str())
60 .ok_or_else(|| Error::Config(format!("Template not found: {}", name)))
61 }
62
63 pub fn substitute(&self, template: &str, variables: &HashMap<String, String>) -> String {
66 let mut result = template.to_string();
67
68 for (key, value) in variables {
69 let placeholder = format!("${{{}}}", key);
70 result = result.replace(&placeholder, value);
71 }
72
73 result
74 }
75
76 pub fn render(
78 &self,
79 template_name: &str,
80 variables: &HashMap<String, String>,
81 ) -> Result<String> {
82 let template = self.get_template(template_name)?;
83 Ok(self.substitute(template, variables))
84 }
85
86 pub fn available_templates(&self) -> Vec<String> {
88 self.templates.keys().cloned().collect()
89 }
90
91 pub fn validate_template(&self, template_name: &str, required_vars: &[&str]) -> Result<()> {
93 let template = self.get_template(template_name)?;
94
95 for var in required_vars {
96 let placeholder = format!("${{{}}}", var);
97 if !template.contains(&placeholder) {
98 log::warn!(
99 "Template '{}' does not contain expected variable: {}",
100 template_name,
101 var
102 );
103 }
104 }
105
106 Ok(())
107 }
108}
109
110impl Default for TemplateEngine {
111 fn default() -> Self {
112 Self::new().expect("Failed to create default TemplateEngine")
113 }
114}
115
116#[cfg(test)]
117mod tests {
118 use super::*;
119
120 #[test]
121 fn test_template_engine_creation() {
122 let engine = TemplateEngine::new();
123 assert!(engine.is_ok());
124 }
125
126 #[test]
127 fn test_load_templates() {
128 let mut engine = TemplateEngine::new().unwrap();
129 let result = engine.load_templates();
130
131 assert!(result.is_ok());
132 assert_eq!(engine.templates.len(), 7); assert!(engine.templates.contains_key("bats-test"));
134 assert!(engine.templates.contains_key("concurrency-test"));
135 }
136
137 #[test]
138 fn test_get_template() {
139 let mut engine = TemplateEngine::new().unwrap();
140 engine.load_templates().unwrap();
141
142 let template = engine.get_template("bats-test");
143 assert!(template.is_ok());
144 assert!(!template.unwrap().is_empty());
145 }
146
147 #[test]
148 fn test_get_nonexistent_template() {
149 let mut engine = TemplateEngine::new().unwrap();
150 engine.load_templates().unwrap();
151
152 let result = engine.get_template("nonexistent");
153 assert!(result.is_err());
154 }
155
156 #[test]
157 fn test_substitute() {
158 let engine = TemplateEngine::new().unwrap();
159
160 let template = "Hello ${NAME}, you are ${AGE} years old.";
161 let mut vars = HashMap::new();
162 vars.insert("NAME".to_string(), "Alice".to_string());
163 vars.insert("AGE".to_string(), "30".to_string());
164
165 let result = engine.substitute(template, &vars);
166 assert_eq!(result, "Hello Alice, you are 30 years old.");
167 }
168
169 #[test]
170 fn test_substitute_missing_variable() {
171 let engine = TemplateEngine::new().unwrap();
172
173 let template = "Hello ${NAME}, you are ${AGE} years old.";
174 let mut vars = HashMap::new();
175 vars.insert("NAME".to_string(), "Alice".to_string());
176
177 let result = engine.substitute(template, &vars);
178 assert_eq!(result, "Hello Alice, you are ${AGE} years old.");
180 }
181
182 #[test]
183 fn test_available_templates() {
184 let mut engine = TemplateEngine::new().unwrap();
185 engine.load_templates().unwrap();
186
187 let templates = engine.available_templates();
188 assert_eq!(templates.len(), 7);
189 assert!(templates.contains(&"bats-test".to_string()));
190 assert!(templates.contains(&"performance-test".to_string()));
191 }
192}