error_stack/macros.rs
1pub mod __private {
2 #![doc(hidden)]
3 //! Implementation detail for macros.
4 //!
5 //! ⚠️ **Functionality in this module is considered unstable and is subject to change at any
6 //! time without a major version bump!** ⚠️
7 mod specialization {
8 #![allow(clippy::unused_self)]
9 //! [Autoref-Based Stable Specialization](https://github.com/dtolnay/case-studies/blob/master/autoref-specialization/README.md)
10 //! for macros.
11 //!
12 //! This is a stable implementation for specialization (only possible within macros, as
13 //! there is no trait bound for these things).
14 //!
15 //! The different tags [`ReportTag`] and [`ContextTag`] have a blanket implementation
16 //! returning a concrete type. This type is then used to create a [`Report`].
17 //!
18 //! [`ContextTag`] is implemented for `T: `[`Context`]s while [`ReportTag`] is implement for
19 //! [`Report`]s. Calling `my_report.__kind()` will always return a [`Reporter`] while
20 //! `my_context.__kind()` will return a [`ContextReporter`] so a [`Report`] has the highest
21 //! precedence when calling `.__kind()`. This will use an identity function when creating a
22 //! [`Report`] to ensure that no information will be lost.
23 //!
24 //! Note: The methods on the tags are called `__kind` instead of `kind` to avoid misleading
25 //! suggestions from the Rust compiler, when calling `kind`. It would suggest implementing a
26 //! tag for the type which cannot and should not be implemented.
27
28 pub trait ReportTag {
29 #[inline]
30 fn __kind(&self) -> Reporter {
31 Reporter
32 }
33 }
34 impl<T> ReportTag for Report<T> {}
35
36 pub trait ContextTag {
37 #[inline]
38 fn __kind(&self) -> ContextReporter {
39 ContextReporter
40 }
41 }
42 impl<T> ContextTag for &T where T: ?Sized + Context {}
43 use crate::{Context, Report};
44
45 pub struct Reporter;
46 impl Reporter {
47 #[inline]
48 pub const fn report<T>(self, report: Report<T>) -> Report<T> {
49 report
50 }
51 }
52
53 pub struct ContextReporter;
54 impl ContextReporter {
55 #[inline]
56 #[track_caller]
57 pub fn report<C: Context>(self, context: C) -> Report<C> {
58 Report::new(context)
59 }
60 }
61 }
62
63 // Import anonymously to allow calling `__kind` but forbid implementing the tag-traits.
64 pub use self::specialization::{ContextTag as _, ReportTag as _};
65}
66
67/// Creates a [`Report`] from the given parameters.
68///
69/// The parameters may either be [`Context`] or a [`Report`]. The returned [`Report`] will use the
70/// the provided type as context.
71///
72/// [`Report`]: crate::Report
73/// [`Context`]: crate::Context
74/// [`Error`]: core::error::Error
75///
76/// # Examples
77///
78/// Create a [`Report`] from [`Error`]:
79///
80/// ```rust
81/// use std::fs;
82///
83/// use error_stack::report;
84///
85/// # fn wrapper() -> error_stack::Result<(), impl core::fmt::Debug> {
86/// match fs::read_to_string("/path/to/file") {
87/// Ok(content) => println!("file contents: {content}"),
88/// Err(err) => return Err(report!(err)),
89/// }
90/// # Ok(()) }
91/// # assert!(wrapper().unwrap_err().contains::<std::io::Error>());
92/// ```
93///
94/// Create a [`Report`] from [`Context`]:
95///
96/// ```rust
97/// # fn has_permission(_: &u32, _: &u32) -> bool { true }
98/// # type User = u32;
99/// # let user = 0;
100/// # type Resource = u32;
101/// # let resource = 0;
102/// use core::fmt;
103///
104/// use error_stack::{report, Context};
105///
106/// #[derive(Debug)]
107/// # #[allow(dead_code)]
108/// struct PermissionDenied(User, Resource);
109///
110/// impl fmt::Display for PermissionDenied {
111/// # #[allow(unused_variables)]
112/// fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
113/// # const _: &str = stringify! {
114/// ...
115/// # }; Ok(())}
116/// }
117///
118/// impl Context for PermissionDenied {}
119///
120/// if !has_permission(&user, &resource) {
121/// return Err(report!(PermissionDenied(user, resource)));
122/// }
123/// # Ok(())
124/// ```
125#[macro_export]
126macro_rules! report {
127 ($err:expr $(,)?) => {{
128 use $crate::__private::*;
129 let error = $err;
130 (&error).__kind().report(error)
131 }};
132}
133
134/// Creates a [`Report`] and returns it as [`Result`].
135///
136/// Shorthand for `return `Err`(`[`report!(...)`]`)`
137///
138/// [`Report`]: crate::Report
139/// [`report!(...)`]: report
140///
141/// # Examples
142///
143/// Create a [`Report`] from [`Error`]:
144///
145/// [`Error`]: core::error::Error
146///
147/// ```
148/// use std::fs;
149///
150/// use error_stack::bail;
151/// # fn wrapper() -> error_stack::Result<(), impl core::fmt::Debug> {
152/// match fs::read_to_string("/path/to/file") {
153/// Ok(content) => println!("file contents: {content}"),
154/// Err(err) => bail!(err),
155/// }
156/// # Ok(()) }
157/// # assert!(wrapper().unwrap_err().contains::<std::io::Error>());
158/// ```
159///
160/// Create a [`Report`] from [`Context`]:
161///
162/// [`Context`]: crate::Context
163///
164/// ```rust
165/// # fn has_permission(_: &u32, _: &u32) -> bool { true }
166/// # type User = u32;
167/// # let user = 0;
168/// # type Resource = u32;
169/// # let resource = 0;
170/// use core::fmt;
171///
172/// use error_stack::{bail, Context};
173///
174/// #[derive(Debug)]
175/// # #[allow(dead_code)]
176/// struct PermissionDenied(User, Resource);
177///
178/// impl fmt::Display for PermissionDenied {
179/// # #[allow(unused_variables)]
180/// fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
181/// # const _: &str = stringify! {
182/// ...
183/// # }; Ok(())}
184/// }
185///
186/// impl Context for PermissionDenied {}
187///
188/// if !has_permission(&user, &resource) {
189/// bail!(PermissionDenied(user, resource));
190/// }
191/// # Ok(())
192/// ```
193#[macro_export]
194macro_rules! bail {
195 ($err:expr $(,)?) => {{
196 return $crate::Result::Err($crate::report!($err));
197 }};
198}
199
200/// Ensures `$cond` is met, otherwise return an error.
201///
202/// Shorthand for `if !$cond { `[`bail!(...)`]`) }`
203///
204/// [`Report`]: crate::Report
205/// [`bail!(...)`]: bail
206///
207/// # Examples
208///
209/// Create a [`Report`] from [`Context`]:
210///
211/// [`Report`]: crate::Report
212/// [`Context`]: crate::Context
213///
214/// ```rust
215/// # fn has_permission(_: &u32, _: &u32) -> bool { true }
216/// # type User = u32;
217/// # let user = 0;
218/// # type Resource = u32;
219/// # let resource = 0;
220/// # use core::fmt;
221///
222/// use error_stack::{ensure, Context};
223///
224/// #[derive(Debug)]
225/// # #[allow(dead_code)]
226/// struct PermissionDenied(User, Resource);
227///
228/// impl fmt::Display for PermissionDenied {
229/// # #[allow(unused_variables)]
230/// fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
231/// const _: &str = stringify! {
232/// ...
233/// };
234/// Ok(())
235/// }
236/// }
237///
238/// impl Context for PermissionDenied {}
239///
240/// ensure!(
241/// has_permission(&user, &resource),
242/// PermissionDenied(user, resource)
243/// );
244/// # Ok(())
245/// ```
246#[macro_export]
247macro_rules! ensure {
248 ($cond:expr, $err:expr $(,)?) => {{
249 if !bool::from($cond) {
250 $crate::bail!($err)
251 }
252 }};
253}