/// Private API. #[doc(hidden)] #[macro_export] macro_rules! __log_rejection { ( rejection_type = $ty:ident, body_text = $body_text:expr, status = $status:expr, ) => { #[cfg(feature = "tracing")] { tracing::event!( target: "axum::rejection", tracing::Level::TRACE, status = $status.as_u16(), body = $body_text, rejection_type = std::any::type_name::<$ty>(), "rejecting request", ); } }; } /// Private API. #[doc(hidden)] #[macro_export] macro_rules! __define_rejection { ( #[status = $status:ident] #[body = $body:expr] $(#[$m:meta])* pub struct $name:ident; ) => { $(#[$m])* #[derive(Debug)] #[non_exhaustive] pub struct $name; impl $crate::response::IntoResponse for $name { fn into_response(self) -> $crate::response::Response { $crate::__log_rejection!( rejection_type = $name, body_text = $body, status = http::StatusCode::$status, ); (self.status(), $body).into_response() } } impl $name { /// Get the response body text used for this rejection. pub fn body_text(&self) -> String { $body.into() } /// Get the status code used for this rejection. pub fn status(&self) -> http::StatusCode { http::StatusCode::$status } } impl std::fmt::Display for $name { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!(f, "{}", $body) } } impl std::error::Error for $name {} impl Default for $name { fn default() -> Self { Self } } }; ( #[status = $status:ident] #[body = $body:expr] $(#[$m:meta])* pub struct $name:ident (Error); ) => { $(#[$m])* #[derive(Debug)] pub struct $name(pub(crate) $crate::Error); impl $name { pub(crate) fn from_err(err: E) -> Self where E: Into<$crate::BoxError>, { Self($crate::Error::new(err)) } } impl $crate::response::IntoResponse for $name { fn into_response(self) -> $crate::response::Response { $crate::__log_rejection!( rejection_type = $name, body_text = self.body_text(), status = http::StatusCode::$status, ); (self.status(), self.body_text()).into_response() } } impl $name { /// Get the response body text used for this rejection. pub fn body_text(&self) -> String { format!(concat!($body, ": {}"), self.0).into() } /// Get the status code used for this rejection. pub fn status(&self) -> http::StatusCode { http::StatusCode::$status } } impl std::fmt::Display for $name { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!(f, "{}", $body) } } impl std::error::Error for $name { fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { Some(&self.0) } } }; } /// Private API. #[doc(hidden)] #[macro_export] macro_rules! __composite_rejection { ( $(#[$m:meta])* pub enum $name:ident { $($variant:ident),+ $(,)? } ) => { $(#[$m])* #[derive(Debug)] #[non_exhaustive] pub enum $name { $( #[allow(missing_docs)] $variant($variant) ),+ } impl $crate::response::IntoResponse for $name { fn into_response(self) -> $crate::response::Response { match self { $( Self::$variant(inner) => inner.into_response(), )+ } } } impl $name { /// Get the response body text used for this rejection. pub fn body_text(&self) -> String { match self { $( Self::$variant(inner) => inner.body_text(), )+ } } /// Get the status code used for this rejection. pub fn status(&self) -> http::StatusCode { match self { $( Self::$variant(inner) => inner.status(), )+ } } } $( impl From<$variant> for $name { fn from(inner: $variant) -> Self { Self::$variant(inner) } } )+ impl std::fmt::Display for $name { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { $( Self::$variant(inner) => write!(f, "{inner}"), )+ } } } impl std::error::Error for $name { fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { match self { $( Self::$variant(inner) => inner.source(), )+ } } } }; } #[rustfmt::skip] macro_rules! all_the_tuples { ($name:ident) => { $name!([], T1); $name!([T1], T2); $name!([T1, T2], T3); $name!([T1, T2, T3], T4); $name!([T1, T2, T3, T4], T5); $name!([T1, T2, T3, T4, T5], T6); $name!([T1, T2, T3, T4, T5, T6], T7); $name!([T1, T2, T3, T4, T5, T6, T7], T8); $name!([T1, T2, T3, T4, T5, T6, T7, T8], T9); $name!([T1, T2, T3, T4, T5, T6, T7, T8, T9], T10); $name!([T1, T2, T3, T4, T5, T6, T7, T8, T9, T10], T11); $name!([T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11], T12); $name!([T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12], T13); $name!([T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13], T14); $name!([T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14], T15); $name!([T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15], T16); }; } macro_rules! all_the_tuples_no_last_special_case { ($name:ident) => { $name!(T1); $name!(T1, T2); $name!(T1, T2, T3); $name!(T1, T2, T3, T4); $name!(T1, T2, T3, T4, T5); $name!(T1, T2, T3, T4, T5, T6); $name!(T1, T2, T3, T4, T5, T6, T7); $name!(T1, T2, T3, T4, T5, T6, T7, T8); $name!(T1, T2, T3, T4, T5, T6, T7, T8, T9); $name!(T1, T2, T3, T4, T5, T6, T7, T8, T9, T10); $name!(T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11); $name!(T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12); $name!(T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13); $name!(T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14); $name!(T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15); $name!(T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16); }; } /// Private API. #[doc(hidden)] #[macro_export] macro_rules! __impl_deref { ($ident:ident) => { impl std::ops::Deref for $ident { type Target = T; #[inline] fn deref(&self) -> &Self::Target { &self.0 } } impl std::ops::DerefMut for $ident { #[inline] fn deref_mut(&mut self) -> &mut Self::Target { &mut self.0 } } }; ($ident:ident: $ty:ty) => { impl std::ops::Deref for $ident { type Target = $ty; #[inline] fn deref(&self) -> &Self::Target { &self.0 } } impl std::ops::DerefMut for $ident { #[inline] fn deref_mut(&mut self) -> &mut Self::Target { &mut self.0 } } }; } #[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); } }