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