use std::{collections::HashMap, path::PathBuf}; use handlebars::Handlebars; use serde::Serialize; use crate::error::Result; #[derive(Debug, Default)] pub struct TemplateEngine { partials: HashMap, } impl TemplateEngine { pub fn new>(partial_path: T) -> Result { let pp: PathBuf = partial_path.into(); let partials = if pp.exists() { pp.read_dir()? .filter_map(|i| i.ok()) .fold(HashMap::new(), |mut acc, i| { if i.path().is_file() { if let Some(file_name) = i.path().with_extension("").file_name() { acc.insert( file_name.to_str().unwrap_or_default().to_string(), std::fs::read_to_string(i.path()).unwrap_or_default(), ); } } acc }) } else { HashMap::new() }; Ok(Self { partials }) } pub fn render_string(&self, template_string: &str, data: D) -> Result { let mut reg = Handlebars::new(); for (k, v) in self.partials.iter() { reg.register_partial(k, v)?; } let res = reg.render_template(template_string, &data)?; Ok(res) } pub fn render_file( &self, template_file: PathBuf, output_path: PathBuf, data: D, ) -> Result<()> { let template_content = std::fs::read_to_string(template_file)?; let res = self.render_string(&template_content, data)?; std::fs::write(output_path, res)?; Ok(()) } } #[cfg(test)] mod tests { use super::*; use serde::Serialize; #[derive(Serialize)] struct TestData { name: String, age: i32, tags: Vec, } impl Default for TestData { fn default() -> Self { Self { name: "Alice Allison".to_string(), age: 21, tags: vec!["cats".to_string(), "dogs".to_string(), "rust".to_string()], } } } #[test] fn test_template() { let eng = TemplateEngine::default(); let template_string = "Hello, my name is {{ name }}, I am {{ age }} years old and i like {{#each tags}}{{this}} {{/each}}"; let res = eng .render_string(template_string, &TestData::default()) .expect("Error parsing template"); assert_eq!( res, "Hello, my name is Alice Allison, I am 21 years old and i like cats dogs rust " ); } #[test] fn html_inclusion() { let data = TestData { name: "Alice Allison".to_string(), ..Default::default() }; let template_string = "Hello, my name is {{{ name }}}, I am {{ age }} years old and i like {{#each tags}}{{this}} {{/each}}"; let eng = TemplateEngine::default(); let res = eng .render_string(template_string, &data) .expect("Error parsing template"); assert_eq!( res, "Hello, my name is Alice Allison, I am 21 years old and i like cats dogs rust " ); } }