n0_error/
macros.rs

1/// Constructs an error enum/struct value while automatically filling `meta: Meta`.
2///
3/// - `e!(MyError::Variant)` constructs `MyError::Variant { meta: Meta::default() }`.
4/// - `e!(MyError::Variant, source)` constructs `MyError::Variant { source, meta: Meta::default() }`.
5/// - `e!(MyError::Variant { field: value, other })` constructs `MyError::Variant { field: value, other, meta: Meta::default() }`
6#[macro_export]
7macro_rules! e {
8    // No source, no fields
9    ($($err:tt)::+) => {
10        $($err)::+ { meta: $crate::Meta::default() }
11    };
12
13    // Source, no fields
14    ($($err:tt)::+ , $source:expr) => {
15        $($err)::+ { source: $source, meta: $crate::Meta::default() }
16    };
17
18    // Fields and values plus source
19    ($($err:tt)::+ { $($body:tt)* }, $source:expr) => {
20        $($err)::+ { meta: $crate::Meta::default(), source: $source, $($body)* }
21    };
22
23    // Fields and values
24    ($($err:tt)::+ { $($body:tt)* }) => {
25        $($err)::+ { meta: $crate::Meta::default(), $($body)* }
26    };
27}
28
29/// Reexport `spez` for use in the [`anyerr`] macro.
30#[doc(hidden)]
31pub use spez as __spez;
32
33/// Converts a value into [`AnyError`].
34///
35/// - `anyerr!("msg")` creates an error with just a message.
36/// - `anyerr!("this failed at {a} with {}", b)` creates an error with a formatted message
37/// - `anyerr!(value)` converts any [`impl StackError`], `impl std::error::Error`, or `impl Display` into [`AnyError`].
38/// - `anyerr!(value, "context")` works as above and then adds context via [`AnyError::context`].
39/// - `anyerr!(value, "context {}", foo)` works as above, but with a formatted string as context
40///
41/// The forms that take `value` use *autoref specialization* to keep the most details possible:
42/// if given a [`StackError`] it uses [`AnyError::from_stack`], if given a std error, uses [`AnyError::from_std`],
43/// if given a value that impls `Display` it uses [`AnyError::from_display`] - in this order.
44///
45/// [`AnyError::context`]: crate::AnyError::context
46/// [`AnyError::from_display`]: crate::AnyError::from_display
47/// [`AnyError::from_stack`]: crate::AnyError::from_stack
48/// [`AnyError::from_std`]: crate::AnyError::from_std
49/// [`AnyError`]: crate::AnyError
50/// [`StackError`]: crate::StackError
51/// [`impl StackError`]: crate::StackError
52#[macro_export]
53macro_rules! anyerr {
54    ($fmt:literal$(, $($arg:expr),* $(,)?)?) => {
55        $crate::anyerr!(::std::format!($fmt$(, $($arg),*)*))
56    };
57
58    ($err:expr, $fmt:literal$(, $($arg:expr),* $(,)?)?) => {
59        $crate::anyerr!($err).context(format!($fmt$(, $($arg),*)*))
60    };
61
62    ($err:expr) => {
63        $crate::__spez::spez! {
64            for err = $err;
65            match $crate::AnyError -> $crate::AnyError {
66                err
67            }
68            match<T: $crate::StackError + 'static> T -> $crate::AnyError {
69                $crate::AnyError::from_stack(err)
70            }
71            match<T: ::std::error::Error + Send + Sync + 'static> T -> $crate::AnyError {
72                $crate::AnyError::from_std(err)
73            }
74            match <T: ::std::fmt::Display> T -> $crate::AnyError {
75                $crate::AnyError::from_display(err)
76            }
77        }
78    };
79}
80
81/// Ensures a condition, otherwise returns the error constructed with [`e`] from the remaining args.
82///
83/// This macro takes an expression as its first argument. If the expression evaluates
84/// to `false`, the macro expands to returning an error result.
85///
86/// The error will be constructed by passing all remaining arguments to [`e`].
87/// See its docs for details on accepted forms.
88#[macro_export]
89macro_rules! ensure {
90    ($predicate:expr, $($tt:tt)*) => {
91        if !$predicate {
92            $crate::bail!($($tt)*)
93        }
94    };
95}
96
97/// Ensures a condition, otherwise returns an [`AnyError`].
98///
99/// This macro takes an expression as its first argument. If the expression evaluates
100/// to `false`, the macro expands to returning an error result. The error will be constructed
101/// by passing the remaining arguments after the expression to [`anyerr`]. See its docs for
102/// supported forms.
103///
104/// [`AnyError`]: crate::AnyError
105/// [`anyerr`]: crate::anyerr
106#[macro_export]
107macro_rules! ensure_any {
108    ($cond:expr, $($tt:tt)*) => {
109        if !$cond {
110            $crate::bail_any!($($tt)*)
111        }
112    };
113}
114
115/// Returns an error result by constructing a `StackError` with [`e`].
116///
117/// This macro accepts the same forms as [`e`], but wraps the error into `Err` and
118/// expands to returning the result from the current function.
119#[macro_export]
120macro_rules! bail {
121    ($($tt:tt)*) => {
122        return ::core::result::Result::Err($crate::e!($($tt)*).into())
123    }
124}
125
126/// Returns an error result by constructing an [`AnyError`] with [`anyerr`].
127///
128/// This macro accepts the same forms as [`anyerr`], but wraps the error into `Err` and
129/// expands to returning the result from the current function.
130///
131/// [`AnyError`]: crate::AnyError
132/// [`anyerr`]: crate::anyerr
133#[macro_export]
134macro_rules! bail_any {
135    ($($tt:tt)*) => {
136        return ::core::result::Result::Err($crate::anyerr!($($tt)*).into())
137    }
138}
139
140/// Unwraps a result, returning in the error case while converting the error.
141///
142/// If the result is the error variant, this will construct a new error with [`e`]
143/// that takes the result's error as its source.
144#[macro_export]
145macro_rules! try_or {
146    ($result:expr, $($tt:tt)*) => {
147        match $result {
148            ::core::result::Result::Ok(v) => v,
149            ::core::result::Result::Err(e) => {
150                return ::core::result::Result::Err($crate::e!($($tt)*, e));
151            }
152        }
153    };
154}
155
156/// Unwraps a result, returning in the error case while adding context to the error.
157///
158/// If the result is the error variant, this will construct a new error with [`anyerr`]
159/// from the result's error while providing additional context.
160///
161/// [`anyerr`]: crate::anyerr
162#[macro_export]
163macro_rules! try_or_any {
164    ($result:expr, $($context:tt)*) => {
165        match $result {
166            ::core::result::Result::Ok(v) => v,
167            ::core::result::Result::Err(e) => {
168                return ::core::result::Result::Err($crate::anyerr!(e, $($context)*));
169            }
170        }
171    };
172}