http_types_2/
error.rs

1//! HTTP error types
2
3use std::error::Error as StdError;
4use std::fmt::{self, Debug, Display};
5
6use crate::StatusCode;
7use std::convert::TryInto;
8
9/// A specialized `Result` type for HTTP operations.
10///
11/// This type is broadly used across `http_types` for any operation which may
12/// produce an error.
13pub type Result<T> = std::result::Result<T, Error>;
14
15/// The error type for HTTP operations.
16pub struct Error {
17    error: anyhow::Error,
18    status: crate::StatusCode,
19    type_name: Option<&'static str>,
20}
21
22#[allow(unreachable_pub)]
23#[derive(Debug)]
24#[doc(hidden)]
25pub struct BacktracePlaceholder;
26
27impl Display for BacktracePlaceholder {
28    fn fmt(&self, _: &mut fmt::Formatter<'_>) -> fmt::Result {
29        unreachable!()
30    }
31}
32
33impl Error {
34    /// Create a new error object from any error type.
35    ///
36    /// The error type must be threadsafe and 'static, so that the Error will be
37    /// as well. If the error type does not provide a backtrace, a backtrace will
38    /// be created here to ensure that a backtrace exists.
39    pub fn new<S, E>(status: S, error: E) -> Self
40    where
41        S: TryInto<StatusCode>,
42        S::Error: Debug,
43        E: Into<anyhow::Error>,
44    {
45        Self {
46            status: status
47                .try_into()
48                .expect("Could not convert into a valid `StatusCode`"),
49            error: error.into(),
50            type_name: Some(std::any::type_name::<E>()),
51        }
52    }
53
54    /// Create a new error object from static string.
55    pub fn from_str<S, M>(status: S, msg: M) -> Self
56    where
57        S: TryInto<StatusCode>,
58        S::Error: Debug,
59        M: Display + Debug + Send + Sync + 'static,
60    {
61        Self {
62            status: status
63                .try_into()
64                .expect("Could not convert into a valid `StatusCode`"),
65            error: anyhow::Error::msg(msg),
66            type_name: None,
67        }
68    }
69    /// Create a new error from a message.
70    pub(crate) fn new_adhoc<M>(message: M) -> Error
71    where
72        M: Display + Debug + Send + Sync + 'static,
73    {
74        Self::from_str(StatusCode::InternalServerError, message)
75    }
76
77    /// Get the status code associated with this error.
78    pub fn status(&self) -> StatusCode {
79        self.status
80    }
81
82    /// Set the status code associated with this error.
83    pub fn set_status<S>(&mut self, status: S)
84    where
85        S: TryInto<StatusCode>,
86        S::Error: Debug,
87    {
88        self.status = status
89            .try_into()
90            .expect("Could not convert into a valid `StatusCode`");
91    }
92
93    /// Get the backtrace for this Error.
94    ///
95    /// Backtraces are only available on the nightly channel. Tracking issue:
96    /// [rust-lang/rust#53487][tracking].
97    ///
98    /// In order for the backtrace to be meaningful, the environment variable
99    /// `RUST_LIB_BACKTRACE=1` must be defined. Backtraces are somewhat
100    /// expensive to capture in Rust, so we don't necessarily want to be
101    /// capturing them all over the place all the time.
102    ///
103    /// [tracking]: https://github.com/rust-lang/rust/issues/53487
104    ///
105    /// Note: This function can be called whether or not backtraces
106    /// are enabled and available. It will return a `None` variant if
107    /// compiled on a toolchain that does not support backtraces, or
108    /// if executed without backtraces enabled with
109    /// `RUST_LIB_BACKTRACE=1`.
110    #[allow(missing_docs)]
111    pub const fn backtrace(&self) -> Option<BacktracePlaceholder> {
112        None
113    }
114
115    /// Returns the inner [`anyhow::Error`]
116    /// Note: This will lose status code information
117    pub fn into_inner(self) -> anyhow::Error {
118        self.error
119    }
120
121    /// Attempt to downcast the error object to a concrete type.
122    pub fn downcast<E>(self) -> std::result::Result<E, Self>
123    where
124        E: Display + Debug + Send + Sync + 'static,
125    {
126        if self.error.downcast_ref::<E>().is_some() {
127            Ok(self.error.downcast().unwrap())
128        } else {
129            Err(self)
130        }
131    }
132
133    /// Downcast this error object by reference.
134    pub fn downcast_ref<E>(&self) -> Option<&E>
135    where
136        E: Display + Debug + Send + Sync + 'static,
137    {
138        self.error.downcast_ref::<E>()
139    }
140
141    /// Downcast this error object by mutable reference.
142    pub fn downcast_mut<E>(&mut self) -> Option<&mut E>
143    where
144        E: Display + Debug + Send + Sync + 'static,
145    {
146        self.error.downcast_mut::<E>()
147    }
148
149    /// Retrieves a reference to the type name of the error, if available.
150    pub fn type_name(&self) -> Option<&str> {
151        self.type_name
152    }
153
154    /// Converts anything which implements `Display` into an `http_types::Error`.
155    ///
156    /// This is handy for errors which are not `Send + Sync + 'static` because `std::error::Error` requires `Display`.
157    /// Note that any assiciated context not included in the `Display` output will be lost,
158    /// and so this may be lossy for some types which implement `std::error::Error`.
159    ///
160    /// **Note: Prefer `error.into()` via `From<Into<anyhow::Error>>` when possible!**
161    pub fn from_display<D: Display>(error: D) -> Self {
162        anyhow::Error::msg(error.to_string()).into()
163    }
164
165    /// Converts anything which implements `Debug` into an `http_types::Error`.
166    ///
167    /// This is handy for errors which are not `Send + Sync + 'static` because `std::error::Error` requires `Debug`.
168    /// Note that any assiciated context not included in the `Debug` output will be lost,
169    /// and so this may be lossy for some types which implement `std::error::Error`.
170    ///
171    /// **Note: Prefer `error.into()` via `From<Into<anyhow::Error>>` when possible!**
172    pub fn from_debug<D: Debug>(error: D) -> Self {
173        anyhow::Error::msg(format!("{error:?}")).into()
174    }
175}
176
177impl Display for Error {
178    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
179        Display::fmt(&self.error, formatter)
180    }
181}
182
183impl Debug for Error {
184    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
185        Debug::fmt(&self.error, formatter)
186    }
187}
188
189impl<E: Into<anyhow::Error>> From<E> for Error {
190    fn from(error: E) -> Self {
191        Self::new(StatusCode::InternalServerError, error)
192    }
193}
194
195impl AsRef<dyn StdError + Send + Sync> for Error {
196    fn as_ref(&self) -> &(dyn StdError + Send + Sync + 'static) {
197        self.error.as_ref()
198    }
199}
200
201impl AsRef<StatusCode> for Error {
202    fn as_ref(&self) -> &StatusCode {
203        &self.status
204    }
205}
206
207impl AsMut<StatusCode> for Error {
208    fn as_mut(&mut self) -> &mut StatusCode {
209        &mut self.status
210    }
211}
212
213impl AsRef<dyn StdError> for Error {
214    fn as_ref(&self) -> &(dyn StdError + 'static) {
215        self.error.as_ref()
216    }
217}
218
219impl From<Error> for Box<dyn StdError + Send + Sync + 'static> {
220    fn from(error: Error) -> Self {
221        error.error.into()
222    }
223}
224
225impl From<Error> for Box<dyn StdError + 'static> {
226    fn from(error: Error) -> Self {
227        Box::<dyn StdError + Send + Sync>::from(error.error)
228    }
229}
230
231impl AsRef<anyhow::Error> for Error {
232    fn as_ref(&self) -> &anyhow::Error {
233        &self.error
234    }
235}