use std::path::{Component, Path, PathBuf};
use async_trait::async_trait;
use crate::core::router::Handler;
use crate::core::{Request, Response};
pub struct ServeDir {
root: PathBuf,
}
impl ServeDir {
pub fn new<P: Into<PathBuf>>(root: P) -> Self {
Self { root: root.into() }
}
fn sanitize(rel: &str) -> PathBuf {
let mut out = PathBuf::new();
for comp in Path::new(rel).components() {
if let Component::Normal(s) = comp {
out.push(s)
}
}
out
}
}
#[async_trait]
impl Handler for ServeDir {
async fn handle(&self, req: Request) -> Response {
let mut full = match req.param("path") {
Some(rel) if !rel.is_empty() => {
let safe = Self::sanitize(rel);
self.root.join(safe)
}
_ => self.root.join("index.html"),
};
if let Ok(meta) = tokio::fs::metadata(&full).await
&& meta.is_dir()
{
full = full.join("index.html");
}
match tokio::fs::metadata(&full).await {
Ok(meta) if meta.is_file() => Response::stream_file(200, &full),
_ => Response::text(404, "Not Found"),
}
}
}