193 lines
5.7 KiB
Rust
193 lines
5.7 KiB
Rust
//! Error construction macros
|
|
//!
|
|
//! These are specialized macros specific to this project's patterns for
|
|
//! throwing Errors; they make Error construction succinct and reduce clutter.
|
|
//! They are developed from folding existing patterns into the macro while
|
|
//! fixing several anti-patterns in the codebase.
|
|
//!
|
|
//! - The primary macros `Err!` and `err!` are provided. `Err!` simply wraps
|
|
//! `err!` in the Result variant to reduce `Err(err!(...))` boilerplate, thus
|
|
//! `err!` can be used in any case.
|
|
//!
|
|
//! 1. The macro makes the general Error construction easy: `return
|
|
//! Err!("something went wrong")` replaces the prior `return
|
|
//! Err(Error::Err("something went wrong".to_owned()))`.
|
|
//!
|
|
//! 2. The macro integrates format strings automatically: `return
|
|
//! Err!("something bad: {msg}")` replaces the prior `return
|
|
//! Err(Error::Err(format!("something bad: {msg}")))`.
|
|
//!
|
|
//! 3. The macro scopes variants of Error: `return Err!(Database("problem with
|
|
//! bad database."))` replaces the prior `return Err(Error::Database("problem
|
|
//! with bad database."))`.
|
|
//!
|
|
//! 4. The macro matches and scopes some special-case sub-variants, for example
|
|
//! with ruma ErrorKind: `return Err!(Request(MissingToken("you must provide
|
|
//! an access token")))`.
|
|
//!
|
|
//! 5. The macro fixes the anti-pattern of repeating messages in an error! log
|
|
//! and then again in an Error construction, often slightly different due to
|
|
//! the Error variant not supporting a format string. Instead `return
|
|
//! Err(Database(error!("problem with db: {msg}")))` logs the error at the
|
|
//! callsite and then returns the error with the same string. Caller has the
|
|
//! option of replacing `error!` with `debug_error!`.
|
|
|
|
#[macro_export]
|
|
macro_rules! Err {
|
|
($($args:tt)*) => {
|
|
Err($crate::err!($($args)*))
|
|
};
|
|
}
|
|
|
|
#[macro_export]
|
|
macro_rules! err {
|
|
(Request(Forbidden($level:ident!($($args:tt)+)))) => {{
|
|
let mut buf = String::new();
|
|
$crate::error::Error::Request(
|
|
$crate::ruma::api::client::error::ErrorKind::forbidden(),
|
|
$crate::err_log!(buf, $level, $($args)+),
|
|
$crate::http::StatusCode::BAD_REQUEST
|
|
)
|
|
}};
|
|
|
|
(Request(Forbidden($($args:tt)+))) => {
|
|
$crate::error::Error::Request(
|
|
$crate::ruma::api::client::error::ErrorKind::forbidden(),
|
|
$crate::format_maybe!($($args)+),
|
|
$crate::http::StatusCode::BAD_REQUEST
|
|
)
|
|
};
|
|
|
|
(Request($variant:ident($level:ident!($($args:tt)+)))) => {{
|
|
let mut buf = String::new();
|
|
$crate::error::Error::Request(
|
|
$crate::ruma::api::client::error::ErrorKind::$variant,
|
|
$crate::err_log!(buf, $level, $($args)+),
|
|
$crate::http::StatusCode::BAD_REQUEST
|
|
)
|
|
}};
|
|
|
|
(Request($variant:ident($($args:tt)+))) => {
|
|
$crate::error::Error::Request(
|
|
$crate::ruma::api::client::error::ErrorKind::$variant,
|
|
$crate::format_maybe!($($args)+),
|
|
$crate::http::StatusCode::BAD_REQUEST
|
|
)
|
|
};
|
|
|
|
(Config($item:literal, $($args:tt)+)) => {{
|
|
let mut buf = String::new();
|
|
$crate::error::Error::Config($item, $crate::err_log!(buf, error, config = %$item, $($args)+))
|
|
}};
|
|
|
|
($variant:ident($level:ident!($($args:tt)+))) => {{
|
|
let mut buf = String::new();
|
|
$crate::error::Error::$variant($crate::err_log!(buf, $level, $($args)+))
|
|
}};
|
|
|
|
($variant:ident($($args:ident),+)) => {
|
|
$crate::error::Error::$variant($($args),+)
|
|
};
|
|
|
|
($variant:ident($($args:tt)+)) => {
|
|
$crate::error::Error::$variant($crate::format_maybe!($($args)+))
|
|
};
|
|
|
|
($level:ident!($($args:tt)+)) => {{
|
|
let mut buf = String::new();
|
|
$crate::error::Error::Err($crate::err_log!(buf, $level, $($args)+))
|
|
}};
|
|
|
|
($($args:tt)+) => {
|
|
$crate::error::Error::Err($crate::format_maybe!($($args)+))
|
|
};
|
|
}
|
|
|
|
/// A trinity of integration between tracing, logging, and Error. This is a
|
|
/// customization of tracing::event! with the primary purpose of sharing the
|
|
/// error string, fieldset parsing and formatting. An added benefit is that we
|
|
/// can share the same callsite metadata for the source of our Error and the
|
|
/// associated logging and tracing event dispatches.
|
|
#[macro_export]
|
|
macro_rules! err_log {
|
|
($out:ident, $level:ident, $($fields:tt)+) => {{
|
|
use std::{fmt, fmt::Write};
|
|
|
|
use $crate::tracing::{
|
|
callsite, callsite2, level_enabled, metadata, valueset, Callsite, Event, __macro_support,
|
|
__tracing_log,
|
|
field::{Field, ValueSet, Visit},
|
|
Level,
|
|
};
|
|
|
|
const LEVEL: Level = $crate::err_lev!($level);
|
|
static __CALLSITE: callsite::DefaultCallsite = callsite2! {
|
|
name: std::concat! {
|
|
"event ",
|
|
std::file!(),
|
|
":",
|
|
std::line!(),
|
|
},
|
|
kind: metadata::Kind::EVENT,
|
|
target: std::module_path!(),
|
|
level: LEVEL,
|
|
fields: $($fields)+,
|
|
};
|
|
|
|
let visit = &mut |vs: ValueSet<'_>| {
|
|
struct Visitor<'a>(&'a mut String);
|
|
impl Visit for Visitor<'_> {
|
|
fn record_debug(&mut self, field: &Field, val: &dyn fmt::Debug) {
|
|
if field.name() == "message" {
|
|
write!(self.0, "{:?}", val).expect("stream error");
|
|
} else {
|
|
write!(self.0, " {}={:?}", field.name(), val).expect("stream error");
|
|
}
|
|
}
|
|
}
|
|
|
|
let meta = __CALLSITE.metadata();
|
|
let enabled = level_enabled!(LEVEL) && {
|
|
let interest = __CALLSITE.interest();
|
|
!interest.is_never() && __macro_support::__is_enabled(meta, interest)
|
|
};
|
|
|
|
if enabled {
|
|
Event::dispatch(meta, &vs);
|
|
}
|
|
|
|
__tracing_log!(LEVEL, __CALLSITE, &vs);
|
|
vs.record(&mut Visitor(&mut $out));
|
|
};
|
|
|
|
(visit)(valueset!(__CALLSITE.metadata().fields(), $($fields)+));
|
|
($out).into()
|
|
}}
|
|
}
|
|
|
|
#[macro_export]
|
|
macro_rules! err_lev {
|
|
(debug_warn) => {
|
|
if $crate::debug::logging() {
|
|
$crate::tracing::Level::WARN
|
|
} else {
|
|
$crate::tracing::Level::DEBUG
|
|
}
|
|
};
|
|
|
|
(debug_error) => {
|
|
if $crate::debug::logging() {
|
|
$crate::tracing::Level::ERROR
|
|
} else {
|
|
$crate::tracing::Level::DEBUG
|
|
}
|
|
};
|
|
|
|
(warn) => {
|
|
$crate::tracing::Level::WARN
|
|
};
|
|
|
|
(error) => {
|
|
$crate::tracing::Level::ERROR
|
|
};
|
|
}
|