1use std::{future::Future, pin::Pin};
2
3use axum::{
4 extract::{FromRequest, FromRequestParts, Request},
5 handler::Handler,
6 response::{IntoResponse, Response},
7};
8
9use crate::{Accept, IntoCodecResponse};
10
11#[cfg(not(feature = "aide"))]
12pub trait Input {}
13#[cfg(not(feature = "aide"))]
14impl<T> Input for T {}
15
16#[cfg(feature = "aide")]
17pub trait Input: aide::OperationInput {}
18#[cfg(feature = "aide")]
19impl<T> Input for T where T: aide::OperationInput {}
20
21#[diagnostic::on_unimplemented(
22 note = "Consider wrapping the return value in `Codec` if appropriate",
23 note = "Consider using `#[axum_codec::debug_handler]` to improve the error message"
24)]
25pub trait CodecHandler<T, I: Input, D, S>: Clone + Send + 'static {
26 type Future: Future<Output = Response> + Send;
27
28 fn call(self, req: Request, state: S) -> Self::Future;
29}
30
31pub struct CodecHandlerFn<H, I, D> {
34 pub(crate) f: H,
35 pub(crate) _marker: std::marker::PhantomData<(I, fn() -> D)>,
36}
37
38impl<H, I, D> CodecHandlerFn<H, I, D> {
39 pub(crate) fn new(f: H) -> Self {
40 Self {
41 f,
42 _marker: std::marker::PhantomData,
43 }
44 }
45}
46
47impl<H, I, D> Clone for CodecHandlerFn<H, I, D>
48where
49 H: Clone,
50{
51 fn clone(&self) -> Self {
52 Self {
53 f: self.f.clone(),
54 _marker: std::marker::PhantomData,
55 }
56 }
57}
58
59#[cfg(feature = "aide")]
60impl<H, I, D> aide::OperationInput for CodecHandlerFn<H, I, D>
61where
62 I: aide::OperationInput,
63{
64 fn operation_input(
65 ctx: &mut aide::generate::GenContext,
66 operation: &mut aide::openapi::Operation,
67 ) {
68 I::operation_input(ctx, operation);
69 }
70
71 fn inferred_early_responses(
72 ctx: &mut aide::generate::GenContext,
73 operation: &mut aide::openapi::Operation,
74 ) -> Vec<(Option<u16>, aide::openapi::Response)> {
75 I::inferred_early_responses(ctx, operation)
76 }
77}
78
79#[cfg(feature = "aide")]
80impl<H, I, D> aide::OperationOutput for CodecHandlerFn<H, I, D>
81where
82 D: aide::OperationOutput,
83{
84 type Inner = D;
85
86 fn operation_response(
87 ctx: &mut aide::generate::GenContext,
88 operation: &mut aide::openapi::Operation,
89 ) -> Option<aide::openapi::Response> {
90 D::operation_response(ctx, operation)
91 }
92
93 fn inferred_responses(
94 ctx: &mut aide::generate::GenContext,
95 operation: &mut aide::openapi::Operation,
96 ) -> Vec<(Option<u16>, aide::openapi::Response)> {
97 D::inferred_responses(ctx, operation)
98 }
99}
100
101#[cfg(feature = "aide")]
102impl<F, I, D> aide::operation::OperationHandler<I, crate::Codec<D>> for CodecHandlerFn<F, I, D>
103where
104 I: aide::OperationInput,
105 D: schemars::JsonSchema,
106{
107}
108
109#[cfg(feature = "aide")]
110impl<F, I, D> aide::operation::OperationHandler<I, D> for CodecHandlerFn<F, I, D>
111where
112 I: aide::OperationInput,
113 D: aide::OperationOutput,
114{
115}
116
117impl<T, H, I, D, S> Handler<T, S> for CodecHandlerFn<H, I, D>
118where
119 H: CodecHandler<T, I, D, S> + Sync,
120 S: Send + 'static,
121 I: Input + Send + Sync + 'static,
122 D: IntoCodecResponse + Send + 'static,
123{
124 type Future = Pin<Box<dyn Future<Output = Response> + Send>>;
125
126 #[inline]
127 fn call(self, req: Request, state: S) -> Self::Future {
128 Box::pin(async move { CodecHandler::<T, I, D, S>::call(self.f, req, state).await })
129 }
130}
131
132impl<F, Fut, Res, S> CodecHandler<((),), (), Res, S> for F
133where
134 F: FnOnce() -> Fut + Clone + Send + 'static,
135 Fut: Future<Output = Res> + Send,
136 Res: IntoCodecResponse,
137 S: Send + Sync + 'static,
138{
139 type Future = Pin<Box<dyn Future<Output = Response> + Send>>;
140
141 fn call(self, req: Request, state: S) -> Self::Future {
142 Box::pin(async move {
143 let (mut parts, ..) = req.into_parts();
144
145 let content_type = Accept::from_request_parts(&mut parts, &state)
146 .await
147 .unwrap();
148
149 self().await.into_codec_response(content_type.into())
150 })
151 }
152}
153
154macro_rules! impl_handler {
155 (
156 [$($ty:ident),*], $last:ident
157 ) => {
158 #[allow(non_snake_case, unused_mut)]
159 impl<F, Fut, S, Res, M, $($ty,)* $last> CodecHandler<(M, $($ty,)* $last,), ($($ty,)* $last,), Res, S> for F
160 where
161 F: FnOnce($($ty,)* $last,) -> Fut + Clone + Send + 'static,
162 Fut: Future<Output = Res> + Send,
163 S: Send + Sync + 'static,
164 Res: IntoCodecResponse,
165 $( $ty: Input + FromRequestParts<S> + Send, )*
166 $last: Input + FromRequest<S, M> + Send,
167 {
168 type Future = Pin<Box<dyn Future<Output = Response> + Send>>;
169
170 fn call(self, req: Request, state: S) -> Self::Future {
171 Box::pin(async move {
172 let (mut parts, body) = req.into_parts();
173
174 let content_type = Accept::from_request_parts(&mut parts, &state).await.unwrap();
175
176 $(
177 let $ty = match $ty::from_request_parts(&mut parts, &state).await {
178 Ok(value) => value,
179 Err(rejection) => return rejection.into_response(),
180 };
181 )*
182
183 let req = Request::from_parts(parts, body);
184
185 let $last = match $last::from_request(req, &state).await {
186 Ok(value) => value,
187 Err(rejection) => return rejection.into_response(),
188 };
189
190 self($($ty,)* $last,).await
191 .into_codec_response(content_type.into())
192 })
193 }
194 }
195 };
196}
197
198macro_rules! all_the_tuples {
199 ($name:ident) => {
200 $name!([], T1);
201 $name!([T1], T2);
202 $name!([T1, T2], T3);
203 $name!([T1, T2, T3], T4);
204 $name!([T1, T2, T3, T4], T5);
205 $name!([T1, T2, T3, T4, T5], T6);
206 $name!([T1, T2, T3, T4, T5, T6], T7);
207 $name!([T1, T2, T3, T4, T5, T6, T7], T8);
208 $name!([T1, T2, T3, T4, T5, T6, T7, T8], T9);
209 $name!([T1, T2, T3, T4, T5, T6, T7, T8, T9], T10);
210 $name!([T1, T2, T3, T4, T5, T6, T7, T8, T9, T10], T11);
211 $name!([T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11], T12);
212 $name!([T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12], T13);
213 $name!(
214 [T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13],
215 T14
216 );
217 $name!(
218 [T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14],
219 T15
220 );
221 $name!(
222 [T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15],
223 T16
224 );
225 };
226}
227
228all_the_tuples!(impl_handler);