#[macro_export]
macro_rules! forward_error {
($ty:ty) => {
impl ::core::error::Error for $ty {
fn source(&self) -> Option<&(dyn ::core::error::Error + 'static)> {
Some(::core::ops::Deref::deref(&self.0))
}
}
};
($ty:ty => $field:ident) => {
impl ::core::error::Error for $ty {
fn source(&self) -> Option<&(dyn ::core::error::Error + 'static)> {
Some(::core::ops::Deref::deref(&self.$field))
}
}
};
}
#[macro_export]
macro_rules! impl_error_enum {
($ty:ty: $($variant:ident ($($inner:ident),+) => $source:expr),+ ,) => {
impl ::core::error::Error for $ty {
fn source(&self) -> ::core::option::Option<&(dyn ::core::error::Error + 'static)> {
match self {
$(
Self::$variant($($inner),+) => ::core::option::Option::Some($source),
)*
_ => ::core::option::Option::None,
}
}
}
};
($ty:ty: $($variant:ident ($($inner:ident),+) => $source:expr),+) => {
$crate::impl_error_enum!($ty: $($variant ($($inner),+) => $source),+ ,);
};
($ty:ty: $($variant:ident { $($inner:ident),+ } => $source:expr),+ ,) => {
impl ::core::error::Error for $ty {
fn source(&self) -> ::core::option::Option<&(dyn ::core::error::Error + 'static)> {
match self {
$(
Self::$variant($($inner),+) => ::core::option::Option::Some($source),
)*
_ => ::core::option::Option::None,
}
}
}
};
($ty:ty: $($variant:ident { $($inner:ident),+ } => $source:expr),+) => {
$crate::impl_error_enum!($ty, $($variant { $($inner),+ } => $source),+ ,);
};
($ty:ty) => {
impl ::core::error::Error for $ty {}
};
}
#[macro_export]
macro_rules! impl_leaf_error {
($ty:ty) => {
impl ::core::error::Error for $ty {}
};
}
#[cfg(test)]
#[rustversion::since(1.81)]
mod tests {
use alloc::string::String;
use core::error::Error as _;
#[test]
fn with_trailing_comma() {
#![allow(unused)]
#[derive(Debug)]
enum Foo {
Bar,
}
impl_display_enum!(Foo: Bar => "bar");
impl_leaf_error!(Foo);
}
#[test]
fn no_inner_data() {
#[derive(Debug)]
enum Foo {
Bar,
Baz,
}
impl_display_enum!(Foo: Bar => "bar", Baz => "qux");
impl_error_enum!(Foo);
assert!(Foo::Bar.source().is_none());
assert!(Foo::Baz.source().is_none());
}
#[test]
fn uniform_enum() {
#[derive(Debug)]
enum Foo {
Bar(String),
Baz(std::io::Error),
Qux(String, std::io::Error),
}
impl_display_enum! {
Foo:
Bar(desc) => "{desc}",
Baz(err) => "{err}",
Qux(desc, err) => "{desc}: {err}"
};
impl_error_enum! {
Foo:
Baz(err) => err,
Qux(_desc, err) => err
};
assert!(Foo::Bar(String::new()).source().is_none());
let io_err = std::io::Error::new(std::io::ErrorKind::Other, "test");
assert!(Foo::Baz(io_err).source().is_some());
let io_err = std::io::Error::new(std::io::ErrorKind::Other, "test");
assert!(Foo::Qux(String::new(), io_err).source().is_some());
}
}