error_stack/
context.rs

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