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 {}