use std::path::{Path, PathBuf};
use crate::domain::model::site::{Asset, Source};
use crate::domain::usecases::site::readers::AssetsReader;
pub struct FsAssetsReader {
walk_root: PathBuf,
key_root: PathBuf,
}
impl FsAssetsReader {
pub fn new(walk_root: impl Into<PathBuf>, key_root: impl Into<PathBuf>) -> Self {
Self {
walk_root: walk_root.into(),
key_root: key_root.into(),
}
}
pub fn from_theme_dir(theme: impl Into<PathBuf>) -> Self {
let theme = theme.into();
let walk_root = theme.join("assets");
Self {
walk_root,
key_root: theme,
}
}
}
impl AssetsReader for FsAssetsReader {
fn assets<'a>(&'a self) -> Box<dyn Iterator<Item = anyhow::Result<Asset>> + 'a> {
if !self.walk_root.is_dir() {
return Box::new(std::iter::empty());
}
let key_root = self.key_root.as_path();
let it = walkdir::WalkDir::new(&self.walk_root)
.follow_links(false)
.into_iter()
.filter_map(move |res| match res {
Ok(e) if e.file_type().is_file() => Some(read_asset(key_root, e.path())),
Ok(_) => None,
Err(e) => Some(Err(anyhow::Error::new(e))),
});
Box::new(it)
}
}
fn read_asset(key_root: &Path, path: &Path) -> anyhow::Result<Asset> {
let relative = path
.strip_prefix(key_root)
.unwrap_or(path)
.to_string_lossy()
.replace('\\', "/");
let source = Source::relative_path(&relative)?;
let bytes = std::fs::read(path)?;
Ok(Asset::new(source, bytes))
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn missing_walk_root_is_empty() {
let reader = FsAssetsReader::new("/no/such/dir", "/no/such");
assert_eq!(reader.assets().count(), 0);
}
#[test]
fn key_root_distinct_from_walk_root_preserves_prefix() {
let tmp = tempfile::tempdir().unwrap();
let theme = tmp.path();
let assets_dir = theme.join("assets");
std::fs::create_dir_all(&assets_dir).unwrap();
std::fs::write(assets_dir.join("style.css"), b"body{}").unwrap();
std::fs::write(assets_dir.join("logo.svg"), b"<svg/>").unwrap();
let reader = FsAssetsReader::new(&assets_dir, theme);
let mut assets: Vec<Asset> = reader.assets().collect::<anyhow::Result<_>>().unwrap();
assets.sort_by(|x, y| x.source.as_str().cmp(y.source.as_str()));
assert_eq!(assets.len(), 2);
assert_eq!(assets[0].source.as_str(), "assets/logo.svg");
assert_eq!(assets[1].source.as_str(), "assets/style.css");
assert_eq!(assets[1].bytes, b"body{}");
}
}