1use core::fmt;
2
3use std::{error, io};
4
5use http::{
6 header::{ALLOW, CONTENT_RANGE},
7 request::Parts,
8 HeaderValue, Request, Response, StatusCode,
9};
10
11use super::buf::buf_write_header;
12
13#[derive(Debug)]
19pub enum ServeError {
20 MethodNotAllowed,
22 InvalidPath,
24 NotModified,
26 PreconditionFailed,
28 RangeNotSatisfied(u64),
30 NotFound,
32 Io(io::Error),
34}
35
36impl ServeError {
37 pub fn into_response_from<Ext>(self, req: Request<Ext>) -> Response<()> {
55 let (
56 Parts {
57 mut headers,
58 extensions,
59 ..
60 },
61 _,
62 ) = req.into_parts();
63 headers.clear();
64
65 let mut res = Response::new(());
66 *res.headers_mut() = headers;
67 *res.extensions_mut() = extensions;
68
69 self._into_response(res)
70 }
71
72 #[inline]
74 pub fn into_response(self) -> Response<()> {
75 self._into_response(Response::new(()))
76 }
77
78 fn _into_response(self, mut res: Response<()>) -> Response<()> {
79 match self {
80 Self::MethodNotAllowed => {
81 *res.status_mut() = StatusCode::METHOD_NOT_ALLOWED;
82 res.headers_mut().insert(ALLOW, HeaderValue::from_static("GET,HEAD"));
83 }
84 Self::InvalidPath => *res.status_mut() = StatusCode::BAD_REQUEST,
85 Self::NotModified => *res.status_mut() = StatusCode::NOT_MODIFIED,
86 Self::PreconditionFailed => *res.status_mut() = StatusCode::PRECONDITION_FAILED,
87 Self::RangeNotSatisfied(size) => {
88 *res.status_mut() = StatusCode::RANGE_NOT_SATISFIABLE;
89 let val = buf_write_header!(0, "bytes */{size}");
90 res.headers_mut().insert(CONTENT_RANGE, val);
91 }
92 Self::NotFound => *res.status_mut() = StatusCode::NOT_FOUND,
93 Self::Io(_) => *res.status_mut() = StatusCode::INTERNAL_SERVER_ERROR,
94 }
95 res
96 }
97}
98
99impl fmt::Display for ServeError {
100 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
101 match *self {
102 Self::MethodNotAllowed => f.write_str("request method not allowed"),
103 Self::InvalidPath => f.write_str("file path is not valid"),
104 Self::NotModified => f.write_str("file has not been modified"),
105 Self::PreconditionFailed => f.write_str("precondition failed. file has been modified"),
106 Self::RangeNotSatisfied(size) => write!(f, "range is out of bound. max range of file is {size}"),
107 Self::NotFound => f.write_str("file can not be found"),
108 Self::Io(ref e) => fmt::Display::fmt(e, f),
109 }
110 }
111}
112
113impl error::Error for ServeError {}
114
115impl From<io::Error> for ServeError {
116 fn from(e: io::Error) -> Self {
117 match e.kind() {
118 io::ErrorKind::NotFound => Self::NotFound,
119 io::ErrorKind::PermissionDenied => Self::InvalidPath,
120 _ => Self::Io(e),
121 }
122 }
123}
124
125#[cfg(test)]
126mod test {
127 use super::*;
128
129 #[test]
130 fn response() {
131 assert_eq!(
132 ServeError::RangeNotSatisfied(128)
133 .into_response()
134 .headers_mut()
135 .remove(CONTENT_RANGE)
136 .unwrap(),
137 HeaderValue::from_static("bytes */128")
138 )
139 }
140}