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