mirror of
https://github.com/tokio-rs/axum.git
synced 2024-11-23 07:39:25 +01:00
Support customizing the request body type in #[debug_handler]
(#595)
This commit is contained in:
parent
4b85b227e1
commit
8895926f73
7 changed files with 64 additions and 18 deletions
|
@ -7,7 +7,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||
|
||||
# Unreleased
|
||||
|
||||
- None.
|
||||
- Support checking `FromRequest` bounds for extractors whose request body is something else than
|
||||
`axum::body::Body`. Use `#[debug_handler(body = YourBodyType)]` to use a different request body
|
||||
type.
|
||||
|
||||
# 0.3.1 (06. December 2021)
|
||||
|
||||
|
|
|
@ -73,6 +73,22 @@
|
|||
//! }
|
||||
//! ```
|
||||
//!
|
||||
//! # Changing request body type
|
||||
//!
|
||||
//! By default `#[debug_handler]` assumes your request body type is `axum::body::Body`. This will
|
||||
//! work for most extractors but, for example, it wont work for `Request<axum::body::BoxBody>`,
|
||||
//! which only implements `FromRequest<BoxBody>` and _not_ `FromRequest<Body>`.
|
||||
//!
|
||||
//! To work around that the request body type can be customized like so:
|
||||
//!
|
||||
//! ```rust
|
||||
//! use axum::{body::BoxBody, http::Request};
|
||||
//! # use axum_debug::debug_handler;
|
||||
//!
|
||||
//! #[debug_handler(body = BoxBody)]
|
||||
//! async fn handler(request: Request<BoxBody>) {}
|
||||
//! ```
|
||||
//!
|
||||
//! # Performance
|
||||
//!
|
||||
//! Macros in this crate have no effect when using release profile. (eg. `cargo build --release`)
|
||||
|
@ -125,6 +141,8 @@
|
|||
use proc_macro::TokenStream;
|
||||
|
||||
/// Generates better error messages when applied to a handler function.
|
||||
///
|
||||
/// See the [module docs](self) for more details.
|
||||
#[proc_macro_attribute]
|
||||
pub fn debug_handler(_attr: TokenStream, input: TokenStream) -> TokenStream {
|
||||
#[cfg(not(debug_assertions))]
|
||||
|
@ -138,7 +156,7 @@ pub fn debug_handler(_attr: TokenStream, input: TokenStream) -> TokenStream {
|
|||
mod debug_handler {
|
||||
use proc_macro2::TokenStream;
|
||||
use quote::{format_ident, quote, quote_spanned};
|
||||
use syn::{parse::Parse, spanned::Spanned, FnArg, ItemFn};
|
||||
use syn::{parse::Parse, spanned::Spanned, FnArg, ItemFn, Token, Type};
|
||||
|
||||
pub(crate) fn expand(
|
||||
attr: proc_macro::TokenStream,
|
||||
|
@ -151,12 +169,13 @@ mod debug_handler {
|
|||
}
|
||||
|
||||
pub(crate) fn try_expand(attr: TokenStream, input: TokenStream) -> syn::Result<TokenStream> {
|
||||
syn::parse2::<Attrs>(attr)?;
|
||||
let attr = syn::parse2::<Attrs>(attr)?;
|
||||
let item_fn = syn::parse2::<ItemFn>(input.clone())?;
|
||||
|
||||
check_extractor_count(&item_fn)?;
|
||||
|
||||
let check_inputs_impls_from_request = check_inputs_impls_from_request(&item_fn);
|
||||
let check_inputs_impls_from_request =
|
||||
check_inputs_impls_from_request(&item_fn, &attr.body_ty);
|
||||
let check_output_impls_into_response = check_output_impls_into_response(&item_fn);
|
||||
let check_future_send = check_future_send(&item_fn);
|
||||
|
||||
|
@ -170,11 +189,29 @@ mod debug_handler {
|
|||
Ok(tokens)
|
||||
}
|
||||
|
||||
struct Attrs;
|
||||
struct Attrs {
|
||||
body_ty: Type,
|
||||
}
|
||||
|
||||
impl Parse for Attrs {
|
||||
fn parse(_input: syn::parse::ParseStream) -> syn::Result<Self> {
|
||||
Ok(Self)
|
||||
fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
|
||||
let mut body_ty = None;
|
||||
|
||||
while !input.is_empty() {
|
||||
let ident = input.parse::<syn::Ident>()?;
|
||||
if ident == "body" {
|
||||
input.parse::<Token![=]>()?;
|
||||
body_ty = Some(input.parse()?);
|
||||
} else {
|
||||
return Err(syn::Error::new_spanned(ident, "unknown argument"));
|
||||
}
|
||||
|
||||
let _ = input.parse::<Token![,]>();
|
||||
}
|
||||
|
||||
let body_ty = body_ty.unwrap_or_else(|| syn::parse_quote!(axum::body::Body));
|
||||
|
||||
Ok(Self { body_ty })
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -193,7 +230,7 @@ mod debug_handler {
|
|||
}
|
||||
}
|
||||
|
||||
fn check_inputs_impls_from_request(item_fn: &ItemFn) -> TokenStream {
|
||||
fn check_inputs_impls_from_request(item_fn: &ItemFn, body_ty: &Type) -> TokenStream {
|
||||
if !item_fn.sig.generics.params.is_empty() {
|
||||
return syn::Error::new_spanned(
|
||||
&item_fn.sig.generics,
|
||||
|
@ -237,7 +274,7 @@ mod debug_handler {
|
|||
#[allow(warnings)]
|
||||
fn #name()
|
||||
where
|
||||
#ty: ::axum::extract::FromRequest<::axum::body::Body> + Send,
|
||||
#ty: ::axum::extract::FromRequest<#body_ty> + Send,
|
||||
{}
|
||||
}
|
||||
})
|
||||
|
|
|
@ -1,5 +0,0 @@
|
|||
error: unexpected token
|
||||
--> tests/fail/attrs.rs:3:17
|
||||
|
|
||||
3 | #[debug_handler(foo)]
|
||||
| ^^^
|
5
axum-debug/tests/fail/invalid_attrs.stderr
Normal file
5
axum-debug/tests/fail/invalid_attrs.stderr
Normal file
|
@ -0,0 +1,5 @@
|
|||
error: unknown argument
|
||||
--> tests/fail/invalid_attrs.rs:3:17
|
||||
|
|
||||
3 | #[debug_handler(foo)]
|
||||
| ^^^
|
10
axum-debug/tests/pass/different_request_body_type.rs
Normal file
10
axum-debug/tests/pass/different_request_body_type.rs
Normal file
|
@ -0,0 +1,10 @@
|
|||
use axum::{body::BoxBody, http::Request};
|
||||
use axum_debug::debug_handler;
|
||||
|
||||
#[debug_handler(body = BoxBody)]
|
||||
async fn handler(_: Request<BoxBody>) {}
|
||||
|
||||
#[debug_handler(body = axum::body::BoxBody,)]
|
||||
async fn handler_with_trailing_comma_and_type_path(_: Request<axum::body::BoxBody>) {}
|
||||
|
||||
fn main() {}
|
|
@ -1,7 +1,4 @@
|
|||
use axum::{
|
||||
body::BoxBody,
|
||||
response::{IntoResponse, Response},
|
||||
};
|
||||
use axum::response::{IntoResponse, Response};
|
||||
use axum_debug::debug_handler;
|
||||
|
||||
struct A;
|
||||
|
|
Loading…
Reference in a new issue