2024-05-14 14:30:03 -05:00
|
|
|
use crate::{file_watcher::prelude::*, prelude::*};
|
2024-05-13 17:53:23 -05:00
|
|
|
use minijinja::Environment;
|
|
|
|
use pathdiff::diff_paths;
|
2024-05-14 14:30:03 -05:00
|
|
|
use std::{path::PathBuf, sync::Arc};
|
|
|
|
use tokio::sync::Mutex;
|
2024-05-14 12:28:27 -05:00
|
|
|
use tower_livereload::Reloader;
|
2024-05-14 14:30:03 -05:00
|
|
|
use tracing::{info, instrument};
|
2024-05-13 17:53:23 -05:00
|
|
|
|
2024-05-14 14:30:03 -05:00
|
|
|
#[derive(Clone, Debug)]
|
2024-05-13 17:53:23 -05:00
|
|
|
pub struct Templates {
|
|
|
|
env: Arc<Mutex<Environment<'static>>>,
|
2024-05-14 12:28:27 -05:00
|
|
|
dir: PathBuf,
|
2024-05-13 17:53:23 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
impl Templates {
|
2024-05-14 12:28:27 -05:00
|
|
|
pub fn for_dir<P: Into<PathBuf>>(dir: P) -> Self {
|
2024-05-13 17:53:23 -05:00
|
|
|
let env = Arc::new(Mutex::new(Environment::new()));
|
2024-05-14 12:28:27 -05:00
|
|
|
Self {
|
|
|
|
env,
|
|
|
|
dir: dir.into(),
|
|
|
|
}
|
2024-05-13 17:53:23 -05:00
|
|
|
}
|
|
|
|
|
2024-05-14 14:30:03 -05:00
|
|
|
pub async fn try_load<P: Into<PathBuf>>(dir: P) -> Result<Self> {
|
2024-05-14 12:28:27 -05:00
|
|
|
let result = Self::for_dir(dir);
|
2024-05-13 17:53:23 -05:00
|
|
|
result.load_env().await?;
|
|
|
|
Ok(result)
|
|
|
|
}
|
|
|
|
|
2024-05-14 12:28:27 -05:00
|
|
|
pub async fn start_watcher(
|
|
|
|
self: Arc<Self>,
|
|
|
|
reloader: Option<Reloader>,
|
2024-05-14 14:30:03 -05:00
|
|
|
) -> Result<Option<FileWatcher>> {
|
2024-05-14 12:28:27 -05:00
|
|
|
if let Some(rl) = reloader {
|
|
|
|
Ok(Some(self.watch(rl).await?))
|
|
|
|
} else {
|
|
|
|
Ok(None)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-05-14 14:30:03 -05:00
|
|
|
async fn watch(self: Arc<Self>, reloader: Reloader) -> Result<FileWatcher> {
|
|
|
|
// TODO: only reload template that changed?
|
|
|
|
let watcher = file_monitor(self.dir.clone(), move || {
|
|
|
|
futures::executor::block_on(async {
|
|
|
|
self.load_env()
|
2024-05-14 12:28:27 -05:00
|
|
|
.await
|
|
|
|
.expect("Failed to reload templates after template changed during runtime");
|
2024-05-14 14:30:03 -05:00
|
|
|
reloader.reload();
|
|
|
|
});
|
|
|
|
})?;
|
2024-05-14 12:28:27 -05:00
|
|
|
Ok(watcher)
|
2024-05-13 17:53:23 -05:00
|
|
|
}
|
|
|
|
|
2024-05-14 14:30:03 -05:00
|
|
|
#[instrument]
|
|
|
|
pub async fn load_env(&self) -> Result<()> {
|
2024-05-13 17:53:23 -05:00
|
|
|
info!("Loading templates...");
|
2024-05-14 12:28:27 -05:00
|
|
|
for d in walkdir::WalkDir::new(&self.dir) {
|
2024-05-13 17:53:23 -05:00
|
|
|
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();
|
2024-05-14 14:30:03 -05:00
|
|
|
info!("Loading template {filename:?} ({d:?})");
|
2024-05-13 17:53:23 -05:00
|
|
|
self.env
|
|
|
|
.lock()
|
|
|
|
.await
|
|
|
|
.add_template_owned(filename, std::fs::read_to_string(d.path())?)?;
|
|
|
|
}
|
|
|
|
Err(_) => todo!(),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
|
|
|
pub async fn render<S: serde::ser::Serialize>(
|
|
|
|
&self,
|
|
|
|
template_name: &str,
|
|
|
|
context: S,
|
2024-05-14 14:30:03 -05:00
|
|
|
) -> Result<String> {
|
|
|
|
Ok(self
|
|
|
|
.env
|
2024-05-13 17:53:23 -05:00
|
|
|
.lock()
|
|
|
|
.await
|
|
|
|
.get_template(template_name)?
|
2024-05-14 14:30:03 -05:00
|
|
|
.render(context)?)
|
2024-05-13 17:53:23 -05:00
|
|
|
}
|
|
|
|
}
|