use std::{ fmt::Debug, fs, path::{Path, PathBuf}, }; use anyhow::Context; pub trait Loadable: Sized { fn load(path: &Path, slug_prefix: &str) -> anyhow::Result<Self>; } pub fn load_from_path<L: Loadable>(path: &str, slug_prefix: &str) -> anyhow::Result<Vec<L>> { recursive_scan(path)? .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(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) }