diff --git a/axum-core/src/macros.rs b/axum-core/src/macros.rs index 10365e1556..e0a4e8683c 100644 --- a/axum-core/src/macros.rs +++ b/axum-core/src/macros.rs @@ -203,7 +203,7 @@ macro_rules! __composite_rejection { fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { match self { $( - Self::$variant(inner) => Some(inner), + Self::$variant(inner) => inner.source(), )+ } } @@ -211,6 +211,46 @@ macro_rules! __composite_rejection { }; } +#[cfg(test)] +mod composite_rejection_tests { + use self::defs::*; + use crate::Error; + use std::error::Error as _; + + #[allow(dead_code, unreachable_pub)] + mod defs { + use crate::{__composite_rejection, __define_rejection}; + + __define_rejection! { + #[status = BAD_REQUEST] + #[body = "error message 1"] + pub struct Inner1; + } + __define_rejection! { + #[status = BAD_REQUEST] + #[body = "error message 2"] + pub struct Inner2(Error); + } + __composite_rejection! { + pub enum Outer { Inner1, Inner2 } + } + } + + /// The implementation of `.source()` on `Outer` should defer straight to the implementation + /// on its inner type instead of returning the inner type itself, because the `Display` + /// implementation on `Outer` already forwards to the inner type and so it would result in two + /// errors in the chain `Display`ing the same thing. + #[test] + fn source_gives_inner_source() { + let rejection = Outer::Inner1(Inner1); + assert!(rejection.source().is_none()); + + let msg = "hello world"; + let rejection = Outer::Inner2(Inner2(Error::new(msg))); + assert_eq!(rejection.source().unwrap().to_string(), msg); + } +} + #[rustfmt::skip] macro_rules! all_the_tuples { ($name:ident) => {