use core::fmt;
use std::{error, io};
use http::{
header::{ALLOW, CONTENT_RANGE},
request::Parts,
HeaderValue, Request, Response, StatusCode,
};
use super::buf::buf_write_header;
#[derive(Debug)]
pub enum ServeError {
MethodNotAllowed,
InvalidPath,
NotModified,
PreconditionFailed,
RangeNotSatisfied(u64),
NotFound,
Io(io::Error),
}
impl ServeError {
pub fn into_response_from<Ext>(self, req: Request<Ext>) -> Response<()> {
let (
Parts {
mut headers,
extensions,
..
},
_,
) = req.into_parts();
headers.clear();
let mut res = Response::new(());
*res.headers_mut() = headers;
*res.extensions_mut() = extensions;
self._into_response(res)
}
#[inline]
pub fn into_response(self) -> Response<()> {
self._into_response(Response::new(()))
}
fn _into_response(self, mut res: Response<()>) -> Response<()> {
match self {
Self::MethodNotAllowed => {
*res.status_mut() = StatusCode::METHOD_NOT_ALLOWED;
res.headers_mut().insert(ALLOW, HeaderValue::from_static("GET,HEAD"));
}
Self::InvalidPath => *res.status_mut() = StatusCode::BAD_REQUEST,
Self::NotModified => *res.status_mut() = StatusCode::NOT_MODIFIED,
Self::PreconditionFailed => *res.status_mut() = StatusCode::PRECONDITION_FAILED,
Self::RangeNotSatisfied(size) => {
*res.status_mut() = StatusCode::RANGE_NOT_SATISFIABLE;
let val = buf_write_header!(0, "bytes */{size}");
res.headers_mut().insert(CONTENT_RANGE, val);
}
Self::NotFound => *res.status_mut() = StatusCode::NOT_FOUND,
Self::Io(_) => *res.status_mut() = StatusCode::INTERNAL_SERVER_ERROR,
}
res
}
}
impl fmt::Display for ServeError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match *self {
Self::MethodNotAllowed => f.write_str("request method not allowed"),
Self::InvalidPath => f.write_str("file path is not valid"),
Self::NotModified => f.write_str("file has not been modified"),
Self::PreconditionFailed => f.write_str("precondition failed. file has been modified"),
Self::RangeNotSatisfied(size) => write!(f, "range is out of bound. max range of file is {size}"),
Self::NotFound => f.write_str("file can not be found"),
Self::Io(ref e) => fmt::Display::fmt(e, f),
}
}
}
impl error::Error for ServeError {}
impl From<io::Error> for ServeError {
fn from(e: io::Error) -> Self {
match e.kind() {
io::ErrorKind::NotFound => Self::NotFound,
io::ErrorKind::PermissionDenied => Self::InvalidPath,
_ => Self::Io(e),
}
}
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn response() {
assert_eq!(
ServeError::RangeNotSatisfied(128)
.into_response()
.headers_mut()
.remove(CONTENT_RANGE)
.unwrap(),
HeaderValue::from_static("bytes */128")
)
}
}