use crate::error::{Error, Result};
use std::collections::HashMap;
const BATS_TEST_TEMPLATE: &str = include_str!("../../templates/bats-test.template");
const CONCURRENCY_TEST: &str = include_str!("../../templates/concurrency-test.fragment");
const DESTRUCTIVE_OPS: &str = include_str!("../../templates/destructive-ops.fragment");
const DIRECTORY_TRAVERSAL_LIMITS: &str =
include_str!("../../templates/directory-traversal-limits.fragment");
const INPUT_VALIDATION: &str = include_str!("../../templates/input-validation.fragment");
const PERFORMANCE_TEST: &str = include_str!("../../templates/performance-test.fragment");
const SUBCOMMAND_HELP: &str = include_str!("../../templates/subcommand-help.fragment");
pub struct TemplateEngine {
templates: HashMap<String, String>,
}
impl TemplateEngine {
pub fn new() -> Result<Self> {
Ok(Self {
templates: HashMap::new(),
})
}
pub fn load_templates(&mut self) -> Result<()> {
log::info!("Loading embedded templates");
self.templates
.insert("bats-test".to_string(), BATS_TEST_TEMPLATE.to_string());
self.templates
.insert("concurrency-test".to_string(), CONCURRENCY_TEST.to_string());
self.templates
.insert("destructive-ops".to_string(), DESTRUCTIVE_OPS.to_string());
self.templates.insert(
"directory-traversal-limits".to_string(),
DIRECTORY_TRAVERSAL_LIMITS.to_string(),
);
self.templates
.insert("input-validation".to_string(), INPUT_VALIDATION.to_string());
self.templates
.insert("performance-test".to_string(), PERFORMANCE_TEST.to_string());
self.templates
.insert("subcommand-help".to_string(), SUBCOMMAND_HELP.to_string());
log::info!("Loaded {} templates", self.templates.len());
Ok(())
}
pub fn get_template(&self, name: &str) -> Result<&str> {
self.templates
.get(name)
.map(|s| s.as_str())
.ok_or_else(|| Error::Config(format!("Template not found: {}", name)))
}
pub fn substitute(&self, template: &str, variables: &HashMap<String, String>) -> String {
let mut result = template.to_string();
for (key, value) in variables {
let placeholder = format!("${{{}}}", key);
result = result.replace(&placeholder, value);
}
result
}
pub fn render(
&self,
template_name: &str,
variables: &HashMap<String, String>,
) -> Result<String> {
let template = self.get_template(template_name)?;
Ok(self.substitute(template, variables))
}
pub fn available_templates(&self) -> Vec<String> {
self.templates.keys().cloned().collect()
}
pub fn validate_template(&self, template_name: &str, required_vars: &[&str]) -> Result<()> {
let template = self.get_template(template_name)?;
for var in required_vars {
let placeholder = format!("${{{}}}", var);
if !template.contains(&placeholder) {
log::warn!(
"Template '{}' does not contain expected variable: {}",
template_name,
var
);
}
}
Ok(())
}
}
impl Default for TemplateEngine {
fn default() -> Self {
Self::new().expect("Failed to create default TemplateEngine")
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_template_engine_creation() {
let engine = TemplateEngine::new();
assert!(engine.is_ok());
}
#[test]
fn test_load_templates() {
let mut engine = TemplateEngine::new().unwrap();
let result = engine.load_templates();
assert!(result.is_ok());
assert_eq!(engine.templates.len(), 7); assert!(engine.templates.contains_key("bats-test"));
assert!(engine.templates.contains_key("concurrency-test"));
}
#[test]
fn test_get_template() {
let mut engine = TemplateEngine::new().unwrap();
engine.load_templates().unwrap();
let template = engine.get_template("bats-test");
assert!(template.is_ok());
assert!(!template.unwrap().is_empty());
}
#[test]
fn test_get_nonexistent_template() {
let mut engine = TemplateEngine::new().unwrap();
engine.load_templates().unwrap();
let result = engine.get_template("nonexistent");
assert!(result.is_err());
}
#[test]
fn test_substitute() {
let engine = TemplateEngine::new().unwrap();
let template = "Hello ${NAME}, you are ${AGE} years old.";
let mut vars = HashMap::new();
vars.insert("NAME".to_string(), "Alice".to_string());
vars.insert("AGE".to_string(), "30".to_string());
let result = engine.substitute(template, &vars);
assert_eq!(result, "Hello Alice, you are 30 years old.");
}
#[test]
fn test_substitute_missing_variable() {
let engine = TemplateEngine::new().unwrap();
let template = "Hello ${NAME}, you are ${AGE} years old.";
let mut vars = HashMap::new();
vars.insert("NAME".to_string(), "Alice".to_string());
let result = engine.substitute(template, &vars);
assert_eq!(result, "Hello Alice, you are ${AGE} years old.");
}
#[test]
fn test_available_templates() {
let mut engine = TemplateEngine::new().unwrap();
engine.load_templates().unwrap();
let templates = engine.available_templates();
assert_eq!(templates.len(), 7);
assert!(templates.contains(&"bats-test".to_string()));
assert!(templates.contains(&"performance-test".to_string()));
}
}