miette/eyreish/
macros.rs

1/// Return early with an error.
2///
3/// This macro is equivalent to `return Err(From::from($err))`.
4///
5/// # Example
6///
7/// ```
8/// # use miette::{bail, Result};
9/// #
10/// # fn has_permission(user: usize, resource: usize) -> bool {
11/// #     true
12/// # }
13/// #
14/// # fn main() -> Result<()> {
15/// #     let user = 0;
16/// #     let resource = 0;
17/// #
18/// if !has_permission(user, resource) {
19#[cfg_attr(
20    not(feature = "no-format-args-capture"),
21    doc = r#"     bail!("permission denied for accessing {resource}");"#
22)]
23#[cfg_attr(
24    feature = "no-format-args-capture",
25    doc = r#"     bail!("permission denied for accessing {}", resource);"#
26)]
27/// }
28/// #     Ok(())
29/// # }
30/// ```
31///
32/// ```
33/// # use miette::{bail, Result};
34/// # use thiserror::Error;
35/// #
36/// # const MAX_DEPTH: usize = 1;
37/// #
38/// #[derive(Error, Debug)]
39/// enum ScienceError {
40///     #[error("recursion limit exceeded")]
41///     RecursionLimitExceeded,
42///     # #[error("...")]
43///     # More = (stringify! {
44///     ...
45///     # }, 1).1,
46/// }
47///
48/// # fn main() -> Result<()> {
49/// #     let depth = 0;
50/// #     let err: &'static dyn std::error::Error = &ScienceError::RecursionLimitExceeded;
51/// #
52/// if depth > MAX_DEPTH {
53///     bail!(ScienceError::RecursionLimitExceeded);
54/// }
55/// #     Ok(())
56/// # }
57/// ```
58///
59/// ```
60/// use miette::{bail, Result, Severity};
61///
62/// fn divide(x: f64, y: f64) -> Result<f64> {
63///     if y.abs() < 1e-3 {
64///         bail!(
65///             severity = Severity::Warning,
66#[cfg_attr(
67    not(feature = "no-format-args-capture"),
68    doc = r#"             "dividing by value ({y}) close to 0""#
69)]
70#[cfg_attr(
71    feature = "no-format-args-capture",
72    doc = r#"             "dividing by value ({}) close to 0", y"#
73)]
74///         );
75///     }
76///     Ok(x / y)
77/// }
78/// ```
79#[macro_export]
80macro_rules! bail {
81    ($($key:ident = $value:expr,)* $fmt:literal $($arg:tt)*) => {
82        return $crate::private::Err(
83            $crate::miette!($($key = $value,)* $fmt $($arg)*)
84        );
85    };
86    ($err:expr $(,)?) => {
87        return $crate::private::Err($crate::miette!($err));
88    };
89}
90
91/// Return early with an error if a condition is not satisfied.
92///
93/// This macro is equivalent to `if !$cond { return Err(From::from($err)); }`.
94///
95/// Analogously to `assert!`, `ensure!` takes a condition and exits the function
96/// if the condition fails. Unlike `assert!`, `ensure!` returns an `Error`
97/// rather than panicking.
98///
99/// # Example
100///
101/// ```
102/// # use miette::{ensure, Result};
103/// #
104/// # fn main() -> Result<()> {
105/// #     let user = 0;
106/// #
107/// ensure!(user == 0, "only user 0 is allowed");
108/// #     Ok(())
109/// # }
110/// ```
111///
112/// ```
113/// # use miette::{ensure, Result};
114/// # use thiserror::Error;
115/// #
116/// # const MAX_DEPTH: usize = 1;
117/// #
118/// #[derive(Error, Debug)]
119/// enum ScienceError {
120///     #[error("recursion limit exceeded")]
121///     RecursionLimitExceeded,
122///     # #[error("...")]
123///     # More = (stringify! {
124///     ...
125///     # }, 1).1,
126/// }
127///
128/// # fn main() -> Result<()> {
129/// #     let depth = 0;
130/// #
131/// ensure!(depth <= MAX_DEPTH, ScienceError::RecursionLimitExceeded);
132/// #     Ok(())
133/// # }
134/// ```
135///
136/// ```
137/// use miette::{ensure, Result, Severity};
138///
139/// fn divide(x: f64, y: f64) -> Result<f64> {
140///     ensure!(
141///         y.abs() >= 1e-3,
142///         severity = Severity::Warning,
143#[cfg_attr(
144    not(feature = "no-format-args-capture"),
145    doc = r#"             "dividing by value ({y}) close to 0""#
146)]
147#[cfg_attr(
148    feature = "no-format-args-capture",
149    doc = r#"             "dividing by value ({}) close to 0", y"#
150)]
151///     );
152///     Ok(x / y)
153/// }
154/// ```
155#[macro_export]
156macro_rules! ensure {
157    ($cond:expr, $($key:ident = $value:expr,)* $fmt:literal $($arg:tt)*) => {
158        if !$cond {
159            return $crate::private::Err(
160                $crate::miette!($($key = $value,)* $fmt $($arg)*)
161            );
162        }
163    };
164    ($cond:expr, $err:expr $(,)?) => {
165        if !$cond {
166            return $crate::private::Err($crate::miette!($err));
167        }
168    };
169}
170
171/// Construct an ad-hoc [`Report`].
172///
173/// # Examples
174///
175/// With string literal and interpolation:
176/// ```
177/// # use miette::miette;
178/// let x = 1;
179/// let y = 2;
180#[cfg_attr(
181    not(feature = "no-format-args-capture"),
182    doc = r#"let report = miette!("{x} + {} = {z}", y, z = x + y);"#
183)]
184#[cfg_attr(
185    feature = "no-format-args-capture",
186    doc = r#"let report = miette!("{} + {} = {z}", x, y, z = x + y);"#
187)]
188///
189/// assert_eq!(report.to_string().as_str(), "1 + 2 = 3");
190///
191/// let z = x + y;
192#[cfg_attr(
193    not(feature = "no-format-args-capture"),
194    doc = r#"let report = miette!("{x} + {y} = {z}");"#
195)]
196#[cfg_attr(
197    feature = "no-format-args-capture",
198    doc = r#"let report = miette!("{} + {} = {}", x, y, z);"#
199)]
200/// assert_eq!(report.to_string().as_str(), "1 + 2 = 3");
201/// ```
202///
203/// With [`diagnostic!`]-like arguments:
204/// ```
205/// use miette::{miette, LabeledSpan, Severity};
206///
207/// let source = "(2 + 2".to_string();
208/// let report = miette!(
209///     // Those fields are optional
210///     severity = Severity::Error,
211///     code = "expected::rparen",
212///     help = "always close your parens",
213///     labels = vec![LabeledSpan::at_offset(6, "here")],
214///     url = "https://example.com",
215///     // Rest of the arguments are passed to `format!`
216///     // to form diagnostic message
217///     "expected closing ')'"
218/// )
219/// .with_source_code(source);
220/// ```
221///
222/// ## `anyhow`/`eyre` Users
223///
224/// You can just replace `use`s of the `anyhow!`/`eyre!` macros with `miette!`.
225///
226/// [`diagnostic!`]: crate::diagnostic!
227/// [`Report`]: crate::Report
228#[macro_export]
229macro_rules! miette {
230    ($($key:ident = $value:expr,)* $fmt:literal $($arg:tt)*) => {
231        $crate::Report::from(
232            $crate::diagnostic!($($key = $value,)* $fmt $($arg)*)
233        )
234    };
235    ($err:expr $(,)?) => ({
236        use $crate::private::kind::*;
237        let error = $err;
238        (&error).miette_kind().new(error)
239    });
240}
241
242/// Construct a [`MietteDiagnostic`] in more user-friendly way.
243///
244/// # Examples
245/// ```
246/// use miette::{diagnostic, LabeledSpan, Severity};
247///
248/// let source = "(2 + 2".to_string();
249/// let diag = diagnostic!(
250///     // Those fields are optional
251///     severity = Severity::Error,
252///     code = "expected::rparen",
253///     help = "always close your parens",
254///     labels = vec![LabeledSpan::at_offset(6, "here")],
255///     url = "https://example.com",
256///     // Rest of the arguments are passed to `format!`
257///     // to form diagnostic message
258///     "expected closing ')'",
259/// );
260/// ```
261/// Diagnostic without any fields:
262/// ```
263/// # use miette::diagnostic;
264/// let x = 1;
265/// let y = 2;
266///
267#[cfg_attr(
268    not(feature = "no-format-args-capture"),
269    doc = r#" let diag = diagnostic!("{x} + {} = {z}", y, z = x + y);"#
270)]
271#[cfg_attr(
272    feature = "no-format-args-capture",
273    doc = r#" let diag = diagnostic!("{} + {} = {z}", x, y, z = x + y);"#
274)]
275/// assert_eq!(diag.message, "1 + 2 = 3");
276///
277/// let z = x + y;
278#[cfg_attr(
279    not(feature = "no-format-args-capture"),
280    doc = r#"let diag = diagnostic!("{x} + {y} = {z}");"#
281)]
282#[cfg_attr(
283    feature = "no-format-args-capture",
284    doc = r#"let diag = diagnostic!("{} + {} = {}", x, y, z);"#
285)]
286/// assert_eq!(diag.message, "1 + 2 = 3");
287/// ```
288///
289/// [`MietteDiagnostic`]: crate::MietteDiagnostic
290#[macro_export]
291macro_rules! diagnostic {
292    ($fmt:literal $($arg:tt)*) => {{
293        $crate::MietteDiagnostic::new(format!($fmt $($arg)*))
294    }};
295    ($($key:ident = $value:expr,)+ $fmt:literal $($arg:tt)*) => {{
296        let mut diag = $crate::MietteDiagnostic::new(format!($fmt $($arg)*));
297        $(diag.$key = Some($value.into());)*
298        diag
299    }};
300}