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