error_stack/
context.rs

1#![expect(deprecated, reason = "We use `Context` to maintain compatibility")]
2
3use alloc::string::{String, ToString as _};
4#[cfg(nightly)]
5use core::error::Request;
6use core::{error::Error, fmt};
7
8use crate::Report;
9
10/// Defines the current context of a [`Report`].
11///
12/// When in a `std` environment or on a nightly toolchain, every [`Error`] is a valid `Context`.
13/// This trait is not limited to [`Error`]s and can also be manually implemented on a type.
14///
15/// ## Example
16///
17/// Used for creating a [`Report`] or for switching the [`Report`]'s context:
18///
19/// ```rust
20/// use std::{error::Error, fmt, fs, io};
21///
22/// use error_stack::{ResultExt, Report};
23///
24/// # type Config = ();
25/// #[derive(Debug)]
26/// pub enum ConfigError {
27///     ParseError,
28/// }
29///
30/// impl fmt::Display for ConfigError {
31///     # #[allow(unused_variables)] // `fmt` is not used in this example
32///     fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
33///         # const _: &str = stringify! {
34///         ...
35///         # }; Ok(())
36///     }
37/// }
38///
39/// // In this scenario, `Error` is not implemented for `ConfigError` for some reason, so implement
40/// // `Context` manually.
41/// impl Error for ConfigError {}
42///
43/// pub fn read_file(path: &str) -> Result<String, Report<io::Error>> {
44///     // Creates a `Report` from `io::Error`, the current context is `io::Error`
45///     fs::read_to_string(path).map_err(Report::from)
46/// }
47///
48/// pub fn parse_config(path: &str) -> Result<Config, Report<ConfigError>> {
49///     // The return type of `parse_config` requires another context. By calling `change_context`
50///     // the context may be changed.
51///     read_file(path).change_context(ConfigError::ParseError)?;
52///
53///     # const _: &str = stringify! {
54///     ...
55///     # }; Ok(())
56/// }
57/// # let err = parse_config("invalid-path").unwrap_err();
58/// # assert!(err.contains::<io::Error>());
59/// # assert!(err.contains::<ConfigError>());
60/// ```
61#[deprecated(note = "Use `core::error::Error` instead", since = "0.6.0")]
62pub trait Context: fmt::Display + fmt::Debug + Send + Sync + 'static {
63    /// Provide values which can then be requested by [`Report`].
64    #[cfg(nightly)]
65    #[expect(unused_variables)]
66    fn provide<'a>(&'a self, request: &mut Request<'a>) {}
67
68    /// Returns the source of the error, if any.
69    ///
70    /// This method only exists to avoid the requirement of specialization and to get the sources
71    /// for `Error`.
72    #[doc(hidden)]
73    fn __source(&self) -> Option<&(dyn Error + 'static)> {
74        None
75    }
76}
77
78/// Captures an error message as the context of a [`Report`].
79pub(crate) struct SourceContext(String);
80
81impl SourceContext {
82    pub(crate) fn from_error(value: &dyn Error) -> Self {
83        Self(value.to_string())
84    }
85}
86
87impl fmt::Debug for SourceContext {
88    fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
89        fmt::Debug::fmt(&self.0, fmt)
90    }
91}
92
93impl fmt::Display for SourceContext {
94    fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
95        fmt::Display::fmt(&self.0, fmt)
96    }
97}
98
99impl Context for SourceContext {}
100
101impl<C> From<C> for Report<C>
102where
103    C: Context,
104{
105    #[track_caller]
106    #[inline]
107    fn from(context: C) -> Self {
108        Self::new(context)
109    }
110}
111
112impl<C: Error + Send + Sync + 'static> Context for C {
113    #[cfg(nightly)]
114    fn provide<'a>(&'a self, request: &mut Request<'a>) {
115        Error::provide(self, request);
116    }
117
118    #[doc(hidden)]
119    #[inline]
120    fn __source(&self) -> Option<&(dyn Error + 'static)> {
121        self.source()
122    }
123}
124
125/// A trait for types that can be attached to a [`Report`] without being displayed.
126pub trait OpaqueAttachment: Send + Sync + 'static {}
127
128impl<T: Send + Sync + 'static> OpaqueAttachment for T {}
129
130/// A trait for types that can be attached to a [`Report`] and displayed.
131#[diagnostic::on_unimplemented(
132    message = "to attach this type to a `Report` it must implement `fmt::Display` and `fmt::Debug`",
133    note = "if you want to attach a type that is not printable, use `attach_opaque` instead"
134)]
135pub trait Attachment: OpaqueAttachment + fmt::Display + fmt::Debug {}
136
137impl<T: OpaqueAttachment + fmt::Display + fmt::Debug> Attachment for T {}