use crate::config::RuniqueConfig;
use crate::context::tera::static_tera;
use crate::utils::aliases::ARlockmap;
use crate::utils::constante::{ADMIN_TEMPLATES, ERROR_CORPS, FIELD_TEMPLATES, SIMPLE_TEMPLATES};
use regex::{Captures, Regex};
use std::path::Path;
use tera::Tera;
pub struct TemplateLoader;
impl TemplateLoader {
pub fn init(
config: &RuniqueConfig,
url_registry: ARlockmap,
) -> Result<Tera, Box<dyn std::error::Error>> {
let mut tera = Tera::default();
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_url.clone(),
url_registry.clone(),
);
let balise_link =
Regex::new(r#"\{%\s*(?P<tag>static|media)\s*['"](?P<link>[^'"]+)['"]\s*%}"#).unwrap();
let link_regex = Regex::new(
r#"\{%\s*link\s*['"](?P<name>[^'"]+)['"]\s*(?:,\s*)?(?P<params>[^%]*?)\s*%}"#,
)
.unwrap();
let form_field_regex =
Regex::new(r#"\{%\s*form\.([a-zA-Z0-9_]+)\.([a-zA-Z0-9_]+)\s*%}"#).unwrap();
let form_full_regex = Regex::new(r#"\{%\s*form\.([a-zA-Z0-9_]+)\s*%}"#).unwrap();
Self::load_internal_templates(
&mut tera,
&balise_link,
&link_regex,
&form_field_regex,
&form_full_regex,
)?;
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,
&balise_link,
&link_regex,
&form_field_regex,
&form_full_regex,
);
let name = entry
.strip_prefix(template_dir)?
.to_string_lossy()
.replace("\\", "/");
all_templates.push((name, processed));
}
}
}
tera.add_raw_templates(all_templates)?;
Ok(tera)
}
fn process_content(
mut content: String,
balise_link: &Regex,
link_regex: &Regex,
form_field_regex: &Regex,
form_full_regex: &Regex,
) -> String {
content = content.replace("{% csrf %}", r#"{% include "csrf" %}"#);
content = content.replace("{% messages %}", r#"{% include "message" %}"#);
content = content.replace("{{ csp }}", r#"{% include "csp" %}"#);
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 = balise_link
.replace_all(&content, |caps: &Captures| {
format!(r#"{{{{ "{}" | {} }}}}"#, &caps["link"], &caps["tag"])
})
.to_string();
content
}
fn load_internal_templates(
tera: &mut Tera,
balise_link: &Regex,
link_regex: &Regex,
form_field_regex: &Regex,
form_full_regex: &Regex,
) -> Result<(), Box<dyn std::error::Error>> {
for (name, content) in SIMPLE_TEMPLATES
.iter()
.chain(ERROR_CORPS.iter())
.chain(FIELD_TEMPLATES.iter())
.chain(ADMIN_TEMPLATES.iter())
{
let processed = Self::process_content(
content.to_string(),
balise_link,
link_regex,
form_field_regex,
form_full_regex,
);
tera.add_raw_template(name, &processed)?;
}
Ok(())
}
}