support both FromRequest and FromRequestParts in Either

This commit is contained in:
David Mládek 2025-05-05 09:42:02 +02:00
parent b7f815f10d
commit b5c534d073
3 changed files with 44 additions and 27 deletions

1
Cargo.lock generated
View File

@ -390,6 +390,7 @@ dependencies = [
"hyper 1.5.2", "hyper 1.5.2",
"mime", "mime",
"multer", "multer",
"paste",
"percent-encoding", "percent-encoding",
"pin-project-lite", "pin-project-lite",
"prost", "prost",

View File

@ -57,6 +57,7 @@ http = "1.0.0"
http-body = "1.0.0" http-body = "1.0.0"
http-body-util = "0.1.0" http-body-util = "0.1.0"
mime = "0.3" mime = "0.3"
paste = "1.0"
pin-project-lite = "0.2" pin-project-lite = "0.2"
rustversion = "1.0.9" rustversion = "1.0.9"
serde = "1.0" serde = "1.0"

View File

@ -134,13 +134,14 @@ use axum::{
}; };
use bytes::Bytes; use bytes::Bytes;
use http::request::Parts; use http::request::Parts;
use paste::paste;
use tower_layer::Layer; use tower_layer::Layer;
use tower_service::Service; use tower_service::Service;
/// Combines two extractors or responses into a single type. /// Combines two extractors or responses into a single type.
/// ///
/// See the [module docs](self) for examples. /// See the [module docs](self) for examples.
#[derive(Debug, Clone)] #[derive(Debug, Clone, PartialEq, Eq)]
#[must_use] #[must_use]
pub enum Either<E1, E2> { pub enum Either<E1, E2> {
#[allow(missing_docs)] #[allow(missing_docs)]
@ -310,39 +311,41 @@ macro_rules! impl_traits_for_either {
} }
} }
impl<S, $($ident),*, $last> FromRequest<S> for $either<$($ident),*, $last> paste! {
where impl<S, $($ident),*, $last, $([< $ident Via >]),*, [<$last Via>]> FromRequest<S, ($([< $ident Via >]),*, [<$last Via>])> for $either<$($ident),*, $last>
S: Send + Sync, where
$($ident: FromRequest<S>),*, S: Send + Sync,
$last: FromRequest<S>, $($ident: FromRequest<S, [<$ident Via>]>),*,
$($ident::Rejection: Send),*, $last: FromRequest<S, [<$last Via>]>,
$last::Rejection: IntoResponse + Send, $($ident::Rejection: Send),*,
{ $last::Rejection: IntoResponse + Send,
type Rejection = EitherRejection<$last::Rejection>; {
type Rejection = EitherRejection<$last::Rejection>;
async fn from_request(req: Request, state: &S) -> Result<Self, Self::Rejection> { async fn from_request(req: Request, state: &S) -> Result<Self, Self::Rejection> {
let (parts, body) = req.into_parts(); let (parts, body) = req.into_parts();
let bytes = Bytes::from_request(Request::from_parts(parts.clone(), body), state) let bytes = Bytes::from_request(Request::from_parts(parts.clone(), body), state)
.await .await
.map_err(EitherRejection::Bytes)?; .map_err(EitherRejection::Bytes)?;
$(
let req = Request::from_parts(
parts.clone(),
axum::body::Body::new(http_body_util::Full::new(bytes.clone())),
);
if let Ok(extracted) = $ident::from_request(req, state).await {
return Ok(Self::$ident(extracted));
}
)*
$(
let req = Request::from_parts( let req = Request::from_parts(
parts.clone(), parts.clone(),
axum::body::Body::new(http_body_util::Full::new(bytes.clone())), axum::body::Body::new(http_body_util::Full::new(bytes.clone())),
); );
if let Ok(extracted) = $ident::from_request(req, state).await { match $last::from_request(req, state).await {
return Ok(Self::$ident(extracted)); Ok(extracted) => Ok(Self::$last(extracted)),
Err(error) => Err(EitherRejection::LastRejection(error)),
} }
)*
let req = Request::from_parts(
parts.clone(),
axum::body::Body::new(http_body_util::Full::new(bytes.clone())),
);
match $last::from_request(req, state).await {
Ok(extracted) => Ok(Self::$last(extracted)),
Err(error) => Err(EitherRejection::LastRejection(error)),
} }
} }
} }
@ -421,6 +424,7 @@ mod tests {
use super::*; use super::*;
#[derive(Debug, PartialEq)]
struct False; struct False;
impl<S> FromRequestParts<S> for False { impl<S> FromRequestParts<S> for False {
@ -471,4 +475,15 @@ mod tests {
assert!(matches!(either, Either3::E3(State(())))); assert!(matches!(either, Either3::E3(State(()))));
} }
#[tokio::test]
async fn either_from_request_or_parts() {
let request = Request::new(Body::empty());
let either = Either::<False, Bytes>::from_request(request, &())
.await
.unwrap();
assert_eq!(either, Either::E2(Bytes::new()));
}
} }