use crate::config::RuniqueConfig;
use crate::context::tera::static_tera;
use crate::utils::aliases::ARlockmap;
use crate::utils::constante::*;
use regex::Captures;
use std::{collections::HashMap, path::Path};
use tera::Tera;
pub(crate) struct TemplateLoader;
impl TemplateLoader {
pub fn init(
config: &RuniqueConfig,
url_registry: ARlockmap,
) -> Result<Tera, Box<dyn std::error::Error>> {
let mut tera = Tera::default();
tera.autoescape_on(vec!["html", "xml"]);
static_tera::register_asset_filters(
&mut tera,
config.static_files.static_url.clone(),
config.static_files.media_url.clone(),
config.static_files.static_runique_url.clone(),
config.static_files.media_runique.clone(),
url_registry.clone(),
);
let static_dir = Path::new(&config.static_files.staticfiles_dirs);
let integrity_map = crate::utils::integrity::build_integrity_map(static_dir);
Self::load_internal_templates(&mut tera, &integrity_map)?;
let mut all_templates = Vec::new();
for dir_string in &config.static_files.templates_dir {
let template_dir = Path::new(dir_string);
let pattern = format!("{}/**/*.html", template_dir.display());
if let Ok(paths) = glob::glob(&pattern) {
for entry in paths.flatten() {
let content = std::fs::read_to_string(&entry)?;
let processed = Self::process_content(content, &integrity_map);
let name = entry
.strip_prefix(template_dir)?
.to_string_lossy()
.replace("\\", "/");
all_templates.push((name, processed));
}
}
}
let user_count = all_templates.len();
if let Err(e) = tera.add_raw_templates(all_templates) {
tracing::error!(error = %e, "user template failed to load");
return Err(Box::new(e));
}
if let Some(level) = crate::utils::runique_log::get_log()
.builder
.as_ref()
.and_then(|b| b.templates)
{
let internal_count = SIMPLE_TEMPLATES.len()
+ ERROR_CORPS.len()
+ FIELD_TEMPLATES.len()
+ AUTH_TEMPLATES.len()
+ ADMIN_TEMPLATES.len();
crate::runique_log!(
level,
internal = internal_count,
user = user_count,
total = internal_count + user_count,
"templates loaded"
);
}
Ok(tera)
}
fn process_content(mut content: String, integrity_map: &HashMap<String, String>) -> String {
content = content.replace("{% csrf %}", r#"{% include "csrf.html" %}"#);
content = content.replace("{% messages %}", r#"{% include "message.html" %}"#);
content = content.replace("{% csp %}", r#"{% include "csp.html" %}"#);
content = FORM_FIELD_REGEX
.replace_all(&content, |caps: &Captures| {
format!(
r"{{{{ {} | form(field='{}') | safe }}}}",
&caps[1], &caps[2]
)
})
.to_string();
content = FORM_FULL_REGEX
.replace_all(&content, |caps: &Captures| {
format!("{{{{ {} | form | safe }}}}", &caps[1])
})
.to_string();
content = LINK_REGEX
.replace_all(&content, |caps: &Captures| {
let name = &caps["name"];
let params = caps
.name("params")
.map(|m| m.as_str().trim())
.filter(|s| !s.is_empty());
match params {
Some(p) => format!(r"{{{{ link(link='{}', {}) }}}}", name, p),
None => format!(r"{{{{ link(link='{}') }}}}", name),
}
})
.to_string();
content = MARKDOWN_REGEX
.replace_all(&content, |caps: &Captures| {
format!("{{{{ {} | markdown | safe }}}}", &caps[1])
})
.to_string();
content = ADMIN_FORM_HTML_REGEX
.replace_all(&content, "{{ form_fields.html | safe }}")
.to_string();
content = BALISE_LINK
.replace_all(&content, |caps: &Captures| {
let path = &caps["link"];
let tag = &caps["tag"];
let q = &caps["q"];
let url = format!(r#"{{{{ "{}" | {} }}}}"#, path, tag);
match integrity_map.get(path) {
Some(hash) => {
format!(
r#"{}{}{} integrity="{}" crossorigin="anonymous""#,
q, url, q, hash
)
}
None => format!("{}{}{}", q, url, q),
}
})
.to_string();
content = BALISE_LINK_VAR
.replace_all(&content, |caps: &Captures| {
format!("{{{{ {} | {} }}}}", &caps["var"], &caps["tag"])
})
.to_string();
content
}
fn load_internal_templates(
tera: &mut Tera,
integrity_map: &HashMap<String, String>,
) -> Result<(), Box<dyn std::error::Error>> {
for (name, content) in SIMPLE_TEMPLATES
.iter()
.chain(ERROR_CORPS.iter())
.chain(FIELD_TEMPLATES.iter())
.chain(AUTH_TEMPLATES.iter())
.chain(ADMIN_TEMPLATES.iter())
{
let processed = Self::process_content(content.to_string(), integrity_map);
if let Err(e) = tera.add_raw_template(name, &processed) {
tracing::error!(template = %name, error = %e, "internal template failed to load");
return Err(Box::new(e));
}
}
Ok(())
}
}