108 lines
3.5 KiB
Rust
108 lines
3.5 KiB
Rust
use std::path::{Path, PathBuf};
|
|
|
|
use tracing::{error, instrument};
|
|
|
|
use super::{Content, Dir, EditPath, Entries, Query, VPath, VPathBuf};
|
|
|
|
#[derive(Debug, Clone)]
|
|
pub struct PhysicalDir {
|
|
root: PathBuf,
|
|
}
|
|
|
|
impl PhysicalDir {
|
|
pub fn new(root: PathBuf) -> Self {
|
|
Self { root }
|
|
}
|
|
|
|
fn entries(&self, vpath: &VPath) -> Vec<VPathBuf> {
|
|
let physical = self.root.join(physical_path(vpath));
|
|
if !physical.is_dir() {
|
|
return vec![];
|
|
}
|
|
|
|
match std::fs::read_dir(physical) {
|
|
Ok(read_dir) => read_dir
|
|
.filter_map(|entry| {
|
|
entry
|
|
.inspect_err(|err| {
|
|
error!(
|
|
"{self:?} error while reading entries: {err:?}",
|
|
)
|
|
})
|
|
.ok()
|
|
.and_then(|entry| {
|
|
let path = entry.path();
|
|
let path_str = match path.strip_prefix(&self.root).unwrap_or(&path).to_str() {
|
|
Some(p) => p,
|
|
None => {
|
|
error!("{self:?} entry {path:?} has invalid UTF-8 (while reading vpath {vpath:?})");
|
|
return None;
|
|
},
|
|
};
|
|
let vpath_buf = VPathBuf::try_new(path_str.replace('\\', "/"))
|
|
.inspect_err(|err| {
|
|
error!("{self:?} error with vpath for {path_str:?}: {err:?}");
|
|
})
|
|
.ok()?;
|
|
Some(vpath_buf)
|
|
})
|
|
})
|
|
.collect(),
|
|
Err(err) => {
|
|
error!(
|
|
"{self:?} cannot read vpath {vpath:?}: {err:?}",
|
|
);
|
|
vec![]
|
|
}
|
|
}
|
|
}
|
|
|
|
#[instrument("PhysicalDir::content", skip(self))]
|
|
fn content(&self, path: &VPath) -> Option<Content> {
|
|
let physical_path = self.root.join(physical_path(path));
|
|
std::fs::read(&physical_path)
|
|
.inspect_err(|err| error!("{self:?} cannot read file at vpath {path:?} / physical {physical_path:?}: {err:?}",))
|
|
.ok()
|
|
.map(|bytes| {
|
|
Content::new(
|
|
path.extension()
|
|
.and_then(guess_content_type)
|
|
.unwrap_or("text/plain"),
|
|
bytes,
|
|
)
|
|
})
|
|
}
|
|
|
|
fn edit_path(&self, path: &VPath) -> EditPath {
|
|
EditPath {
|
|
path: self.root.join(physical_path(path)),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl Dir for PhysicalDir {
|
|
fn query(&self, path: &VPath, query: &mut Query) {
|
|
query.provide(|| Entries(self.entries(path)));
|
|
query.try_provide(|| self.content(path));
|
|
query.provide(|| self.edit_path(path));
|
|
}
|
|
}
|
|
|
|
fn physical_path(path: &VPath) -> &Path {
|
|
Path::new(path.as_str())
|
|
}
|
|
|
|
fn guess_content_type(extension: &str) -> Option<&'static str> {
|
|
match extension {
|
|
"html" => Some("text/html"),
|
|
"js" => Some("text/javascript"),
|
|
"css" => Some("text/css"),
|
|
"woff" => Some("font/woff2"),
|
|
"svg" => Some("image/svg+xml"),
|
|
"atom" => Some("application/atom+xml"),
|
|
"png" => Some("image/png"),
|
|
"webp" => Some("image/webp"),
|
|
"jpg" | "jpeg" => Some("image/jpeg"),
|
|
_ => None,
|
|
}
|
|
}
|