rjango 0.1.1

A full-stack Rust backend framework inspired by Django
Documentation
use std::path::PathBuf;

use crate::template::{
    engine::TemplateEngine,
    loaders::{FileSystemLoader, LoaderError, TemplateLoader},
};

use super::{Template, TemplateBackend, TemplateError, validate_template_source};

#[derive(Debug)]
pub struct Jinja2 {
    dirs: Vec<PathBuf>,
    app_dirs: bool,
    engine: TemplateEngine,
}

impl Jinja2 {
    #[must_use]
    pub fn new(dirs: Vec<PathBuf>, app_dirs: bool) -> Self {
        Self {
            dirs,
            app_dirs,
            engine: TemplateEngine::new(),
        }
    }

    #[must_use]
    pub fn dirs(&self) -> &[PathBuf] {
        &self.dirs
    }

    #[must_use]
    pub const fn app_dirs(&self) -> bool {
        self.app_dirs
    }

    #[must_use]
    pub fn engine(&self) -> &TemplateEngine {
        &self.engine
    }

    fn search_dirs(&self) -> Vec<PathBuf> {
        let mut dirs = self.dirs.clone();
        if self.app_dirs {
            dirs.extend(self.dirs.iter().map(|dir| dir.join("jinja2")));
        }
        dirs
    }

    fn loader(&self) -> FileSystemLoader {
        FileSystemLoader::new(self.search_dirs())
    }
}

impl Default for Jinja2 {
    fn default() -> Self {
        Self::new(Vec::new(), false)
    }
}

impl TemplateBackend for Jinja2 {
    fn from_string(&self, template_string: &str) -> Result<Template, TemplateError> {
        validate_template_source("<string>", template_string)?;
        Ok(Template::new("<string>", template_string))
    }

    fn get_template(&self, template_name: &str) -> Result<Template, TemplateError> {
        let (source, _) = self
            .loader()
            .get_template(template_name)
            .map_err(map_loader_error)?;
        validate_template_source(template_name, &source)?;
        Ok(Template::new(template_name, source))
    }
}

fn map_loader_error(error: LoaderError) -> TemplateError {
    match error {
        LoaderError::NotFound(name) => TemplateError::NotFound(name),
        LoaderError::Io(error) => {
            TemplateError::RenderError(format!("failed to load template: {error}"))
        }
    }
}