use crate::util::{open_with_metadata, RequestedPath};
use http::{Method, Request};
use mime_guess::{Mime, MimeGuess};
use std::fs::Metadata;
use std::io::{Error as IoError, ErrorKind as IoErrorKind};
use std::path::PathBuf;
use tokio::fs::File;
#[derive(Debug)]
pub enum ResolveResult {
MethodNotMatched,
NotFound,
PermissionDenied,
IsDirectory,
Found(File, Metadata, Mime),
}
fn map_open_err(err: IoError) -> Result<ResolveResult, IoError> {
match err.kind() {
IoErrorKind::NotFound => Ok(ResolveResult::NotFound),
IoErrorKind::PermissionDenied => Ok(ResolveResult::PermissionDenied),
_ => Err(err),
}
}
pub async fn resolve<B>(
root: impl Into<PathBuf>,
req: &Request<B>,
) -> Result<ResolveResult, IoError> {
match *req.method() {
Method::HEAD | Method::GET => {}
_ => {
return Ok(ResolveResult::MethodNotMatched);
}
}
resolve_path(root, req.uri().path()).await
}
pub async fn resolve_path(
root: impl Into<PathBuf>,
request_path: &str,
) -> Result<ResolveResult, IoError> {
let RequestedPath {
sanitized,
is_dir_request,
} = RequestedPath::resolve(request_path);
let mut full_path = root.into();
full_path.extend(&sanitized);
let (file, metadata) = match open_with_metadata(&full_path).await {
Ok(pair) => pair,
Err(err) => return map_open_err(err),
};
if is_dir_request && !metadata.is_dir() {
return Ok(ResolveResult::NotFound);
}
if !is_dir_request && metadata.is_dir() {
return Ok(ResolveResult::IsDirectory);
}
if !is_dir_request {
let mime = MimeGuess::from_path(&full_path).first_or_octet_stream();
return Ok(ResolveResult::Found(file, metadata, mime));
}
full_path.push("index.html");
let (file, metadata) = match open_with_metadata(&full_path).await {
Ok(pair) => pair,
Err(err) => return map_open_err(err),
};
if metadata.is_dir() {
return Ok(ResolveResult::NotFound);
}
let mime = MimeGuess::from_path(full_path).first_or_octet_stream();
Ok(ResolveResult::Found(file, metadata, mime))
}