| /// Return early with an error. | |
| /// | |
| /// This macro is equivalent to `return Err(From::from($err))`. | |
| /// | |
| /// # Example | |
| /// | |
| /// ``` | |
| /// # use miette::{bail, Result}; | |
| /// # | |
| /// # fn has_permission(user: usize, resource: usize) -> bool { | |
| /// # true | |
| /// # } | |
| /// # | |
| /// # fn main() -> Result<()> { | |
| /// # let user = 0; | |
| /// # let resource = 0; | |
| /// # | |
| /// if !has_permission(user, resource) { | |
| #[cfg_attr( | |
| not(feature = "no-format-args-capture"), | |
| doc = r#" bail!("permission denied for accessing {resource}");"# | |
| )] | |
| #[cfg_attr( | |
| feature = "no-format-args-capture", | |
| doc = r#" bail!("permission denied for accessing {}", resource);"# | |
| )] | |
| /// } | |
| /// # Ok(()) | |
| /// # } | |
| /// ``` | |
| /// | |
| /// ``` | |
| /// # use miette::{bail, Result}; | |
| /// # use thiserror::Error; | |
| /// # | |
| /// # const MAX_DEPTH: usize = 1; | |
| /// # | |
| /// #[derive(Error, Debug)] | |
| /// enum ScienceError { | |
| /// #[error("recursion limit exceeded")] | |
| /// RecursionLimitExceeded, | |
| /// # #[error("...")] | |
| /// # More = (stringify! { | |
| /// ... | |
| /// # }, 1).1, | |
| /// } | |
| /// | |
| /// # fn main() -> Result<()> { | |
| /// # let depth = 0; | |
| /// # let err: &'static dyn std::error::Error = &ScienceError::RecursionLimitExceeded; | |
| /// # | |
| /// if depth > MAX_DEPTH { | |
| /// bail!(ScienceError::RecursionLimitExceeded); | |
| /// } | |
| /// # Ok(()) | |
| /// # } | |
| /// ``` | |
| /// | |
| /// ``` | |
| /// use miette::{bail, Result, Severity}; | |
| /// | |
| /// fn divide(x: f64, y: f64) -> Result<f64> { | |
| /// if y.abs() < 1e-3 { | |
| /// bail!( | |
| /// severity = Severity::Warning, | |
| #[cfg_attr( | |
| not(feature = "no-format-args-capture"), | |
| doc = r#" "dividing by value ({y}) close to 0""# | |
| )] | |
| #[cfg_attr( | |
| feature = "no-format-args-capture", | |
| doc = r#" "dividing by value ({}) close to 0", y"# | |
| )] | |
| /// ); | |
| /// } | |
| /// Ok(x / y) | |
| /// } | |
| /// ``` | |
| #[macro_export] | |
| macro_rules! bail { | |
| ($($key:ident = $value:expr,)* $fmt:literal $($arg:tt)*) => { | |
| return $crate::private::Err( | |
| $crate::miette!($($key = $value,)* $fmt $($arg)*) | |
| ); | |
| }; | |
| ($err:expr $(,)?) => { | |
| return $crate::private::Err($crate::miette!($err)); | |
| }; | |
| } | |
| /// Return early with an error if a condition is not satisfied. | |
| /// | |
| /// This macro is equivalent to `if !$cond { return Err(From::from($err)); }`. | |
| /// | |
| /// Analogously to `assert!`, `ensure!` takes a condition and exits the function | |
| /// if the condition fails. Unlike `assert!`, `ensure!` returns an `Error` | |
| /// rather than panicking. | |
| /// | |
| /// # Example | |
| /// | |
| /// ``` | |
| /// # use miette::{ensure, Result}; | |
| /// # | |
| /// # fn main() -> Result<()> { | |
| /// # let user = 0; | |
| /// # | |
| /// ensure!(user == 0, "only user 0 is allowed"); | |
| /// # Ok(()) | |
| /// # } | |
| /// ``` | |
| /// | |
| /// ``` | |
| /// # use miette::{ensure, Result}; | |
| /// # use thiserror::Error; | |
| /// # | |
| /// # const MAX_DEPTH: usize = 1; | |
| /// # | |
| /// #[derive(Error, Debug)] | |
| /// enum ScienceError { | |
| /// #[error("recursion limit exceeded")] | |
| /// RecursionLimitExceeded, | |
| /// # #[error("...")] | |
| /// # More = (stringify! { | |
| /// ... | |
| /// # }, 1).1, | |
| /// } | |
| /// | |
| /// # fn main() -> Result<()> { | |
| /// # let depth = 0; | |
| /// # | |
| /// ensure!(depth <= MAX_DEPTH, ScienceError::RecursionLimitExceeded); | |
| /// # Ok(()) | |
| /// # } | |
| /// ``` | |
| /// | |
| /// ``` | |
| /// use miette::{ensure, Result, Severity}; | |
| /// | |
| /// fn divide(x: f64, y: f64) -> Result<f64> { | |
| /// ensure!( | |
| /// y.abs() >= 1e-3, | |
| /// severity = Severity::Warning, | |
| #[cfg_attr( | |
| not(feature = "no-format-args-capture"), | |
| doc = r#" "dividing by value ({y}) close to 0""# | |
| )] | |
| #[cfg_attr( | |
| feature = "no-format-args-capture", | |
| doc = r#" "dividing by value ({}) close to 0", y"# | |
| )] | |
| /// ); | |
| /// Ok(x / y) | |
| /// } | |
| /// ``` | |
| #[macro_export] | |
| macro_rules! ensure { | |
| ($cond:expr, $($key:ident = $value:expr,)* $fmt:literal $($arg:tt)*) => { | |
| if !$cond { | |
| return $crate::private::Err( | |
| $crate::miette!($($key = $value,)* $fmt $($arg)*) | |
| ); | |
| } | |
| }; | |
| ($cond:expr, $err:expr $(,)?) => { | |
| if !$cond { | |
| return $crate::private::Err($crate::miette!($err)); | |
| } | |
| }; | |
| } | |
| /// Construct an ad-hoc [`Report`]. | |
| /// | |
| /// # Examples | |
| /// | |
| /// With string literal and interpolation: | |
| /// ``` | |
| /// # use miette::miette; | |
| /// let x = 1; | |
| /// let y = 2; | |
| #[cfg_attr( | |
| not(feature = "no-format-args-capture"), | |
| doc = r#"let report = miette!("{x} + {} = {z}", y, z = x + y);"# | |
| )] | |
| #[cfg_attr( | |
| feature = "no-format-args-capture", | |
| doc = r#"let report = miette!("{} + {} = {z}", x, y, z = x + y);"# | |
| )] | |
| /// | |
| /// assert_eq!(report.to_string().as_str(), "1 + 2 = 3"); | |
| /// | |
| /// let z = x + y; | |
| #[cfg_attr( | |
| not(feature = "no-format-args-capture"), | |
| doc = r#"let report = miette!("{x} + {y} = {z}");"# | |
| )] | |
| #[cfg_attr( | |
| feature = "no-format-args-capture", | |
| doc = r#"let report = miette!("{} + {} = {}", x, y, z);"# | |
| )] | |
| /// assert_eq!(report.to_string().as_str(), "1 + 2 = 3"); | |
| /// ``` | |
| /// | |
| /// With [`diagnostic!`]-like arguments: | |
| /// ``` | |
| /// use miette::{miette, LabeledSpan, Severity}; | |
| /// | |
| /// let source = "(2 + 2".to_string(); | |
| /// let report = miette!( | |
| /// // Those fields are optional | |
| /// severity = Severity::Error, | |
| /// code = "expected::rparen", | |
| /// help = "always close your parens", | |
| /// labels = vec![LabeledSpan::at_offset(6, "here")], | |
| /// url = "https://example.com", | |
| /// // Rest of the arguments are passed to `format!` | |
| /// // to form diagnostic message | |
| /// "expected closing ')'" | |
| /// ) | |
| /// .with_source_code(source); | |
| /// ``` | |
| /// | |
| /// ## `anyhow`/`eyre` Users | |
| /// | |
| /// You can just replace `use`s of the `anyhow!`/`eyre!` macros with `miette!`. | |
| /// | |
| /// [`diagnostic!`]: crate::diagnostic! | |
| /// [`Report`]: crate::Report | |
| #[macro_export] | |
| macro_rules! miette { | |
| ($($key:ident = $value:expr,)* $fmt:literal $($arg:tt)*) => { | |
| $crate::Report::from( | |
| $crate::diagnostic!($($key = $value,)* $fmt $($arg)*) | |
| ) | |
| }; | |
| ($err:expr $(,)?) => ({ | |
| use $crate::private::kind::*; | |
| let error = $err; | |
| (&error).miette_kind().new(error) | |
| }); | |
| } | |
| /// Construct a [`MietteDiagnostic`] in more user-friendly way. | |
| /// | |
| /// # Examples | |
| /// ``` | |
| /// use miette::{diagnostic, LabeledSpan, Severity}; | |
| /// | |
| /// let source = "(2 + 2".to_string(); | |
| /// let diag = diagnostic!( | |
| /// // Those fields are optional | |
| /// severity = Severity::Error, | |
| /// code = "expected::rparen", | |
| /// help = "always close your parens", | |
| /// labels = vec![LabeledSpan::at_offset(6, "here")], | |
| /// url = "https://example.com", | |
| /// // Rest of the arguments are passed to `format!` | |
| /// // to form diagnostic message | |
| /// "expected closing ')'", | |
| /// ); | |
| /// ``` | |
| /// Diagnostic without any fields: | |
| /// ``` | |
| /// # use miette::diagnostic; | |
| /// let x = 1; | |
| /// let y = 2; | |
| /// | |
| #[cfg_attr( | |
| not(feature = "no-format-args-capture"), | |
| doc = r#" let diag = diagnostic!("{x} + {} = {z}", y, z = x + y);"# | |
| )] | |
| #[cfg_attr( | |
| feature = "no-format-args-capture", | |
| doc = r#" let diag = diagnostic!("{} + {} = {z}", x, y, z = x + y);"# | |
| )] | |
| /// assert_eq!(diag.message, "1 + 2 = 3"); | |
| /// | |
| /// let z = x + y; | |
| #[cfg_attr( | |
| not(feature = "no-format-args-capture"), | |
| doc = r#"let diag = diagnostic!("{x} + {y} = {z}");"# | |
| )] | |
| #[cfg_attr( | |
| feature = "no-format-args-capture", | |
| doc = r#"let diag = diagnostic!("{} + {} = {}", x, y, z);"# | |
| )] | |
| /// assert_eq!(diag.message, "1 + 2 = 3"); | |
| /// ``` | |
| /// | |
| /// [`MietteDiagnostic`]: crate::MietteDiagnostic | |
| #[macro_export] | |
| macro_rules! diagnostic { | |
| ($fmt:literal $($arg:tt)*) => {{ | |
| $crate::MietteDiagnostic::new(format!($fmt $($arg)*)) | |
| }}; | |
| ($($key:ident = $value:expr,)+ $fmt:literal $($arg:tt)*) => {{ | |
| let mut diag = $crate::MietteDiagnostic::new(format!($fmt $($arg)*)); | |
| $(diag.$key = Some($value.into());)* | |
| diag | |
| }}; | |
| } |