http_endpoint/
error.rs

1// Copyright (C) 2020-2024 Daniel Mueller <deso@posteo.net>
2// SPDX-License-Identifier: GPL-3.0-or-later
3
4use std::error::Error as StdError;
5use std::fmt::Display;
6use std::fmt::Formatter;
7use std::fmt::Result as FmtResult;
8use std::str::from_utf8;
9
10use http::Error as HttpError;
11use http::StatusCode as HttpStatusCode;
12
13
14/// An error type that any endpoint related error can be converted into.
15///
16/// Please note that this error type necessarily looses some information
17/// over dealing with the actual endpoint error type.
18#[derive(Debug)]
19pub enum Error<B>
20where
21  B: StdError,
22{
23  /// An HTTP related error.
24  Http(HttpError),
25  /// We encountered an HTTP that either represents a failure or is not
26  /// supported.
27  HttpStatus(HttpStatusCode, Vec<u8>),
28  /// Some kind of conversion error was encountered.
29  Conversion(B),
30}
31
32impl<B> Display for Error<B>
33where
34  B: StdError,
35{
36  fn fmt(&self, fmt: &mut Formatter<'_>) -> FmtResult {
37    match self {
38      Error::Http(err) => write!(fmt, "{}", err),
39      Error::HttpStatus(status, data) => {
40        write!(fmt, "HTTP status: {}: ", status)?;
41        match from_utf8(data) {
42          Ok(s) => fmt.write_str(s)?,
43          Err(b) => write!(fmt, "{:?}", b)?,
44        }
45        Ok(())
46      },
47      Error::Conversion(err) => write!(fmt, "{}", err),
48    }
49  }
50}
51
52impl<B> StdError for Error<B>
53where
54  B: StdError,
55{
56  fn source(&self) -> Option<&(dyn StdError + 'static)> {
57    match self {
58      Error::Http(err) => err.source(),
59      Error::HttpStatus(..) => None,
60      Error::Conversion(err) => err.source(),
61    }
62  }
63}
64
65impl<B> From<HttpError> for Error<B>
66where
67  B: StdError,
68{
69  fn from(e: HttpError) -> Self {
70    Error::Http(e)
71  }
72}
73
74
75#[cfg(test)]
76mod tests {
77  use super::*;
78
79
80  /// Check behavior of error related functionality.
81  #[test]
82  fn error() {
83    let invalid_status = HttpStatusCode::from_u16(u16::MAX).unwrap_err();
84    let err = Error::<HttpError>::from(HttpError::from(invalid_status));
85    assert_ne!(err.to_string(), "");
86    let src = err.source();
87    assert!(src.is_none(), "{src:?}");
88
89    let invalid_status = HttpStatusCode::from_u16(u16::MAX).unwrap_err();
90    let err = Error::Conversion(HttpError::from(invalid_status));
91    assert_ne!(err.to_string(), "");
92    let src = err.source();
93    assert!(src.is_none(), "{src:?}");
94
95    let err = Error::<HttpError>::HttpStatus(HttpStatusCode::NOT_FOUND, Vec::new());
96    assert_ne!(err.to_string(), "");
97    let src = err.source();
98    assert!(src.is_none(), "{src:?}");
99
100    let err = Error::<HttpError>::HttpStatus(HttpStatusCode::NOT_FOUND, vec![0, 159, 146, 150]);
101    assert_ne!(err.to_string(), "");
102  }
103}