axum_core/
macros.rs

1/// Private API.
2#[cfg(feature = "tracing")]
3#[doc(hidden)]
4#[macro_export]
5macro_rules! __log_rejection {
6    (
7        rejection_type = $ty:ident,
8        body_text = $body_text:expr,
9        status = $status:expr,
10    ) => {
11        {
12            $crate::__private::tracing::event!(
13                target: "axum::rejection",
14                $crate::__private::tracing::Level::TRACE,
15                status = $status.as_u16(),
16                body = $body_text,
17                rejection_type = ::std::any::type_name::<$ty>(),
18                "rejecting request",
19            );
20        }
21    };
22}
23
24#[cfg(not(feature = "tracing"))]
25#[doc(hidden)]
26#[macro_export]
27macro_rules! __log_rejection {
28    (
29        rejection_type = $ty:ident,
30        body_text = $body_text:expr,
31        status = $status:expr,
32    ) => {};
33}
34
35/// Private API.
36#[doc(hidden)]
37#[macro_export]
38macro_rules! __define_rejection {
39    (
40        #[status = $status:ident]
41        #[body = $body:literal]
42        $(#[$m:meta])*
43        pub struct $name:ident;
44    ) => {
45        $(#[$m])*
46        #[derive(Debug)]
47        #[non_exhaustive]
48        pub struct $name;
49
50        impl $name {
51            /// Get the response body text used for this rejection.
52            pub fn body_text(&self) -> String {
53                self.to_string()
54            }
55
56            /// Get the status code used for this rejection.
57            pub fn status(&self) -> http::StatusCode {
58                http::StatusCode::$status
59            }
60        }
61
62        impl $crate::response::IntoResponse for $name {
63            fn into_response(self) -> $crate::response::Response {
64                let status = self.status();
65
66                $crate::__log_rejection!(
67                    rejection_type = $name,
68                    body_text = $body,
69                    status = status,
70                );
71                (status, $body).into_response()
72            }
73        }
74
75        impl std::fmt::Display for $name {
76            fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
77                write!(f, "{}", $body)
78            }
79        }
80
81        impl std::error::Error for $name {}
82
83        impl Default for $name {
84            fn default() -> Self {
85                Self
86            }
87        }
88    };
89
90    (
91        #[status = $status:ident]
92        #[body = $body:literal]
93        $(#[$m:meta])*
94        pub struct $name:ident (Error);
95    ) => {
96        $(#[$m])*
97        #[derive(Debug)]
98        pub struct $name(pub(crate) $crate::Error);
99
100        impl $name {
101            pub(crate) fn from_err<E>(err: E) -> Self
102            where
103                E: Into<$crate::BoxError>,
104            {
105                Self($crate::Error::new(err))
106            }
107
108            /// Get the response body text used for this rejection.
109            #[must_use]
110            pub fn body_text(&self) -> String {
111                self.to_string()
112            }
113
114            /// Get the status code used for this rejection.
115            #[must_use]
116            pub fn status(&self) -> http::StatusCode {
117                http::StatusCode::$status
118            }
119        }
120
121        impl $crate::response::IntoResponse for $name {
122            fn into_response(self) -> $crate::response::Response {
123                let status = self.status();
124                let body_text = self.body_text();
125
126                $crate::__log_rejection!(
127                    rejection_type = $name,
128                    body_text = body_text,
129                    status = status,
130                );
131                (status, body_text).into_response()
132            }
133        }
134
135        impl std::fmt::Display for $name {
136            fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
137                f.write_str($body)?;
138                f.write_str(": ")?;
139                self.0.fmt(f)
140            }
141        }
142
143        impl std::error::Error for $name {
144            fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
145                Some(&self.0)
146            }
147        }
148    };
149}
150
151/// Private API.
152#[doc(hidden)]
153#[macro_export]
154macro_rules! __composite_rejection {
155    (
156        $(#[$m:meta])*
157        pub enum $name:ident {
158            $($variant:ident),+
159            $(,)?
160        }
161    ) => {
162        $(#[$m])*
163        #[derive(Debug)]
164        #[non_exhaustive]
165        pub enum $name {
166            $(
167                #[allow(missing_docs)]
168                $variant($variant)
169            ),+
170        }
171
172        impl $crate::response::IntoResponse for $name {
173            fn into_response(self) -> $crate::response::Response {
174                match self {
175                    $(
176                        Self::$variant(inner) => inner.into_response(),
177                    )+
178                }
179            }
180        }
181
182        impl $name {
183            /// Get the response body text used for this rejection.
184            #[must_use]
185            pub fn body_text(&self) -> String {
186                match self {
187                    $(
188                        Self::$variant(inner) => inner.body_text(),
189                    )+
190                }
191            }
192
193            /// Get the status code used for this rejection.
194            #[must_use]
195            pub fn status(&self) -> http::StatusCode {
196                match self {
197                    $(
198                        Self::$variant(inner) => inner.status(),
199                    )+
200                }
201            }
202        }
203
204        $(
205            impl From<$variant> for $name {
206                fn from(inner: $variant) -> Self {
207                    Self::$variant(inner)
208                }
209            }
210        )+
211
212        impl std::fmt::Display for $name {
213            fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
214                match self {
215                    $(
216                        Self::$variant(inner) => write!(f, "{inner}"),
217                    )+
218                }
219            }
220        }
221
222        impl std::error::Error for $name {
223            fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
224                match self {
225                    $(
226                        Self::$variant(inner) => inner.source(),
227                    )+
228                }
229            }
230        }
231    };
232}
233
234#[rustfmt::skip]
235macro_rules! all_the_tuples {
236    ($name:ident) => {
237        $name!([], T1);
238        $name!([T1], T2);
239        $name!([T1, T2], T3);
240        $name!([T1, T2, T3], T4);
241        $name!([T1, T2, T3, T4], T5);
242        $name!([T1, T2, T3, T4, T5], T6);
243        $name!([T1, T2, T3, T4, T5, T6], T7);
244        $name!([T1, T2, T3, T4, T5, T6, T7], T8);
245        $name!([T1, T2, T3, T4, T5, T6, T7, T8], T9);
246        $name!([T1, T2, T3, T4, T5, T6, T7, T8, T9], T10);
247        $name!([T1, T2, T3, T4, T5, T6, T7, T8, T9, T10], T11);
248        $name!([T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11], T12);
249        $name!([T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12], T13);
250        $name!([T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13], T14);
251        $name!([T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14], T15);
252        $name!([T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15], T16);
253    };
254}
255
256macro_rules! all_the_tuples_no_last_special_case {
257    ($name:ident) => {
258        $name!(T1);
259        $name!(T1, T2);
260        $name!(T1, T2, T3);
261        $name!(T1, T2, T3, T4);
262        $name!(T1, T2, T3, T4, T5);
263        $name!(T1, T2, T3, T4, T5, T6);
264        $name!(T1, T2, T3, T4, T5, T6, T7);
265        $name!(T1, T2, T3, T4, T5, T6, T7, T8);
266        $name!(T1, T2, T3, T4, T5, T6, T7, T8, T9);
267        $name!(T1, T2, T3, T4, T5, T6, T7, T8, T9, T10);
268        $name!(T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11);
269        $name!(T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12);
270        $name!(T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13);
271        $name!(T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14);
272        $name!(T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15);
273        $name!(T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16);
274    };
275}
276
277/// Private API.
278#[doc(hidden)]
279#[macro_export]
280macro_rules! __impl_deref {
281    ($ident:ident) => {
282        impl<T> std::ops::Deref for $ident<T> {
283            type Target = T;
284
285            #[inline]
286            fn deref(&self) -> &Self::Target {
287                &self.0
288            }
289        }
290
291        impl<T> std::ops::DerefMut for $ident<T> {
292            #[inline]
293            fn deref_mut(&mut self) -> &mut Self::Target {
294                &mut self.0
295            }
296        }
297    };
298
299    ($ident:ident: $ty:ty) => {
300        impl std::ops::Deref for $ident {
301            type Target = $ty;
302
303            #[inline]
304            fn deref(&self) -> &Self::Target {
305                &self.0
306            }
307        }
308
309        impl std::ops::DerefMut for $ident {
310            #[inline]
311            fn deref_mut(&mut self) -> &mut Self::Target {
312                &mut self.0
313            }
314        }
315    };
316}
317
318#[cfg(test)]
319mod composite_rejection_tests {
320    use self::defs::*;
321    use crate::Error;
322    use std::error::Error as _;
323
324    #[allow(dead_code, unreachable_pub)]
325    mod defs {
326        __define_rejection! {
327            #[status = BAD_REQUEST]
328            #[body = "error message 1"]
329            pub struct Inner1;
330        }
331        __define_rejection! {
332            #[status = BAD_REQUEST]
333            #[body = "error message 2"]
334            pub struct Inner2(Error);
335        }
336        __composite_rejection! {
337            pub enum Outer { Inner1, Inner2 }
338        }
339    }
340
341    /// The implementation of `.source()` on `Outer` should defer straight to the implementation
342    /// on its inner type instead of returning the inner type itself, because the `Display`
343    /// implementation on `Outer` already forwards to the inner type and so it would result in two
344    /// errors in the chain `Display`ing the same thing.
345    #[test]
346    fn source_gives_inner_source() {
347        let rejection = Outer::Inner1(Inner1);
348        assert!(rejection.source().is_none());
349
350        let msg = "hello world";
351        let rejection = Outer::Inner2(Inner2(Error::new(msg)));
352        assert_eq!(rejection.source().unwrap().to_string(), msg);
353    }
354}