mirror of
https://github.com/tokio-rs/axum.git
synced 2024-10-24 09:56:56 +02:00
Make axum-debug
handle more cases (#518)
* Make `axum-debug` handle more cases * Only just trybuild tests on stable * revert changes to hello-world example * Remove a bit of duplication * return error on generics * address review feedback * Support associated functions with receiver or returns `Self` * fix indentation
This commit is contained in:
parent
22931688f7
commit
f1f004a057
24 changed files with 408 additions and 275 deletions
|
@ -7,6 +7,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||
|
||||
# Unreleased
|
||||
|
||||
- Make macro handle more cases such as mutable extractors and handlers taking
|
||||
`self` ([#518])
|
||||
|
||||
[#518]: https://github.com/tokio-rs/axum/pull/518
|
||||
|
||||
# 0.2.0 (13. October 2021)
|
||||
|
||||
- **breaking:** Removed `debug_router` macro.
|
||||
|
|
|
@ -21,3 +21,4 @@ syn = { version = "1.0", features = ["full"] }
|
|||
[dev-dependencies]
|
||||
axum = { path = "../axum", version = "0.3" }
|
||||
trybuild = "1.0"
|
||||
rustversion = "1.0"
|
||||
|
|
|
@ -124,298 +124,273 @@
|
|||
use proc_macro::TokenStream;
|
||||
|
||||
/// Generates better error messages when applied to a handler function.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// Function is not async:
|
||||
///
|
||||
/// ```rust,compile_fail
|
||||
/// #[axum_debug::debug_handler]
|
||||
/// fn handler() -> &'static str {
|
||||
/// "Hello, world"
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// ```text
|
||||
/// error: handlers must be async functions
|
||||
/// --> main.rs:xx:1
|
||||
/// |
|
||||
/// xx | fn handler() -> &'static str {
|
||||
/// | ^^
|
||||
/// ```
|
||||
///
|
||||
/// Wrong return type:
|
||||
///
|
||||
/// ```rust,compile_fail
|
||||
/// #[axum_debug::debug_handler]
|
||||
/// async fn handler() -> bool {
|
||||
/// false
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// ```text
|
||||
/// error[E0277]: the trait bound `bool: IntoResponse` is not satisfied
|
||||
/// --> main.rs:xx:23
|
||||
/// |
|
||||
/// xx | async fn handler() -> bool {
|
||||
/// | ^^^^
|
||||
/// | |
|
||||
/// | the trait `IntoResponse` is not implemented for `bool`
|
||||
/// ```
|
||||
///
|
||||
/// Wrong extractor:
|
||||
///
|
||||
/// ```rust,compile_fail
|
||||
/// #[axum_debug::debug_handler]
|
||||
/// async fn handler(a: bool) -> String {
|
||||
/// format!("Can I extract a bool? {}", a)
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// ```text
|
||||
/// error[E0277]: the trait bound `bool: FromRequest` is not satisfied
|
||||
/// --> main.rs:xx:21
|
||||
/// |
|
||||
/// xx | async fn handler(a: bool) -> String {
|
||||
/// | ^^^^
|
||||
/// | |
|
||||
/// | the trait `FromRequest` is not implemented for `bool`
|
||||
/// ```
|
||||
///
|
||||
/// Too many extractors:
|
||||
///
|
||||
/// ```rust,compile_fail
|
||||
/// #[axum_debug::debug_handler]
|
||||
/// async fn handler(
|
||||
/// a: String,
|
||||
/// b: String,
|
||||
/// c: String,
|
||||
/// d: String,
|
||||
/// e: String,
|
||||
/// f: String,
|
||||
/// g: String,
|
||||
/// h: String,
|
||||
/// i: String,
|
||||
/// j: String,
|
||||
/// k: String,
|
||||
/// l: String,
|
||||
/// m: String,
|
||||
/// n: String,
|
||||
/// o: String,
|
||||
/// p: String,
|
||||
/// q: String,
|
||||
/// ) {}
|
||||
/// ```
|
||||
///
|
||||
/// ```text
|
||||
/// error: too many extractors. 16 extractors are allowed
|
||||
/// note: you can nest extractors like "a: (Extractor, Extractor), b: (Extractor, Extractor)"
|
||||
/// --> main.rs:xx:5
|
||||
/// |
|
||||
/// xx | / a: String,
|
||||
/// xx | | b: String,
|
||||
/// xx | | c: String,
|
||||
/// xx | | d: String,
|
||||
/// ... |
|
||||
/// xx | | p: String,
|
||||
/// xx | | q: String,
|
||||
/// | |______________^
|
||||
/// ```
|
||||
///
|
||||
/// Future is not [`Send`]:
|
||||
///
|
||||
/// ```rust,compile_fail
|
||||
/// #[axum_debug::debug_handler]
|
||||
/// async fn handler() {
|
||||
/// let not_send = std::rc::Rc::new(());
|
||||
///
|
||||
/// async {}.await;
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// ```text
|
||||
/// error: future cannot be sent between threads safely
|
||||
/// --> main.rs:xx:10
|
||||
/// |
|
||||
/// xx | async fn handler() {
|
||||
/// | ^^^^^^^
|
||||
/// | |
|
||||
/// | future returned by `handler` is not `Send`
|
||||
/// ```
|
||||
#[proc_macro_attribute]
|
||||
pub fn debug_handler(_attr: TokenStream, input: TokenStream) -> TokenStream {
|
||||
#[cfg(not(debug_assertions))]
|
||||
return input;
|
||||
|
||||
#[cfg(debug_assertions)]
|
||||
return debug::apply_debug_handler(input);
|
||||
return debug_handler::expand(_attr, input);
|
||||
}
|
||||
|
||||
#[cfg(debug_assertions)]
|
||||
mod debug {
|
||||
mod debug_handler {
|
||||
use proc_macro2::TokenStream;
|
||||
use quote::{format_ident, quote_spanned};
|
||||
use syn::{parse_macro_input, spanned::Spanned, FnArg, Ident, ItemFn, ReturnType, Signature};
|
||||
use quote::{format_ident, quote, quote_spanned};
|
||||
use syn::{parse::Parse, spanned::Spanned, FnArg, ItemFn};
|
||||
|
||||
pub(crate) fn apply_debug_handler(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
|
||||
let function = parse_macro_input!(input as ItemFn);
|
||||
|
||||
let vis = &function.vis;
|
||||
let sig = &function.sig;
|
||||
let ident = &sig.ident;
|
||||
let span = ident.span();
|
||||
let len = sig.inputs.len();
|
||||
let generics = create_generics(len);
|
||||
let params = sig.inputs.iter().map(|fn_arg| {
|
||||
if let FnArg::Typed(pat_type) = fn_arg {
|
||||
&pat_type.pat
|
||||
} else {
|
||||
panic!("not a handler function");
|
||||
}
|
||||
});
|
||||
let block = &function.block;
|
||||
|
||||
if let Err(err) = async_check(sig) {
|
||||
return err.into_compile_error().into();
|
||||
pub(crate) fn expand(
|
||||
attr: proc_macro::TokenStream,
|
||||
input: proc_macro::TokenStream,
|
||||
) -> proc_macro::TokenStream {
|
||||
match try_expand(attr.into(), input.into()) {
|
||||
Ok(tokens) => tokens.into(),
|
||||
Err(err) => err.into_compile_error().into(),
|
||||
}
|
||||
}
|
||||
|
||||
if let Err(err) = param_limit_check(sig) {
|
||||
return err.into_compile_error().into();
|
||||
}
|
||||
pub(crate) fn try_expand(attr: TokenStream, input: TokenStream) -> syn::Result<TokenStream> {
|
||||
syn::parse2::<Attrs>(attr)?;
|
||||
let item_fn = syn::parse2::<ItemFn>(input.clone())?;
|
||||
|
||||
let check_trait = check_trait_code(sig, &generics);
|
||||
let check_return = check_return_code(sig, &generics);
|
||||
let check_params = match check_params_code(sig, &generics) {
|
||||
Ok(tokens) => tokens,
|
||||
Err(err) => return err.into_compile_error().into(),
|
||||
check_extractor_count(&item_fn)?;
|
||||
|
||||
let check_inputs_impls_from_request = check_inputs_impls_from_request(&item_fn);
|
||||
let check_output_impls_into_response = check_output_impls_into_response(&item_fn);
|
||||
let check_future_send = check_future_send(&item_fn);
|
||||
|
||||
let tokens = quote! {
|
||||
#input
|
||||
#check_inputs_impls_from_request
|
||||
#check_output_impls_into_response
|
||||
#check_future_send
|
||||
};
|
||||
|
||||
let expanded = quote_spanned! {span=>
|
||||
#vis #sig {
|
||||
#check_trait
|
||||
#check_return
|
||||
#(#check_params)*
|
||||
|
||||
#sig #block
|
||||
|
||||
#ident(#(#params),*).await
|
||||
}
|
||||
};
|
||||
|
||||
expanded.into()
|
||||
Ok(tokens)
|
||||
}
|
||||
|
||||
fn create_generics(len: usize) -> Vec<Ident> {
|
||||
(1..=len).map(|i| format_ident!("T{}", i)).collect()
|
||||
}
|
||||
struct Attrs;
|
||||
|
||||
fn async_check(sig: &Signature) -> Result<(), syn::Error> {
|
||||
if sig.asyncness.is_none() {
|
||||
Err(syn::Error::new_spanned(
|
||||
sig.fn_token,
|
||||
"handlers must be async functions",
|
||||
))
|
||||
} else {
|
||||
Ok(())
|
||||
impl Parse for Attrs {
|
||||
fn parse(_input: syn::parse::ParseStream) -> syn::Result<Self> {
|
||||
Ok(Self)
|
||||
}
|
||||
}
|
||||
|
||||
fn param_limit_check(sig: &Signature) -> Result<(), syn::Error> {
|
||||
fn check_extractor_count(item_fn: &ItemFn) -> syn::Result<()> {
|
||||
let max_extractors = 16;
|
||||
|
||||
if sig.inputs.len() > max_extractors {
|
||||
let msg = format!(
|
||||
"too many extractors. {} extractors are allowed\n\
|
||||
note: you can nest extractors like \"a: (Extractor, Extractor), b: (Extractor, Extractor)\"",
|
||||
max_extractors
|
||||
);
|
||||
|
||||
Err(syn::Error::new_spanned(&sig.inputs, msg))
|
||||
} else {
|
||||
if item_fn.sig.inputs.len() <= max_extractors {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(syn::Error::new_spanned(
|
||||
&item_fn.sig.inputs,
|
||||
format!(
|
||||
"Handlers cannot take more than {} arguments. Use `(a, b): (ExtractorA, ExtractorA)` to further nest extractors",
|
||||
max_extractors,
|
||||
)
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
fn check_trait_code(sig: &Signature, generics: &[Ident]) -> proc_macro2::TokenStream {
|
||||
let ident = &sig.ident;
|
||||
let span = ident.span();
|
||||
|
||||
quote_spanned! {span=>
|
||||
{
|
||||
debug_handler(#ident);
|
||||
|
||||
fn debug_handler<F, Fut, #(#generics),*>(_f: F)
|
||||
where
|
||||
F: ::std::ops::FnOnce(#(#generics),*) -> Fut + Clone + Send + 'static,
|
||||
Fut: ::std::future::Future + Send,
|
||||
{}
|
||||
}
|
||||
fn check_inputs_impls_from_request(item_fn: &ItemFn) -> TokenStream {
|
||||
if !item_fn.sig.generics.params.is_empty() {
|
||||
return syn::Error::new_spanned(
|
||||
&item_fn.sig.generics,
|
||||
"`#[axum_debug::debug_handler]` doesn't support generic functions",
|
||||
)
|
||||
.into_compile_error();
|
||||
}
|
||||
}
|
||||
|
||||
fn check_return_code(sig: &Signature, generics: &[Ident]) -> proc_macro2::TokenStream {
|
||||
let span = match &sig.output {
|
||||
ReturnType::Default => sig.output.span(),
|
||||
ReturnType::Type(_, ty) => ty.span(),
|
||||
};
|
||||
let ident = &sig.ident;
|
||||
|
||||
quote_spanned! {span=>
|
||||
{
|
||||
debug_handler(#ident);
|
||||
|
||||
fn debug_handler<F, Fut, Res, #(#generics),*>(_f: F)
|
||||
where
|
||||
F: ::std::ops::FnOnce(#(#generics),*) -> Fut,
|
||||
Fut: ::std::future::Future<Output = Res>,
|
||||
Res: ::axum::response::IntoResponse,
|
||||
{}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn check_params_code(
|
||||
sig: &Signature,
|
||||
generics: &[Ident],
|
||||
) -> Result<Vec<TokenStream>, syn::Error> {
|
||||
let ident = &sig.ident;
|
||||
generics
|
||||
item_fn
|
||||
.sig
|
||||
.inputs
|
||||
.iter()
|
||||
.enumerate()
|
||||
.map(|(i, generic)| {
|
||||
let span = match &sig.inputs[i] {
|
||||
FnArg::Typed(pat_type) => pat_type.ty.span(),
|
||||
.map(|arg| {
|
||||
let (span, ty) = match arg {
|
||||
FnArg::Receiver(receiver) => {
|
||||
// TODO: look into whether its possible to make this work
|
||||
return Err(syn::Error::new_spanned(
|
||||
receiver,
|
||||
"`#[debug_handler]` is not supported on methods",
|
||||
));
|
||||
if receiver.reference.is_some() {
|
||||
return syn::Error::new_spanned(
|
||||
receiver,
|
||||
"Handlers must only take owned values",
|
||||
)
|
||||
.into_compile_error();
|
||||
}
|
||||
|
||||
let span = receiver.span();
|
||||
(span, syn::parse_quote!(Self))
|
||||
}
|
||||
FnArg::Typed(typed) => {
|
||||
let ty = &typed.ty;
|
||||
let span = ty.span();
|
||||
(span, ty.clone())
|
||||
}
|
||||
};
|
||||
|
||||
let tokens = quote_spanned! {span=>
|
||||
{
|
||||
debug_handler(#ident);
|
||||
|
||||
fn debug_handler<F, Fut, #(#generics),*>(_f: F)
|
||||
where
|
||||
F: ::std::ops::FnOnce(#(#generics),*) -> Fut,
|
||||
Fut: ::std::future::Future,
|
||||
#generic: ::axum::extract::FromRequest + Send,
|
||||
{}
|
||||
}
|
||||
};
|
||||
|
||||
Ok(tokens)
|
||||
let name = format_ident!("__axum_debug_check_{}_from_request", item_fn.sig.ident);
|
||||
quote_spanned! {span=>
|
||||
#[allow(warnings)]
|
||||
fn #name()
|
||||
where
|
||||
#ty: ::axum::extract::FromRequest + Send,
|
||||
{}
|
||||
}
|
||||
})
|
||||
.collect()
|
||||
.collect::<TokenStream>()
|
||||
}
|
||||
|
||||
fn check_output_impls_into_response(item_fn: &ItemFn) -> TokenStream {
|
||||
let ty = match &item_fn.sig.output {
|
||||
syn::ReturnType::Default => return quote! {},
|
||||
syn::ReturnType::Type(_, ty) => ty,
|
||||
};
|
||||
let span = ty.span();
|
||||
|
||||
let make_value_name = format_ident!(
|
||||
"__axum_debug_check_{}_into_response_make_value",
|
||||
item_fn.sig.ident
|
||||
);
|
||||
|
||||
let make = if item_fn.sig.asyncness.is_some() {
|
||||
quote_spanned! {span=>
|
||||
#[allow(warnings)]
|
||||
async fn #make_value_name() -> #ty { panic!() }
|
||||
}
|
||||
} else if let syn::Type::ImplTrait(_) = &**ty {
|
||||
// lets just assume it returns `impl Future`
|
||||
quote_spanned! {span=>
|
||||
#[allow(warnings)]
|
||||
fn #make_value_name() -> #ty { async { panic!() } }
|
||||
}
|
||||
} else {
|
||||
quote_spanned! {span=>
|
||||
#[allow(warnings)]
|
||||
fn #make_value_name() -> #ty { panic!() }
|
||||
}
|
||||
};
|
||||
|
||||
let name = format_ident!("__axum_debug_check_{}_into_response", item_fn.sig.ident);
|
||||
|
||||
if let Some(receiver) = self_receiver(item_fn) {
|
||||
quote_spanned! {span=>
|
||||
#make
|
||||
|
||||
#[allow(warnings)]
|
||||
async fn #name() {
|
||||
let value = #receiver #make_value_name().await;
|
||||
fn check<T>(_: T)
|
||||
where T: ::axum::response::IntoResponse
|
||||
{}
|
||||
check(value);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
quote_spanned! {span=>
|
||||
#[allow(warnings)]
|
||||
async fn #name() {
|
||||
#make
|
||||
|
||||
let value = #make_value_name().await;
|
||||
fn check<T>(_: T)
|
||||
where T: ::axum::response::IntoResponse
|
||||
{}
|
||||
check(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn check_future_send(item_fn: &ItemFn) -> TokenStream {
|
||||
if item_fn.sig.asyncness.is_none() {
|
||||
match &item_fn.sig.output {
|
||||
syn::ReturnType::Default => {
|
||||
return syn::Error::new_spanned(
|
||||
&item_fn.sig.fn_token,
|
||||
"Handlers must be `async fn`s",
|
||||
)
|
||||
.into_compile_error();
|
||||
}
|
||||
syn::ReturnType::Type(_, ty) => ty,
|
||||
};
|
||||
}
|
||||
|
||||
let span = item_fn.span();
|
||||
|
||||
let handler_name = &item_fn.sig.ident;
|
||||
|
||||
let args = item_fn.sig.inputs.iter().map(|_| {
|
||||
quote_spanned! {span=> panic!() }
|
||||
});
|
||||
|
||||
let name = format_ident!("__axum_debug_check_{}_future", item_fn.sig.ident);
|
||||
|
||||
if let Some(receiver) = self_receiver(item_fn) {
|
||||
quote_spanned! {span=>
|
||||
#[allow(warnings)]
|
||||
fn #name() {
|
||||
let future = #receiver #handler_name(#(#args),*);
|
||||
fn check<T>(_: T)
|
||||
where T: ::std::future::Future + Send
|
||||
{}
|
||||
check(future);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
quote_spanned! {span=>
|
||||
#[allow(warnings)]
|
||||
fn #name() {
|
||||
#item_fn
|
||||
|
||||
let future = #handler_name(#(#args),*);
|
||||
fn check<T>(_: T)
|
||||
where T: ::std::future::Future + Send
|
||||
{}
|
||||
check(future);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn self_receiver(item_fn: &ItemFn) -> Option<TokenStream> {
|
||||
let takes_self = item_fn
|
||||
.sig
|
||||
.inputs
|
||||
.iter()
|
||||
.any(|arg| matches!(arg, syn::FnArg::Receiver(_)));
|
||||
if takes_self {
|
||||
return Some(quote! { Self:: });
|
||||
}
|
||||
|
||||
if let syn::ReturnType::Type(_, ty) = &item_fn.sig.output {
|
||||
if let syn::Type::Path(path) = &**ty {
|
||||
let segments = &path.path.segments;
|
||||
if segments.len() == 1 {
|
||||
if let Some(last) = segments.last() {
|
||||
match &last.arguments {
|
||||
syn::PathArguments::None if last.ident == "Self" => {
|
||||
return Some(quote! { Self:: });
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn ui() {
|
||||
let t = trybuild::TestCases::new();
|
||||
t.pass("tests/pass/*.rs");
|
||||
t.compile_fail("tests/fail/*.rs");
|
||||
#[rustversion::stable]
|
||||
fn go() {
|
||||
let t = trybuild::TestCases::new();
|
||||
t.compile_fail("tests/fail/*.rs");
|
||||
t.pass("tests/pass/*.rs");
|
||||
}
|
||||
|
||||
#[rustversion::not(stable)]
|
||||
fn go() {}
|
||||
|
||||
go();
|
||||
}
|
||||
|
|
|
@ -4,8 +4,4 @@ error[E0277]: the trait bound `bool: FromRequest` is not satisfied
|
|||
4 | async fn handler(foo: bool) {}
|
||||
| ^^^^ the trait `FromRequest` is not implemented for `bool`
|
||||
|
|
||||
note: required by a bound in `handler::{closure#0}::debug_handler`
|
||||
--> tests/fail/argument_not_extractor.rs:4:23
|
||||
|
|
||||
4 | async fn handler(foo: bool) {}
|
||||
| ^^^^ required by this bound in `handler::{closure#0}::debug_handler`
|
||||
= help: see issue #48214
|
||||
|
|
6
axum-debug/tests/fail/attrs.rs
Normal file
6
axum-debug/tests/fail/attrs.rs
Normal file
|
@ -0,0 +1,6 @@
|
|||
use axum_debug::debug_handler;
|
||||
|
||||
#[debug_handler(foo)]
|
||||
async fn handler() {}
|
||||
|
||||
fn main() {}
|
5
axum-debug/tests/fail/attrs.stderr
Normal file
5
axum-debug/tests/fail/attrs.stderr
Normal file
|
@ -0,0 +1,5 @@
|
|||
error: unexpected token
|
||||
--> tests/fail/attrs.rs:3:17
|
||||
|
|
||||
3 | #[debug_handler(foo)]
|
||||
| ^^^
|
23
axum-debug/tests/fail/extract_self_mut.rs
Normal file
23
axum-debug/tests/fail/extract_self_mut.rs
Normal file
|
@ -0,0 +1,23 @@
|
|||
use axum::{
|
||||
async_trait,
|
||||
extract::{FromRequest, RequestParts},
|
||||
};
|
||||
use axum_debug::debug_handler;
|
||||
|
||||
struct A;
|
||||
|
||||
#[async_trait]
|
||||
impl FromRequest for A {
|
||||
type Rejection = ();
|
||||
|
||||
async fn from_request(_req: &mut RequestParts) -> Result<Self, Self::Rejection> {
|
||||
unimplemented!()
|
||||
}
|
||||
}
|
||||
|
||||
impl A {
|
||||
#[debug_handler]
|
||||
async fn handler(&mut self) {}
|
||||
}
|
||||
|
||||
fn main() {}
|
5
axum-debug/tests/fail/extract_self_mut.stderr
Normal file
5
axum-debug/tests/fail/extract_self_mut.stderr
Normal file
|
@ -0,0 +1,5 @@
|
|||
error: Handlers must only take owned values
|
||||
--> tests/fail/extract_self_mut.rs:20:22
|
||||
|
|
||||
20 | async fn handler(&mut self) {}
|
||||
| ^^^^^^^^^
|
23
axum-debug/tests/fail/extract_self_ref.rs
Normal file
23
axum-debug/tests/fail/extract_self_ref.rs
Normal file
|
@ -0,0 +1,23 @@
|
|||
use axum::{
|
||||
async_trait,
|
||||
extract::{FromRequest, RequestParts},
|
||||
};
|
||||
use axum_debug::debug_handler;
|
||||
|
||||
struct A;
|
||||
|
||||
#[async_trait]
|
||||
impl FromRequest for A {
|
||||
type Rejection = ();
|
||||
|
||||
async fn from_request(_req: &mut RequestParts) -> Result<Self, Self::Rejection> {
|
||||
unimplemented!()
|
||||
}
|
||||
}
|
||||
|
||||
impl A {
|
||||
#[debug_handler]
|
||||
async fn handler(&self) {}
|
||||
}
|
||||
|
||||
fn main() {}
|
5
axum-debug/tests/fail/extract_self_ref.stderr
Normal file
5
axum-debug/tests/fail/extract_self_ref.stderr
Normal file
|
@ -0,0 +1,5 @@
|
|||
error: Handlers must only take owned values
|
||||
--> tests/fail/extract_self_ref.rs:20:22
|
||||
|
|
||||
20 | async fn handler(&self) {}
|
||||
| ^^^^^
|
6
axum-debug/tests/fail/generics.rs
Normal file
6
axum-debug/tests/fail/generics.rs
Normal file
|
@ -0,0 +1,6 @@
|
|||
use axum_debug::debug_handler;
|
||||
|
||||
#[debug_handler]
|
||||
async fn handler<T>() {}
|
||||
|
||||
fn main() {}
|
13
axum-debug/tests/fail/generics.stderr
Normal file
13
axum-debug/tests/fail/generics.stderr
Normal file
|
@ -0,0 +1,13 @@
|
|||
error: `#[axum_debug::debug_handler]` doesn't support generic functions
|
||||
--> tests/fail/generics.rs:4:17
|
||||
|
|
||||
4 | async fn handler<T>() {}
|
||||
| ^^^
|
||||
|
||||
error[E0282]: type annotations needed
|
||||
--> tests/fail/generics.rs:4:10
|
||||
|
|
||||
4 | async fn handler<T>() {}
|
||||
| ----- ^^^^^^^ cannot infer type for type parameter `T` declared on the function `handler`
|
||||
| |
|
||||
| consider giving `future` a type
|
|
@ -1,4 +1,4 @@
|
|||
error: handlers must be async functions
|
||||
error: Handlers must be `async fn`s
|
||||
--> tests/fail/not_async.rs:4:1
|
||||
|
|
||||
4 | fn handler() {}
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
error: future cannot be sent between threads safely
|
||||
--> tests/fail/not_send.rs:4:10
|
||||
--> tests/fail/not_send.rs:4:1
|
||||
|
|
||||
4 | async fn handler() {
|
||||
| ^^^^^^^ future returned by `handler` is not `Send`
|
||||
| ^^^^^ future returned by `handler` is not `Send`
|
||||
|
|
||||
= help: within `impl Future`, the trait `Send` is not implemented for `Rc<()>`
|
||||
note: future is not `Send` as this value is used across an await
|
||||
|
@ -14,8 +14,8 @@ note: future is not `Send` as this value is used across an await
|
|||
| ^^^^^^^^^^^^^^ await occurs here, with `rc` maybe used later
|
||||
7 | }
|
||||
| - `rc` is later dropped here
|
||||
note: required by a bound in `handler::{closure#0}::debug_handler`
|
||||
--> tests/fail/not_send.rs:4:10
|
||||
note: required by a bound in `check`
|
||||
--> tests/fail/not_send.rs:4:1
|
||||
|
|
||||
4 | async fn handler() {
|
||||
| ^^^^^^^ required by this bound in `handler::{closure#0}::debug_handler`
|
||||
| ^^^^^ required by this bound in `check`
|
||||
|
|
|
@ -1,5 +0,0 @@
|
|||
error: `#[debug_handler]` is not supported on methods
|
||||
--> tests/fail/self_receiver.rs:17:22
|
||||
|
|
||||
17 | async fn handler(self) {}
|
||||
| ^^^^
|
|
@ -1,5 +1,4 @@
|
|||
error: too many extractors. 16 extractors are allowed
|
||||
note: you can nest extractors like "a: (Extractor, Extractor), b: (Extractor, Extractor)"
|
||||
error: Handlers cannot take more than 16 arguments. Use `(a, b): (ExtractorA, ExtractorA)` to further nest extractors
|
||||
--> tests/fail/too_many_extractors.rs:5:5
|
||||
|
|
||||
5 | / e1: String,
|
||||
|
|
|
@ -4,8 +4,8 @@ error[E0277]: the trait bound `bool: IntoResponse` is not satisfied
|
|||
4 | async fn handler() -> bool {
|
||||
| ^^^^ the trait `IntoResponse` is not implemented for `bool`
|
||||
|
|
||||
note: required by a bound in `handler::{closure#0}::debug_handler`
|
||||
note: required by a bound in `__axum_debug_check_handler_into_response::{closure#0}::check`
|
||||
--> tests/fail/wrong_return_type.rs:4:23
|
||||
|
|
||||
4 | async fn handler() -> bool {
|
||||
| ^^^^ required by this bound in `handler::{closure#0}::debug_handler`
|
||||
| ^^^^ required by this bound in `__axum_debug_check_handler_into_response::{closure#0}::check`
|
||||
|
|
10
axum-debug/tests/pass/associated_fn_without_self.rs
Normal file
10
axum-debug/tests/pass/associated_fn_without_self.rs
Normal file
|
@ -0,0 +1,10 @@
|
|||
use axum_debug::debug_handler;
|
||||
|
||||
struct A;
|
||||
|
||||
impl A {
|
||||
#[debug_handler]
|
||||
async fn handler() {}
|
||||
}
|
||||
|
||||
fn main() {}
|
9
axum-debug/tests/pass/impl_future.rs
Normal file
9
axum-debug/tests/pass/impl_future.rs
Normal file
|
@ -0,0 +1,9 @@
|
|||
use axum_debug::debug_handler;
|
||||
use std::future::Future;
|
||||
|
||||
#[debug_handler]
|
||||
fn handler() -> impl Future<Output = ()> {
|
||||
async {}
|
||||
}
|
||||
|
||||
fn main() {}
|
9
axum-debug/tests/pass/impl_into_response.rs
Normal file
9
axum-debug/tests/pass/impl_into_response.rs
Normal file
|
@ -0,0 +1,9 @@
|
|||
use axum_debug::debug_handler;
|
||||
use axum::response::IntoResponse;
|
||||
|
||||
#[debug_handler]
|
||||
async fn handler() -> impl IntoResponse {
|
||||
"hi!"
|
||||
}
|
||||
|
||||
fn main() {}
|
9
axum-debug/tests/pass/mut_extractor.rs
Normal file
9
axum-debug/tests/pass/mut_extractor.rs
Normal file
|
@ -0,0 +1,9 @@
|
|||
use axum_debug::debug_handler;
|
||||
|
||||
#[debug_handler]
|
||||
async fn handler(mut foo: String) -> String {
|
||||
foo += "bar";
|
||||
foo
|
||||
}
|
||||
|
||||
fn main() {}
|
9
axum-debug/tests/pass/ready.rs
Normal file
9
axum-debug/tests/pass/ready.rs
Normal file
|
@ -0,0 +1,9 @@
|
|||
use axum_debug::debug_handler;
|
||||
use std::future::{Ready, ready};
|
||||
|
||||
#[debug_handler]
|
||||
fn handler() -> Ready<()> {
|
||||
ready(())
|
||||
}
|
||||
|
||||
fn main() {}
|
27
axum-debug/tests/pass/returns_self.rs
Normal file
27
axum-debug/tests/pass/returns_self.rs
Normal file
|
@ -0,0 +1,27 @@
|
|||
use axum::{
|
||||
body::{Bytes, Full},
|
||||
http::Response,
|
||||
response::IntoResponse,
|
||||
};
|
||||
use axum_debug::debug_handler;
|
||||
use std::convert::Infallible;
|
||||
|
||||
struct A;
|
||||
|
||||
impl A {
|
||||
#[debug_handler]
|
||||
async fn handler() -> Self {
|
||||
A
|
||||
}
|
||||
}
|
||||
|
||||
impl IntoResponse for A {
|
||||
type Body = Full<Bytes>;
|
||||
type BodyError = Infallible;
|
||||
|
||||
fn into_response(self) -> Response<Self::Body> {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {}
|
|
@ -1,5 +1,8 @@
|
|||
use axum::{
|
||||
async_trait,
|
||||
extract::{FromRequest, RequestParts},
|
||||
};
|
||||
use axum_debug::debug_handler;
|
||||
use axum::{async_trait, extract::{FromRequest, RequestParts}};
|
||||
|
||||
struct A;
|
||||
|
Loading…
Reference in a new issue