use crate::{file_watcher::prelude::*, prelude::*}; use minijinja::Environment; use pathdiff::diff_paths; use std::{path::PathBuf, sync::Arc}; use tokio::sync::Mutex; use tower_livereload::Reloader; use tracing::{info, instrument}; #[derive(Clone, Debug)] pub struct Templates { env: Arc>>, dir: PathBuf, } impl Templates { pub fn for_dir>(dir: P) -> Self { let env = Arc::new(Mutex::new(Environment::new())); Self { env, dir: dir.into(), } } pub async fn try_load>(dir: P) -> Result { let result = Self::for_dir(dir); result.load_env().await?; Ok(result) } pub async fn start_watcher( self: Arc, reloader: Option, ) -> Result> { if let Some(rl) = reloader { Ok(Some(self.watch(rl).await?)) } else { Ok(None) } } async fn watch(self: Arc, reloader: Reloader) -> Result { // TODO: only reload template that changed? let watcher = file_monitor(self.dir.clone(), move || { futures::executor::block_on(async { self.load_env() .await .expect("Failed to reload templates after template changed during runtime"); reloader.reload(); }); })?; Ok(watcher) } #[instrument] pub async fn load_env(&self) -> Result<()> { info!("Loading templates..."); for d in walkdir::WalkDir::new(&self.dir) { match d { Ok(d) => { if d.file_type().is_dir() { continue; } let filename: String = diff_paths(d.path(), "src/templates") .unwrap() .to_string_lossy() .into_owned(); info!("Loading template {filename:?} ({d:?})"); self.env .lock() .await .add_template_owned(filename, std::fs::read_to_string(d.path())?)?; } Err(_) => todo!(), } } Ok(()) } pub async fn render( &self, template_name: &str, context: S, ) -> Result { Ok(self .env .lock() .await .get_template(template_name)? .render(context)?) } }