axum_extra/
either.rs

1//! `Either*` types for combining extractors or responses into a single type.
2//!
3//! # As an extractor
4//!
5//! ```
6//! use axum_extra::either::Either3;
7//! use axum::{
8//!     body::Bytes,
9//!     Router,
10//!     routing::get,
11//!     extract::FromRequestParts,
12//! };
13//!
14//! // extractors for checking permissions
15//! struct AdminPermissions {}
16//!
17//! impl<S> FromRequestParts<S> for AdminPermissions
18//! where
19//!     S: Send + Sync,
20//! {
21//!     // check for admin permissions...
22//!     # type Rejection = ();
23//!     # async fn from_request_parts(parts: &mut axum::http::request::Parts, state: &S) -> Result<Self, Self::Rejection> {
24//!     #     todo!()
25//!     # }
26//! }
27//!
28//! struct User {}
29//!
30//! impl<S> FromRequestParts<S> for User
31//! where
32//!     S: Send + Sync,
33//! {
34//!     // check for a logged in user...
35//!     # type Rejection = ();
36//!     # async fn from_request_parts(parts: &mut axum::http::request::Parts, state: &S) -> Result<Self, Self::Rejection> {
37//!     #     todo!()
38//!     # }
39//! }
40//!
41//! async fn handler(
42//!     body: Either3<AdminPermissions, User, ()>,
43//! ) {
44//!     match body {
45//!         Either3::E1(admin) => { /* ... */ }
46//!         Either3::E2(user) => { /* ... */ }
47//!         Either3::E3(guest) => { /* ... */ }
48//!     }
49//! }
50//! #
51//! # let _: axum::routing::MethodRouter = axum::routing::get(handler);
52//! ```
53//!
54//! Note that if all the inner extractors reject the request, the rejection from the last
55//! extractor will be returned. For the example above that would be [`BytesRejection`].
56//!
57//! # As a response
58//!
59//! ```
60//! use axum_extra::either::Either3;
61//! use axum::{Json, http::StatusCode, response::IntoResponse};
62//! use serde_json::{Value, json};
63//!
64//! async fn handler() -> Either3<Json<Value>, &'static str, StatusCode> {
65//!     if something() {
66//!         Either3::E1(Json(json!({ "data": "..." })))
67//!     } else if something_else() {
68//!         Either3::E2("foobar")
69//!     } else {
70//!         Either3::E3(StatusCode::NOT_FOUND)
71//!     }
72//! }
73//!
74//! fn something() -> bool {
75//!     // ...
76//!     # false
77//! }
78//!
79//! fn something_else() -> bool {
80//!     // ...
81//!     # false
82//! }
83//! #
84//! # let _: axum::routing::MethodRouter = axum::routing::get(handler);
85//! ```
86//!
87//! The general recommendation is to use [`IntoResponse::into_response`] to return different response
88//! types, but if you need to preserve the exact type then `Either*` works as well.
89//!
90//! [`BytesRejection`]: axum::extract::rejection::BytesRejection
91//! [`IntoResponse::into_response`]: https://docs.rs/axum/0.8/axum/response/index.html#returning-different-response-types
92
93use std::task::{Context, Poll};
94
95use axum::{
96    extract::FromRequestParts,
97    response::{IntoResponse, Response},
98};
99use http::request::Parts;
100use tower_layer::Layer;
101use tower_service::Service;
102
103/// Combines two extractors or responses into a single type.
104///
105/// See the [module docs](self) for examples.
106#[derive(Debug, Clone)]
107#[must_use]
108pub enum Either<E1, E2> {
109    #[allow(missing_docs)]
110    E1(E1),
111    #[allow(missing_docs)]
112    E2(E2),
113}
114
115/// Combines three extractors or responses into a single type.
116///
117/// See the [module docs](self) for examples.
118#[derive(Debug, Clone)]
119#[must_use]
120pub enum Either3<E1, E2, E3> {
121    #[allow(missing_docs)]
122    E1(E1),
123    #[allow(missing_docs)]
124    E2(E2),
125    #[allow(missing_docs)]
126    E3(E3),
127}
128
129/// Combines four extractors or responses into a single type.
130///
131/// See the [module docs](self) for examples.
132#[derive(Debug, Clone)]
133#[must_use]
134pub enum Either4<E1, E2, E3, E4> {
135    #[allow(missing_docs)]
136    E1(E1),
137    #[allow(missing_docs)]
138    E2(E2),
139    #[allow(missing_docs)]
140    E3(E3),
141    #[allow(missing_docs)]
142    E4(E4),
143}
144
145/// Combines five extractors or responses into a single type.
146///
147/// See the [module docs](self) for examples.
148#[derive(Debug, Clone)]
149#[must_use]
150pub enum Either5<E1, E2, E3, E4, E5> {
151    #[allow(missing_docs)]
152    E1(E1),
153    #[allow(missing_docs)]
154    E2(E2),
155    #[allow(missing_docs)]
156    E3(E3),
157    #[allow(missing_docs)]
158    E4(E4),
159    #[allow(missing_docs)]
160    E5(E5),
161}
162
163/// Combines six extractors or responses into a single type.
164///
165/// See the [module docs](self) for examples.
166#[derive(Debug, Clone)]
167#[must_use]
168pub enum Either6<E1, E2, E3, E4, E5, E6> {
169    #[allow(missing_docs)]
170    E1(E1),
171    #[allow(missing_docs)]
172    E2(E2),
173    #[allow(missing_docs)]
174    E3(E3),
175    #[allow(missing_docs)]
176    E4(E4),
177    #[allow(missing_docs)]
178    E5(E5),
179    #[allow(missing_docs)]
180    E6(E6),
181}
182
183/// Combines seven extractors or responses into a single type.
184///
185/// See the [module docs](self) for examples.
186#[derive(Debug, Clone)]
187#[must_use]
188pub enum Either7<E1, E2, E3, E4, E5, E6, E7> {
189    #[allow(missing_docs)]
190    E1(E1),
191    #[allow(missing_docs)]
192    E2(E2),
193    #[allow(missing_docs)]
194    E3(E3),
195    #[allow(missing_docs)]
196    E4(E4),
197    #[allow(missing_docs)]
198    E5(E5),
199    #[allow(missing_docs)]
200    E6(E6),
201    #[allow(missing_docs)]
202    E7(E7),
203}
204
205/// Combines eight extractors or responses into a single type.
206///
207/// See the [module docs](self) for examples.
208#[derive(Debug, Clone)]
209#[must_use]
210pub enum Either8<E1, E2, E3, E4, E5, E6, E7, E8> {
211    #[allow(missing_docs)]
212    E1(E1),
213    #[allow(missing_docs)]
214    E2(E2),
215    #[allow(missing_docs)]
216    E3(E3),
217    #[allow(missing_docs)]
218    E4(E4),
219    #[allow(missing_docs)]
220    E5(E5),
221    #[allow(missing_docs)]
222    E6(E6),
223    #[allow(missing_docs)]
224    E7(E7),
225    #[allow(missing_docs)]
226    E8(E8),
227}
228
229macro_rules! impl_traits_for_either {
230    (
231        $either:ident =>
232        [$($ident:ident),* $(,)?],
233        $last:ident $(,)?
234    ) => {
235        impl<S, $($ident),*, $last> FromRequestParts<S> for $either<$($ident),*, $last>
236        where
237            $($ident: FromRequestParts<S>),*,
238            $last: FromRequestParts<S>,
239            S: Send + Sync,
240        {
241            type Rejection = $last::Rejection;
242
243            async fn from_request_parts(parts: &mut Parts, state: &S) -> Result<Self, Self::Rejection> {
244                $(
245                    if let Ok(value) = <$ident as FromRequestParts<S>>::from_request_parts(parts, state).await {
246                        return Ok(Self::$ident(value));
247                    }
248                )*
249
250                <$last as FromRequestParts<S>>::from_request_parts(parts, state).await.map(Self::$last)
251            }
252        }
253
254        impl<$($ident),*, $last> IntoResponse for $either<$($ident),*, $last>
255        where
256            $($ident: IntoResponse),*,
257            $last: IntoResponse,
258        {
259            fn into_response(self) -> Response {
260                match self {
261                    $( Self::$ident(value) => value.into_response(), )*
262                    Self::$last(value) => value.into_response(),
263                }
264            }
265        }
266    };
267}
268
269impl_traits_for_either!(Either => [E1], E2);
270impl_traits_for_either!(Either3 => [E1, E2], E3);
271impl_traits_for_either!(Either4 => [E1, E2, E3], E4);
272impl_traits_for_either!(Either5 => [E1, E2, E3, E4], E5);
273impl_traits_for_either!(Either6 => [E1, E2, E3, E4, E5], E6);
274impl_traits_for_either!(Either7 => [E1, E2, E3, E4, E5, E6], E7);
275impl_traits_for_either!(Either8 => [E1, E2, E3, E4, E5, E6, E7], E8);
276
277impl<E1, E2, S> Layer<S> for Either<E1, E2>
278where
279    E1: Layer<S>,
280    E2: Layer<S>,
281{
282    type Service = Either<E1::Service, E2::Service>;
283
284    fn layer(&self, inner: S) -> Self::Service {
285        match self {
286            Either::E1(layer) => Either::E1(layer.layer(inner)),
287            Either::E2(layer) => Either::E2(layer.layer(inner)),
288        }
289    }
290}
291
292impl<R, E1, E2> Service<R> for Either<E1, E2>
293where
294    E1: Service<R>,
295    E2: Service<R, Response = E1::Response, Error = E1::Error>,
296{
297    type Response = E1::Response;
298    type Error = E1::Error;
299    type Future = futures_util::future::Either<E1::Future, E2::Future>;
300
301    fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
302        match self {
303            Either::E1(inner) => inner.poll_ready(cx),
304            Either::E2(inner) => inner.poll_ready(cx),
305        }
306    }
307
308    fn call(&mut self, req: R) -> Self::Future {
309        match self {
310            Either::E1(inner) => futures_util::future::Either::Left(inner.call(req)),
311            Either::E2(inner) => futures_util::future::Either::Right(inner.call(req)),
312        }
313    }
314}