use std::{ fmt::Debug, fs, path::{Path, PathBuf}, }; use anyhow::Context; pub trait Loadable: Sized { fn load(base_path: &Path, path: &Path, slug_prefix: &str) -> anyhow::Result<Self>; } pub fn load_from_path<L: Loadable>( base_path: &Path, path: &Path, slug_prefix: &str, ) -> anyhow::Result<Vec<L>> { recursive_scan(path) .with_context(|| format!("Error scanning path '{}'", path.display()))? .iter() .map(|file_path| { let mut diff: PathBuf = file_path.strip_prefix(path)?.into(); diff.pop(); let diff = diff .to_str() .context("Could not convert path to a string")?; let prefix = if diff.is_empty() { slug_prefix.to_string() } else if slug_prefix.is_empty() { diff.to_string() } else { format!("{slug_prefix}/{diff}") }; L::load(base_path, file_path, &prefix) }) .collect() } pub fn recursive_scan<P>(path: P) -> anyhow::Result<Vec<PathBuf>> where P: AsRef<Path> + Debug, { let mut out = vec![]; for entry in fs::read_dir(path)? { let entry = entry?; let t = entry.file_type()?; let path = entry.path(); if t.is_dir() { out.extend(recursive_scan(&path)?); } else { out.push(path); } } Ok(out) }