2022-08-10 12:09:08 +02:00
|
|
|
//! Additional handler utilities.
|
|
|
|
|
|
|
|
|
|
use axum::{
|
2022-08-22 12:23:20 +02:00
|
|
|
extract::FromRequest,
|
2022-08-10 12:09:08 +02:00
|
|
|
handler::Handler,
|
|
|
|
|
response::{IntoResponse, Response},
|
|
|
|
|
};
|
|
|
|
|
use futures_util::future::{BoxFuture, FutureExt, Map};
|
2022-08-17 22:08:24 +02:00
|
|
|
use std::{future::Future, marker::PhantomData, sync::Arc};
|
2022-08-10 12:09:08 +02:00
|
|
|
|
|
|
|
|
mod or;
|
|
|
|
|
|
|
|
|
|
pub use self::or::Or;
|
|
|
|
|
|
|
|
|
|
/// Trait for async functions that can be used to handle requests.
|
|
|
|
|
///
|
|
|
|
|
/// This trait is similar to [`Handler`] but rather than taking the request it takes the extracted
|
|
|
|
|
/// inputs.
|
|
|
|
|
///
|
|
|
|
|
/// The drawbacks of this trait is that you cannot apply middleware to individual handlers like you
|
|
|
|
|
/// can with [`Handler::layer`].
|
2022-08-17 17:13:31 +02:00
|
|
|
pub trait HandlerCallWithExtractors<T, S, B>: Sized {
|
2022-08-10 12:09:08 +02:00
|
|
|
/// The type of future calling this handler returns.
|
|
|
|
|
type Future: Future<Output = Response> + Send + 'static;
|
|
|
|
|
|
|
|
|
|
/// Call the handler with the extracted inputs.
|
2022-08-17 22:08:24 +02:00
|
|
|
fn call(
|
|
|
|
|
self,
|
|
|
|
|
extractors: T,
|
2022-08-22 12:23:20 +02:00
|
|
|
state: Arc<S>,
|
2022-08-17 22:08:24 +02:00
|
|
|
) -> <Self as HandlerCallWithExtractors<T, S, B>>::Future;
|
2022-08-10 12:09:08 +02:00
|
|
|
|
|
|
|
|
/// Conver this `HandlerCallWithExtractors` into [`Handler`].
|
2022-08-17 17:13:31 +02:00
|
|
|
fn into_handler(self) -> IntoHandler<Self, T, S, B> {
|
2022-08-10 12:09:08 +02:00
|
|
|
IntoHandler {
|
|
|
|
|
handler: self,
|
|
|
|
|
_marker: PhantomData,
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Chain two handlers together, running the second one if the first one rejects.
|
|
|
|
|
///
|
|
|
|
|
/// Note that this only moves to the next handler if an extractor fails. The response from
|
|
|
|
|
/// handlers are not considered.
|
|
|
|
|
///
|
|
|
|
|
/// # Example
|
|
|
|
|
///
|
|
|
|
|
/// ```
|
|
|
|
|
/// use axum_extra::handler::HandlerCallWithExtractors;
|
|
|
|
|
/// use axum::{
|
|
|
|
|
/// Router,
|
|
|
|
|
/// async_trait,
|
|
|
|
|
/// routing::get,
|
2022-08-22 12:23:20 +02:00
|
|
|
/// extract::FromRequestParts,
|
2022-08-10 12:09:08 +02:00
|
|
|
/// };
|
|
|
|
|
///
|
|
|
|
|
/// // handlers for varying levels of access
|
|
|
|
|
/// async fn admin(admin: AdminPermissions) {
|
|
|
|
|
/// // request came from an admin
|
|
|
|
|
/// }
|
|
|
|
|
///
|
|
|
|
|
/// async fn user(user: User) {
|
|
|
|
|
/// // we have a `User`
|
|
|
|
|
/// }
|
|
|
|
|
///
|
|
|
|
|
/// async fn guest() {
|
|
|
|
|
/// // `AdminPermissions` and `User` failed, so we're just a guest
|
|
|
|
|
/// }
|
|
|
|
|
///
|
|
|
|
|
/// // extractors for checking permissions
|
|
|
|
|
/// struct AdminPermissions {}
|
|
|
|
|
///
|
|
|
|
|
/// #[async_trait]
|
2022-08-22 12:23:20 +02:00
|
|
|
/// impl<S> FromRequestParts<S> for AdminPermissions
|
2022-08-17 17:13:31 +02:00
|
|
|
/// where
|
2022-08-17 22:08:24 +02:00
|
|
|
/// S: Send + Sync,
|
2022-08-17 17:13:31 +02:00
|
|
|
/// {
|
2022-08-10 12:09:08 +02:00
|
|
|
/// // check for admin permissions...
|
|
|
|
|
/// # type Rejection = ();
|
2022-08-22 12:23:20 +02:00
|
|
|
/// # async fn from_request_parts(parts: &mut http::request::Parts, state: &S) -> Result<Self, Self::Rejection> {
|
2022-08-10 12:09:08 +02:00
|
|
|
/// # todo!()
|
|
|
|
|
/// # }
|
|
|
|
|
/// }
|
|
|
|
|
///
|
|
|
|
|
/// struct User {}
|
|
|
|
|
///
|
|
|
|
|
/// #[async_trait]
|
2022-08-22 12:23:20 +02:00
|
|
|
/// impl<S> FromRequestParts<S> for User
|
2022-08-17 17:13:31 +02:00
|
|
|
/// where
|
2022-08-17 22:08:24 +02:00
|
|
|
/// S: Send + Sync,
|
2022-08-17 17:13:31 +02:00
|
|
|
/// {
|
2022-08-10 12:09:08 +02:00
|
|
|
/// // check for a logged in user...
|
|
|
|
|
/// # type Rejection = ();
|
2022-08-22 12:23:20 +02:00
|
|
|
/// # async fn from_request_parts(parts: &mut http::request::Parts, state: &S) -> Result<Self, Self::Rejection> {
|
2022-08-10 12:09:08 +02:00
|
|
|
/// # todo!()
|
|
|
|
|
/// # }
|
|
|
|
|
/// }
|
|
|
|
|
///
|
|
|
|
|
/// let app = Router::new().route(
|
|
|
|
|
/// "/users/:id",
|
|
|
|
|
/// get(
|
|
|
|
|
/// // first try `admin`, if that rejects run `user`, finally falling back
|
|
|
|
|
/// // to `guest`
|
|
|
|
|
/// admin.or(user).or(guest)
|
|
|
|
|
/// )
|
|
|
|
|
/// );
|
|
|
|
|
/// # let _: Router = app;
|
|
|
|
|
/// ```
|
2022-08-17 17:13:31 +02:00
|
|
|
fn or<R, Rt>(self, rhs: R) -> Or<Self, R, T, Rt, S, B>
|
2022-08-10 12:09:08 +02:00
|
|
|
where
|
2022-08-17 17:13:31 +02:00
|
|
|
R: HandlerCallWithExtractors<Rt, S, B>,
|
2022-08-10 12:09:08 +02:00
|
|
|
{
|
|
|
|
|
Or {
|
|
|
|
|
lhs: self,
|
|
|
|
|
rhs,
|
|
|
|
|
_marker: PhantomData,
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
macro_rules! impl_handler_call_with {
|
2022-08-22 12:23:20 +02:00
|
|
|
( $($ty:ident),* $(,)? ) => {
|
|
|
|
|
#[allow(non_snake_case)]
|
|
|
|
|
impl<F, Fut, S, B, $($ty,)*> HandlerCallWithExtractors<($($ty,)*), S, B> for F
|
|
|
|
|
where
|
|
|
|
|
F: FnOnce($($ty,)*) -> Fut,
|
|
|
|
|
Fut: Future + Send + 'static,
|
|
|
|
|
Fut::Output: IntoResponse,
|
|
|
|
|
{
|
|
|
|
|
// this puts `futures_util` in our public API but thats fine in axum-extra
|
|
|
|
|
type Future = Map<Fut, fn(Fut::Output) -> Response>;
|
2022-08-10 12:09:08 +02:00
|
|
|
|
2022-08-22 12:23:20 +02:00
|
|
|
fn call(
|
|
|
|
|
self,
|
|
|
|
|
($($ty,)*): ($($ty,)*),
|
|
|
|
|
_state: Arc<S>,
|
|
|
|
|
) -> <Self as HandlerCallWithExtractors<($($ty,)*), S, B>>::Future {
|
|
|
|
|
self($($ty,)*).map(IntoResponse::into_response)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
}
|
2022-08-10 12:09:08 +02:00
|
|
|
|
|
|
|
|
impl_handler_call_with!();
|
|
|
|
|
impl_handler_call_with!(T1);
|
|
|
|
|
impl_handler_call_with!(T1, T2);
|
|
|
|
|
impl_handler_call_with!(T1, T2, T3);
|
|
|
|
|
impl_handler_call_with!(T1, T2, T3, T4);
|
|
|
|
|
impl_handler_call_with!(T1, T2, T3, T4, T5);
|
|
|
|
|
impl_handler_call_with!(T1, T2, T3, T4, T5, T6);
|
|
|
|
|
impl_handler_call_with!(T1, T2, T3, T4, T5, T6, T7);
|
|
|
|
|
impl_handler_call_with!(T1, T2, T3, T4, T5, T6, T7, T8);
|
|
|
|
|
impl_handler_call_with!(T1, T2, T3, T4, T5, T6, T7, T8, T9);
|
|
|
|
|
impl_handler_call_with!(T1, T2, T3, T4, T5, T6, T7, T8, T9, T10);
|
|
|
|
|
impl_handler_call_with!(T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11);
|
|
|
|
|
impl_handler_call_with!(T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12);
|
|
|
|
|
impl_handler_call_with!(T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13);
|
|
|
|
|
impl_handler_call_with!(T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14);
|
|
|
|
|
impl_handler_call_with!(T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15);
|
|
|
|
|
impl_handler_call_with!(T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16);
|
|
|
|
|
|
|
|
|
|
/// A [`Handler`] created from a [`HandlerCallWithExtractors`].
|
|
|
|
|
///
|
|
|
|
|
/// Created with [`HandlerCallWithExtractors::into_handler`].
|
|
|
|
|
#[allow(missing_debug_implementations)]
|
2022-08-17 17:13:31 +02:00
|
|
|
pub struct IntoHandler<H, T, S, B> {
|
2022-08-10 12:09:08 +02:00
|
|
|
handler: H,
|
2022-08-17 17:13:31 +02:00
|
|
|
_marker: PhantomData<fn() -> (T, S, B)>,
|
2022-08-10 12:09:08 +02:00
|
|
|
}
|
|
|
|
|
|
2022-08-17 17:13:31 +02:00
|
|
|
impl<H, T, S, B> Handler<T, S, B> for IntoHandler<H, T, S, B>
|
2022-08-10 12:09:08 +02:00
|
|
|
where
|
2022-08-17 17:13:31 +02:00
|
|
|
H: HandlerCallWithExtractors<T, S, B> + Clone + Send + 'static,
|
|
|
|
|
T: FromRequest<S, B> + Send + 'static,
|
2022-08-10 12:09:08 +02:00
|
|
|
T::Rejection: Send,
|
|
|
|
|
B: Send + 'static,
|
2022-08-17 22:08:24 +02:00
|
|
|
S: Send + Sync + 'static,
|
2022-08-10 12:09:08 +02:00
|
|
|
{
|
|
|
|
|
type Future = BoxFuture<'static, Response>;
|
|
|
|
|
|
2022-08-22 12:23:20 +02:00
|
|
|
fn call(self, req: http::Request<B>, state: Arc<S>) -> Self::Future {
|
2022-08-10 12:09:08 +02:00
|
|
|
Box::pin(async move {
|
2022-08-22 12:23:20 +02:00
|
|
|
match T::from_request(req, &state).await {
|
|
|
|
|
Ok(t) => self.handler.call(t, state).await,
|
2022-08-10 12:09:08 +02:00
|
|
|
Err(rejection) => rejection.into_response(),
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2022-08-17 17:13:31 +02:00
|
|
|
impl<H, T, S, B> Copy for IntoHandler<H, T, S, B> where H: Copy {}
|
2022-08-10 12:09:08 +02:00
|
|
|
|
2022-08-17 17:13:31 +02:00
|
|
|
impl<H, T, S, B> Clone for IntoHandler<H, T, S, B>
|
2022-08-10 12:09:08 +02:00
|
|
|
where
|
|
|
|
|
H: Clone,
|
|
|
|
|
{
|
|
|
|
|
fn clone(&self) -> Self {
|
|
|
|
|
Self {
|
|
|
|
|
handler: self.handler.clone(),
|
|
|
|
|
_marker: self._marker,
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|