mirror of
https://github.com/tokio-rs/axum.git
synced 2024-12-27 23:10:20 +01:00
Improve debug_handler message for generic request-consuming extractors (#1826)
Co-authored-by: David Pedersen <david.pdrsn@gmail.com>
This commit is contained in:
parent
1327a598ce
commit
a26ddd1063
4 changed files with 73 additions and 31 deletions
|
@ -7,7 +7,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||||
|
|
||||||
# Unreleased
|
# Unreleased
|
||||||
|
|
||||||
- None.
|
- **fixed:** Improve `#[debug_handler]` message for known generic
|
||||||
|
request-consuming extractors ([#1826])
|
||||||
|
|
||||||
|
[#1826]: https://github.com/tokio-rs/axum/pull/1826
|
||||||
|
|
||||||
# 0.3.5 (03. March, 2023)
|
# 0.3.5 (03. March, 2023)
|
||||||
|
|
||||||
|
|
|
@ -228,6 +228,8 @@ fn check_inputs_impls_from_request(
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let consumes_request = request_consuming_type_name(&ty).is_some();
|
||||||
|
|
||||||
let check_fn = format_ident!(
|
let check_fn = format_ident!(
|
||||||
"__axum_macros_check_{}_{}_from_request_check",
|
"__axum_macros_check_{}_{}_from_request_check",
|
||||||
item_fn.sig.ident,
|
item_fn.sig.ident,
|
||||||
|
@ -252,7 +254,7 @@ fn check_inputs_impls_from_request(
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
let check_fn_generics = if must_impl_from_request_parts {
|
let check_fn_generics = if must_impl_from_request_parts || consumes_request {
|
||||||
quote! {}
|
quote! {}
|
||||||
} else {
|
} else {
|
||||||
quote! { <M> }
|
quote! { <M> }
|
||||||
|
@ -262,6 +264,10 @@ fn check_inputs_impls_from_request(
|
||||||
quote_spanned! {span=>
|
quote_spanned! {span=>
|
||||||
#ty: ::axum::extract::FromRequestParts<#state_ty> + Send
|
#ty: ::axum::extract::FromRequestParts<#state_ty> + Send
|
||||||
}
|
}
|
||||||
|
} else if consumes_request {
|
||||||
|
quote_spanned! {span=>
|
||||||
|
#ty: ::axum::extract::FromRequest<#state_ty, #body_ty> + Send
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
quote_spanned! {span=>
|
quote_spanned! {span=>
|
||||||
#ty: ::axum::extract::FromRequest<#state_ty, #body_ty, M> + Send
|
#ty: ::axum::extract::FromRequest<#state_ty, #body_ty, M> + Send
|
||||||
|
@ -300,35 +306,9 @@ fn check_input_order(item_fn: &ItemFn) -> Option<TokenStream> {
|
||||||
FnArg::Typed(pat_type) => &*pat_type.ty,
|
FnArg::Typed(pat_type) => &*pat_type.ty,
|
||||||
FnArg::Receiver(_) => return None,
|
FnArg::Receiver(_) => return None,
|
||||||
};
|
};
|
||||||
let span = ty.span();
|
let type_name = request_consuming_type_name(ty)?;
|
||||||
|
|
||||||
let path = match ty {
|
Some((idx, type_name, ty.span()))
|
||||||
Type::Path(type_path) => &type_path.path,
|
|
||||||
_ => return None,
|
|
||||||
};
|
|
||||||
|
|
||||||
let ident = match path.segments.last() {
|
|
||||||
Some(path_segment) => &path_segment.ident,
|
|
||||||
None => return None,
|
|
||||||
};
|
|
||||||
|
|
||||||
let type_name = match &*ident.to_string() {
|
|
||||||
"Json" => "Json<_>",
|
|
||||||
"BodyStream" => "BodyStream",
|
|
||||||
"RawBody" => "RawBody<_>",
|
|
||||||
"RawForm" => "RawForm",
|
|
||||||
"Multipart" => "Multipart",
|
|
||||||
"Protobuf" => "Protobuf",
|
|
||||||
"JsonLines" => "JsonLines<_>",
|
|
||||||
"Form" => "Form<_>",
|
|
||||||
"Request" => "Request<_>",
|
|
||||||
"Bytes" => "Bytes",
|
|
||||||
"String" => "String",
|
|
||||||
"Parts" => "Parts",
|
|
||||||
_ => return None,
|
|
||||||
};
|
|
||||||
|
|
||||||
Some((idx, type_name, span))
|
|
||||||
})
|
})
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
|
@ -343,7 +323,7 @@ fn check_input_order(item_fn: &ItemFn) -> Option<TokenStream> {
|
||||||
let (_idx, type_name, span) = &types_that_consume_the_request[0];
|
let (_idx, type_name, span) = &types_that_consume_the_request[0];
|
||||||
let error = format!(
|
let error = format!(
|
||||||
"`{type_name}` consumes the request body and thus must be \
|
"`{type_name}` consumes the request body and thus must be \
|
||||||
the last argument to the handler function"
|
the last argument to the handler function"
|
||||||
);
|
);
|
||||||
return Some(quote_spanned! {*span=>
|
return Some(quote_spanned! {*span=>
|
||||||
compile_error!(#error);
|
compile_error!(#error);
|
||||||
|
@ -386,6 +366,36 @@ fn check_input_order(item_fn: &ItemFn) -> Option<TokenStream> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn request_consuming_type_name(ty: &Type) -> Option<&'static str> {
|
||||||
|
let path = match ty {
|
||||||
|
Type::Path(type_path) => &type_path.path,
|
||||||
|
_ => return None,
|
||||||
|
};
|
||||||
|
|
||||||
|
let ident = match path.segments.last() {
|
||||||
|
Some(path_segment) => &path_segment.ident,
|
||||||
|
None => return None,
|
||||||
|
};
|
||||||
|
|
||||||
|
let type_name = match &*ident.to_string() {
|
||||||
|
"Json" => "Json<_>",
|
||||||
|
"BodyStream" => "BodyStream",
|
||||||
|
"RawBody" => "RawBody<_>",
|
||||||
|
"RawForm" => "RawForm",
|
||||||
|
"Multipart" => "Multipart",
|
||||||
|
"Protobuf" => "Protobuf",
|
||||||
|
"JsonLines" => "JsonLines<_>",
|
||||||
|
"Form" => "Form<_>",
|
||||||
|
"Request" => "Request<_>",
|
||||||
|
"Bytes" => "Bytes",
|
||||||
|
"String" => "String",
|
||||||
|
"Parts" => "Parts",
|
||||||
|
_ => return None,
|
||||||
|
};
|
||||||
|
|
||||||
|
Some(type_name)
|
||||||
|
}
|
||||||
|
|
||||||
fn check_output_impls_into_response(item_fn: &ItemFn) -> TokenStream {
|
fn check_output_impls_into_response(item_fn: &ItemFn) -> TokenStream {
|
||||||
let ty = match &item_fn.sig.output {
|
let ty = match &item_fn.sig.output {
|
||||||
syn::ReturnType::Default => return quote! {},
|
syn::ReturnType::Default => return quote! {},
|
||||||
|
|
|
@ -0,0 +1,9 @@
|
||||||
|
use axum::Json;
|
||||||
|
use axum_macros::debug_handler;
|
||||||
|
|
||||||
|
struct Struct {}
|
||||||
|
|
||||||
|
#[debug_handler]
|
||||||
|
async fn handler(foo: Json<Struct>) {}
|
||||||
|
|
||||||
|
fn main() {}
|
|
@ -0,0 +1,20 @@
|
||||||
|
error[E0277]: the trait bound `for<'de> Struct: serde::de::Deserialize<'de>` is not satisfied
|
||||||
|
--> tests/debug_handler/fail/json_not_deserialize.rs:7:23
|
||||||
|
|
|
||||||
|
7 | async fn handler(foo: Json<Struct>) {}
|
||||||
|
| ^^^^^^^^^^^^ the trait `for<'de> serde::de::Deserialize<'de>` is not implemented for `Struct`
|
||||||
|
|
|
||||||
|
= help: the following other types implement trait `serde::de::Deserialize<'de>`:
|
||||||
|
&'a [u8]
|
||||||
|
&'a serde_json::raw::RawValue
|
||||||
|
&'a std::path::Path
|
||||||
|
&'a str
|
||||||
|
()
|
||||||
|
(T0, T1)
|
||||||
|
(T0, T1, T2)
|
||||||
|
(T0, T1, T2, T3)
|
||||||
|
and $N others
|
||||||
|
= note: required for `Struct` to implement `serde::de::DeserializeOwned`
|
||||||
|
= note: required for `Json<Struct>` to implement `FromRequest<(), Body>`
|
||||||
|
= help: see issue #48214
|
||||||
|
= help: add `#![feature(trivial_bounds)]` to the crate attributes to enable
|
Loading…
Reference in a new issue