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}