axum_codec/
handler.rs

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
31/// Transforms a function (that returns [`IntoCodecResponse`]) into a regular
32/// handler.
33pub 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);