pub use anyhow::{Error, Result};
#[macro_export]
macro_rules! bail {
({ $($field:tt)+ } $(,)? $fmt:literal $(, $arg:expr)* $(,)?) => {{
let __ommx_msg = ::std::format!($fmt $(, $arg)*);
::tracing::error!($($field)+, "{}", __ommx_msg);
return ::std::result::Result::Err(::anyhow::Error::msg(__ommx_msg));
}};
($fmt:literal $(, $arg:expr)* $(,)?) => {{
let __ommx_msg = ::std::format!($fmt $(, $arg)*);
::tracing::error!("{}", __ommx_msg);
return ::std::result::Result::Err(::anyhow::Error::msg(__ommx_msg));
}};
($err:expr $(,)?) => {
return ::std::result::Result::Err(::anyhow::Error::from($err))
};
}
#[macro_export]
macro_rules! error {
({ $($field:tt)+ } $(,)? $fmt:literal $(, $arg:expr)* $(,)?) => {{
let __ommx_msg = ::std::format!($fmt $(, $arg)*);
::tracing::error!($($field)+, "{}", __ommx_msg);
::anyhow::Error::msg(__ommx_msg)
}};
($fmt:literal $(, $arg:expr)* $(,)?) => {{
let __ommx_msg = ::std::format!($fmt $(, $arg)*);
::tracing::error!("{}", __ommx_msg);
::anyhow::Error::msg(__ommx_msg)
}};
($err:expr $(,)?) => {
::anyhow::Error::from($err)
};
}
#[macro_export]
macro_rules! ensure {
($cond:expr, { $($field:tt)+ } $(,)? $fmt:literal $(, $arg:expr)* $(,)?) => {{
if !$cond {
let __ommx_msg = ::std::format!($fmt $(, $arg)*);
::tracing::error!($($field)+, "{}", __ommx_msg);
return ::std::result::Result::Err(::anyhow::Error::msg(__ommx_msg));
}
}};
($cond:expr, $fmt:literal $(, $arg:expr)* $(,)?) => {{
if !$cond {
let __ommx_msg = ::std::format!($fmt $(, $arg)*);
::tracing::error!("{}", __ommx_msg);
return ::std::result::Result::Err(::anyhow::Error::msg(__ommx_msg));
}
}};
($cond:expr, $err:expr $(,)?) => {
if !$cond {
return ::std::result::Result::Err(::anyhow::Error::from($err));
}
};
}
#[cfg(test)]
mod tests {
use super::*;
#[derive(Debug, thiserror::Error)]
#[error("signal variant for testing")]
struct TestSignal;
#[test]
fn downcast_recovers_signal() {
let e: Error = TestSignal.into();
assert!(e.is::<TestSignal>());
assert!(e.downcast_ref::<TestSignal>().is_some());
}
#[test]
fn macro_bail_plain_message() {
fn inner() -> Result<()> {
crate::bail!("boom")
}
assert_eq!(inner().unwrap_err().to_string(), "boom");
}
#[test]
fn macro_bail_formatted_message() {
fn inner(code: u32) -> Result<()> {
crate::bail!("boom: code={code}")
}
assert_eq!(inner(7).unwrap_err().to_string(), "boom: code=7");
}
#[test]
fn macro_bail_with_fields() {
fn inner(code: u32) -> Result<()> {
crate::bail!({ code = code }, "boom: code={code}")
}
assert_eq!(inner(9).unwrap_err().to_string(), "boom: code=9");
}
#[test]
fn macro_bail_with_signal_expression() {
fn inner() -> Result<()> {
crate::bail!(TestSignal)
}
let err = inner().unwrap_err();
assert!(err.is::<TestSignal>());
}
#[test]
fn macro_ensure_short_circuits() {
fn inner(ok: bool) -> Result<()> {
crate::ensure!(ok, "not ok");
Ok(())
}
assert!(inner(true).is_ok());
assert_eq!(inner(false).unwrap_err().to_string(), "not ok");
}
#[test]
fn macro_ensure_with_fields() {
fn inner(ok: bool, code: u32) -> Result<()> {
crate::ensure!(ok, { code = code }, "not ok: code={code}");
Ok(())
}
assert!(inner(true, 5).is_ok());
assert_eq!(inner(false, 5).unwrap_err().to_string(), "not ok: code=5");
}
#[test]
fn macro_ensure_with_signal_expression() {
fn inner(ok: bool) -> Result<()> {
crate::ensure!(ok, TestSignal);
Ok(())
}
assert!(inner(true).is_ok());
assert!(inner(false).unwrap_err().is::<TestSignal>());
}
#[test]
fn macro_error_builds_inline() {
let err: Error = crate::error!("inline message {}", 42);
assert_eq!(err.to_string(), "inline message 42");
}
#[test]
fn macro_error_with_fields() {
let code = 7u32;
let err: Error = crate::error!({ code }, "inline: code={code}");
assert_eq!(err.to_string(), "inline: code=7");
}
#[test]
fn macro_error_with_signal_expression() {
let err: Error = crate::error!(TestSignal);
assert!(err.is::<TestSignal>());
}
#[test]
fn macro_bail_evaluates_args_once() {
use std::cell::Cell;
let counter: Cell<u32> = Cell::new(0);
fn inner(counter: &Cell<u32>) -> Result<()> {
crate::bail!("count={}", {
counter.set(counter.get() + 1);
42
});
}
let err = inner(&counter).unwrap_err();
assert_eq!(counter.get(), 1, "arg evaluated {} times", counter.get());
assert_eq!(err.to_string(), "count=42");
}
#[test]
fn macro_error_evaluates_args_once() {
use std::cell::Cell;
let counter: Cell<u32> = Cell::new(0);
let err: Error = crate::error!("count={}", {
counter.set(counter.get() + 1);
42
});
assert_eq!(counter.get(), 1, "arg evaluated {} times", counter.get());
assert_eq!(err.to_string(), "count=42");
}
#[test]
fn macro_ensure_evaluates_args_once() {
use std::cell::Cell;
let counter: Cell<u32> = Cell::new(0);
fn inner(counter: &Cell<u32>) -> Result<()> {
crate::ensure!(false, "count={}", {
counter.set(counter.get() + 1);
42
});
Ok(())
}
let err = inner(&counter).unwrap_err();
assert_eq!(counter.get(), 1, "arg evaluated {} times", counter.get());
assert_eq!(err.to_string(), "count=42");
}
}