#[macro_export]
macro_rules! err_downcast_ref {
( $err:expr ) => {
{ let _ = $err; None }
};
( $err:expr, $($v:ident : $ty:ty => $action:expr),* , ) => {
err_downcast_ref!($err, $($v : $ty => $action),*)
};
( $err:expr, $v:ident : $ty:ty => $action:expr $(, $rv:ident : $rty:ty => $raction:expr)* ) => {{
match $err.downcast_ref::<$ty>() {
Some($v) => Some($action),
None => err_downcast_ref!($err $(, $rv : $rty => $raction)*),
}
}};
}
#[macro_export]
macro_rules! err_downcast {
( $err:expr ) => {
Err($err)
};
( $err:expr, $($v:ident : $ty:ty => $action:expr),* , ) => {
err_downcast!($err, $($v : $ty => $action),*)
};
( $err:expr, $v:ident : $ty:ty => $action:expr $(, $rv:ident : $rty:ty => $raction:expr)* ) => {{
match $err.downcast::<$ty>() {
Ok($v) => Ok($action),
Err(other) => err_downcast!(other $(, $rv : $rty => $raction)*),
}
}};
}
#[allow(clippy::blacklisted_name)]
#[cfg(test)]
mod test {
use anyhow::Error;
use thiserror::Error;
#[derive(Error, Debug)]
#[error("Foo badness")]
struct Foo;
#[derive(Error, Debug)]
#[error("Bar badness")]
struct Bar;
#[derive(Error, Debug)]
#[error("Blat badness")]
struct Blat;
#[derive(Error, Debug)]
#[error("Outer badness")]
struct Outer;
#[test]
fn downcast_ref_syntax() {
let blat = Error::from(Blat);
let _ = err_downcast_ref! {
blat,
v: Foo => v.to_string(),
};
let _ = err_downcast_ref! {
blat,
v: Foo => v.to_string()
};
let _ = err_downcast_ref! {
blat,
v: Foo => v.to_string(),
v: Blat => v.to_string(),
};
let _ = err_downcast_ref! {
blat,
v: Foo => v.to_string(),
v: Blat => v.to_string()
};
}
#[test]
fn downcast_ref_basic() {
let blat = Error::from(Blat);
let msg = err_downcast_ref! {
blat,
foo: Foo => foo.to_string(),
bar: Bar => bar.to_string(),
blat: Blat => blat.to_string(),
outer: Outer => outer.to_string(),
};
assert_eq!(msg.unwrap(), "Blat badness".to_string());
}
#[allow(clippy::cognitive_complexity)]
#[test]
fn downcast_ref_context() {
let foo = Error::from(Foo);
let outer = foo.context(Outer);
let msg1 = err_downcast_ref! {
outer,
foo: Foo => foo.to_string(), bar: Bar => bar.to_string(),
blat: Blat => blat.to_string(),
outer: Outer => outer.to_string(),
};
let msg2 = err_downcast_ref! {
outer,
blat: Blat => blat.to_string(),
outer: Outer => outer.to_string(), foo: Foo => foo.to_string(),
bar: Bar => bar.to_string(),
};
assert_eq!(msg1.unwrap(), "Foo badness".to_string());
assert_eq!(msg2.unwrap(), "Outer badness".to_string());
}
#[test]
fn downcast_ref_miss() {
let blat = Error::from(Blat);
let msg = err_downcast_ref! {
blat,
v: Foo => { let _: &Foo = v; v.to_string() },
v: Bar => { let _: &Bar = v; v.to_string() },
};
assert!(msg.is_none());
assert!(blat.downcast_ref::<Blat>().is_some());
}
#[test]
fn downcast_syntax() {
let blat = Error::from(Blat);
let _ = err_downcast! {
blat,
v: Foo => v.to_string(),
};
let blat = Error::from(Blat);
let _ = err_downcast! {
blat,
v: Foo => v.to_string()
};
let blat = Error::from(Blat);
let _ = err_downcast! {
blat,
v: Foo => v.to_string(),
v: Blat => v.to_string(),
};
let blat = Error::from(Blat);
let _ = err_downcast! {
blat,
v: Foo => v.to_string(),
v: Blat => v.to_string()
};
}
#[test]
fn downcast_basic() {
let blat = Error::from(Blat);
let msg = err_downcast! {
blat,
foo: Foo => foo.to_string(),
bar: Bar => bar.to_string(),
blat: Blat => blat.to_string(),
outer: Outer => outer.to_string(),
};
assert_eq!(msg.unwrap(), "Blat badness".to_string());
}
#[test]
fn downcast_context() {
let foo = Error::from(Foo);
let outer = foo.context(Outer);
let msg = err_downcast! {
outer,
v: Foo => { let _: Foo = v; v.to_string() },
v: Bar => { let _: Bar = v; v.to_string() },
v: Blat => { let _: Blat = v; v.to_string() },
v: Outer => { let _: Outer = v; v.to_string() },
};
assert_eq!(msg.unwrap(), "Foo badness".to_string());
}
#[test]
fn downcast_miss() {
let blat = Error::from(Blat);
let msg = err_downcast! {
blat,
foo: Foo => foo.to_string(),
bar: Bar => bar.to_string(),
outer: Outer => outer.to_string(),
};
assert!(msg.is_err());
assert!(msg.unwrap_err().downcast::<Blat>().is_ok());
}
}