use std::{borrow::Cow, collections::HashMap, fs::File, io, path::Path};
use walkdir::WalkDir;
use crate::{Blog, Metadata, Mime};
use super::{Dir, Mount, Serve, Url};
#[derive(Debug)]
pub struct AsIs {
mount: Mount,
mimes: HashMap<Cow<'static, str>, Mime>,
metafree: bool,
}
impl AsIs {
pub fn new(url: impl Into<Url>, dir: impl Into<Dir>) -> Self {
Self {
mount: Mount::new(url, dir),
mimes: Default::default(),
metafree: false,
}
}
fn get_mime(&self, url: &Url) -> Mime {
let Some(ext) = url.extension().and_then(|t| t.to_str()) else {
return Mime::BYTES;
};
self.mimes.get(ext).cloned().or_else(|| Mime::from_ext(ext)).unwrap_or(Mime::BYTES)
}
pub fn mime(mut self, ext: impl Into<Cow<'static, str>>, mime: Mime) -> Self {
self.mimes.insert(ext.into(), mime);
self
}
pub fn mimes(mut self, addtl: impl Iterator<Item = (Cow<'static, str>, Mime)>) -> Self {
self.mimes.extend(addtl);
self
}
pub fn metafree(mut self) -> Self {
self.metafree = true;
self
}
}
impl Serve for AsIs {
fn base(&self) -> &Url {
&self.mount.url
}
fn scan(&self, dir: &Path) -> Result<Metadata, std::io::Error> {
let mut res = Metadata::default();
let base = dir.join(&*self.mount.dir);
for de in WalkDir::new(&base).follow_links(true) {
let de = de?;
if !de.file_type().is_file() {
continue;
}
let Ok(rel) = de.path().strip_prefix(&base) else {
continue;
};
let url = Url(self.mount.url.join(rel));
let mime = self.get_mime(&url);
if !self.metafree
&& mime == Mime::CSS
&& let Some(name) = url.file_name().and_then(|os| os.to_str())
{
res.stylesheets.insert(name.to_owned(), url.clone());
}
res.files.insert(url, mime);
}
Ok(res)
}
fn render(&self, blog: &Blog, url: &Url, into: &mut dyn std::io::Write) -> io::Result<()> {
let in_dir = self.mount.dir(url).ok_or(io::Error::new(
io::ErrorKind::InvalidInput,
format!("url {:?} not under base {:?}", url, self.mount.url),
))?;
let mut f = File::open(blog.dir.join(&*in_dir))?;
io::copy(&mut f, into)?;
Ok(())
}
}