#![warn(missing_docs)]
use std::{collections::HashMap, path::Path};
use async_trait::async_trait;
use oak_core::Parser;
use oak_jinja::{JinjaLanguage, JinjaLexer, JinjaParser};
use super::TemplateRenderer;
pub struct Jinja2Renderer {
templates: HashMap<String, String>,
language: JinjaLanguage,
}
impl Jinja2Renderer {
pub fn new() -> Self {
Self { templates: HashMap::new(), language: JinjaLanguage::default() }
}
fn render_content(&self, content: &str, context: &serde_json::Value) -> crate::TemplateResult<String> {
let _lexer = JinjaLexer::new(&self.language);
let parser = JinjaParser::new(&self.language);
let mut session = oak_core::parser::ParseSession::<JinjaLanguage>::new(16);
let _output = parser.parse(content, &[], &mut session);
let mut result = content.to_string();
if let serde_json::Value::Object(map) = context {
for (key, value) in map {
let placeholder = format!("{{{{ {} }}}}", key);
let value_str = match value {
serde_json::Value::String(s) => s.clone(),
serde_json::Value::Number(n) => n.to_string(),
serde_json::Value::Bool(b) => b.to_string(),
_ => value.to_string(),
};
result = result.replace(&placeholder, &value_str);
}
}
Ok(result)
}
}
impl Default for Jinja2Renderer {
fn default() -> Self {
Self::new()
}
}
#[async_trait]
impl TemplateRenderer for Jinja2Renderer {
fn render(&self, template_name: &str, context: &serde_json::Value) -> crate::TemplateResult<String> {
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)))?;
self.render_content(template_content, context)
}
fn register_template(&mut self, name: &str, content: &str) -> crate::TemplateResult<()> {
self.templates.insert(name.to_string(), content.to_string());
Ok(())
}
fn register_template_file(&mut self, name: &str, path: &Path) -> crate::TemplateResult<()> {
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)))?;
self.register_template(name, &content)
}
fn register_templates_from_dir(&mut self, dir: &Path, extension: Option<&str>) -> crate::TemplateResult<()> {
let dir_path = dir;
let ext = extension.unwrap_or("jinja2");
for entry in walkdir::WalkDir::new(dir_path) {
let entry = entry?;
let path = entry.path();
if path.is_file() {
if let Some(file_ext) = path.extension() {
if file_ext == ext {
let relative_path = path.strip_prefix(dir_path).map_err(|e| std::io::Error::new(std::io::ErrorKind::Other, e))?;
let template_name = relative_path.with_extension("").to_string_lossy().replace(std::path::MAIN_SEPARATOR, "/");
self.register_template_file(&template_name, path)?;
}
}
}
}
Ok(())
}
}