1use base_scheduler::{Cancelled, OperationError};
5use std::{fmt, io};
6
7#[derive(Debug)]
9pub enum Error {
10 Io(io::Error),
11 Cancelled,
12 Invalid(&'static str),
13 Limit(&'static str),
14 Truncated,
15 LengthMismatch,
16 Body(Box<dyn std::error::Error + Send + Sync>),
17}
18
19impl fmt::Display for Error {
20 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
21 match self {
22 Self::Io(_) => f.write_str("HTTP/1 connection I/O failed"),
23 Self::Cancelled => f.write_str("HTTP exchange cancelled"),
24 Self::Invalid(reason) => write!(f, "invalid HTTP/1 message: {reason}"),
25 Self::Limit(reason) => write!(f, "HTTP/1 limit exceeded: {reason}"),
26 Self::Truncated => f.write_str("truncated HTTP/1 message"),
27 Self::LengthMismatch => f.write_str("body does not match its declared length"),
28 Self::Body(_) => f.write_str("HTTP/1 body failed"),
29 }
30 }
31}
32impl std::error::Error for Error {
33 fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
34 match self {
35 Self::Io(error) => Some(error),
36 Self::Body(error) => Some(error.as_ref()),
37 _ => None,
38 }
39 }
40}
41impl From<io::Error> for Error {
42 fn from(error: io::Error) -> Self {
43 Self::Io(error)
44 }
45}
46impl From<Cancelled> for Error {
47 fn from(_: Cancelled) -> Self {
48 Self::Cancelled
49 }
50}
51impl From<OperationError> for Error {
52 fn from(error: OperationError) -> Self {
53 match error {
54 OperationError::Cancelled(_) => Self::Cancelled,
55 OperationError::Io(error) => Self::Io(error),
56 }
57 }
58}
59impl From<base_http::FieldError> for Error {
60 fn from(error: base_http::FieldError) -> Self {
61 Self::Body(Box::new(error))
62 }
63}
64
65#[cfg(test)]
66mod tests {
67 use super::*;
68 use std::error::Error as _;
69
70 #[test]
71 fn wrapping_variants_describe_their_own_layer() {
72 let error = Error::Io(io::Error::from(io::ErrorKind::ConnectionRefused));
73 assert_eq!(error.to_string(), "HTTP/1 connection I/O failed");
74 assert_eq!(error.source().unwrap().to_string(), "connection refused");
75
76 let error = Error::from(base_http::FieldError::InvalidTrailer("host".to_owned()));
77 assert_eq!(error.to_string(), "HTTP/1 body failed");
78 assert!(error.source().is_some());
79 }
80
81 #[test]
82 fn leaf_variants_describe_the_failure_without_a_source() {
83 for (error, message) in [
84 (Error::Cancelled, "HTTP exchange cancelled"),
85 (
86 Error::Invalid("missing host"),
87 "invalid HTTP/1 message: missing host",
88 ),
89 (
90 Error::Limit("message head"),
91 "HTTP/1 limit exceeded: message head",
92 ),
93 (Error::Truncated, "truncated HTTP/1 message"),
94 (
95 Error::LengthMismatch,
96 "body does not match its declared length",
97 ),
98 ] {
99 assert_eq!(error.to_string(), message);
100 assert!(error.source().is_none());
101 }
102 }
103}