diff --git a/axum-debug/CHANGELOG.md b/axum-debug/CHANGELOG.md index c7c43e69..9e9dd1dd 100644 --- a/axum-debug/CHANGELOG.md +++ b/axum-debug/CHANGELOG.md @@ -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. diff --git a/axum-debug/Cargo.toml b/axum-debug/Cargo.toml index 0d30bf20..760db64e 100644 --- a/axum-debug/Cargo.toml +++ b/axum-debug/Cargo.toml @@ -21,3 +21,4 @@ syn = { version = "1.0", features = ["full"] } [dev-dependencies] axum = { path = "../axum", version = "0.3" } trybuild = "1.0" +rustversion = "1.0" diff --git a/axum-debug/src/lib.rs b/axum-debug/src/lib.rs index ab377539..2d2ec686 100644 --- a/axum-debug/src/lib.rs +++ b/axum-debug/src/lib.rs @@ -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 { + syn::parse2::(attr)?; + let item_fn = syn::parse2::(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 { - (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 { + 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: 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: F) - where - F: ::std::ops::FnOnce(#(#generics),*) -> Fut, - Fut: ::std::future::Future, - Res: ::axum::response::IntoResponse, - {} - } - } - } - - fn check_params_code( - sig: &Signature, - generics: &[Ident], - ) -> Result, 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: 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::() + } + + 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) + 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) + 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) + 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) + where T: ::std::future::Future + Send + {} + check(future); + } + } + } + } + + fn self_receiver(item_fn: &ItemFn) -> Option { + 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(); } diff --git a/axum-debug/tests/fail/argument_not_extractor.stderr b/axum-debug/tests/fail/argument_not_extractor.stderr index 1ef2e6c8..5ee9a59a 100644 --- a/axum-debug/tests/fail/argument_not_extractor.stderr +++ b/axum-debug/tests/fail/argument_not_extractor.stderr @@ -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 diff --git a/axum-debug/tests/fail/attrs.rs b/axum-debug/tests/fail/attrs.rs new file mode 100644 index 00000000..d9fc7f18 --- /dev/null +++ b/axum-debug/tests/fail/attrs.rs @@ -0,0 +1,6 @@ +use axum_debug::debug_handler; + +#[debug_handler(foo)] +async fn handler() {} + +fn main() {} diff --git a/axum-debug/tests/fail/attrs.stderr b/axum-debug/tests/fail/attrs.stderr new file mode 100644 index 00000000..b01bf728 --- /dev/null +++ b/axum-debug/tests/fail/attrs.stderr @@ -0,0 +1,5 @@ +error: unexpected token + --> tests/fail/attrs.rs:3:17 + | +3 | #[debug_handler(foo)] + | ^^^ diff --git a/axum-debug/tests/fail/extract_self_mut.rs b/axum-debug/tests/fail/extract_self_mut.rs new file mode 100644 index 00000000..cc291267 --- /dev/null +++ b/axum-debug/tests/fail/extract_self_mut.rs @@ -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 { + unimplemented!() + } +} + +impl A { + #[debug_handler] + async fn handler(&mut self) {} +} + +fn main() {} diff --git a/axum-debug/tests/fail/extract_self_mut.stderr b/axum-debug/tests/fail/extract_self_mut.stderr new file mode 100644 index 00000000..a9b604bc --- /dev/null +++ b/axum-debug/tests/fail/extract_self_mut.stderr @@ -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) {} + | ^^^^^^^^^ diff --git a/axum-debug/tests/fail/extract_self_ref.rs b/axum-debug/tests/fail/extract_self_ref.rs new file mode 100644 index 00000000..a9d025f7 --- /dev/null +++ b/axum-debug/tests/fail/extract_self_ref.rs @@ -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 { + unimplemented!() + } +} + +impl A { + #[debug_handler] + async fn handler(&self) {} +} + +fn main() {} diff --git a/axum-debug/tests/fail/extract_self_ref.stderr b/axum-debug/tests/fail/extract_self_ref.stderr new file mode 100644 index 00000000..7f628bc6 --- /dev/null +++ b/axum-debug/tests/fail/extract_self_ref.stderr @@ -0,0 +1,5 @@ +error: Handlers must only take owned values + --> tests/fail/extract_self_ref.rs:20:22 + | +20 | async fn handler(&self) {} + | ^^^^^ diff --git a/axum-debug/tests/fail/generics.rs b/axum-debug/tests/fail/generics.rs new file mode 100644 index 00000000..276fc0df --- /dev/null +++ b/axum-debug/tests/fail/generics.rs @@ -0,0 +1,6 @@ +use axum_debug::debug_handler; + +#[debug_handler] +async fn handler() {} + +fn main() {} diff --git a/axum-debug/tests/fail/generics.stderr b/axum-debug/tests/fail/generics.stderr new file mode 100644 index 00000000..7249e89d --- /dev/null +++ b/axum-debug/tests/fail/generics.stderr @@ -0,0 +1,13 @@ +error: `#[axum_debug::debug_handler]` doesn't support generic functions + --> tests/fail/generics.rs:4:17 + | +4 | async fn handler() {} + | ^^^ + +error[E0282]: type annotations needed + --> tests/fail/generics.rs:4:10 + | +4 | async fn handler() {} + | ----- ^^^^^^^ cannot infer type for type parameter `T` declared on the function `handler` + | | + | consider giving `future` a type diff --git a/axum-debug/tests/fail/not_async.stderr b/axum-debug/tests/fail/not_async.stderr index e36eb148..e196a06b 100644 --- a/axum-debug/tests/fail/not_async.stderr +++ b/axum-debug/tests/fail/not_async.stderr @@ -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() {} diff --git a/axum-debug/tests/fail/not_send.stderr b/axum-debug/tests/fail/not_send.stderr index 00403d79..d5dfaff2 100644 --- a/axum-debug/tests/fail/not_send.stderr +++ b/axum-debug/tests/fail/not_send.stderr @@ -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` diff --git a/axum-debug/tests/fail/self_receiver.stderr b/axum-debug/tests/fail/self_receiver.stderr deleted file mode 100644 index d9456dd1..00000000 --- a/axum-debug/tests/fail/self_receiver.stderr +++ /dev/null @@ -1,5 +0,0 @@ -error: `#[debug_handler]` is not supported on methods - --> tests/fail/self_receiver.rs:17:22 - | -17 | async fn handler(self) {} - | ^^^^ diff --git a/axum-debug/tests/fail/too_many_extractors.stderr b/axum-debug/tests/fail/too_many_extractors.stderr index aa25bcbb..f4458c48 100644 --- a/axum-debug/tests/fail/too_many_extractors.stderr +++ b/axum-debug/tests/fail/too_many_extractors.stderr @@ -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, diff --git a/axum-debug/tests/fail/wrong_return_type.stderr b/axum-debug/tests/fail/wrong_return_type.stderr index aabdbceb..596ba1d7 100644 --- a/axum-debug/tests/fail/wrong_return_type.stderr +++ b/axum-debug/tests/fail/wrong_return_type.stderr @@ -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` diff --git a/axum-debug/tests/pass/associated_fn_without_self.rs b/axum-debug/tests/pass/associated_fn_without_self.rs new file mode 100644 index 00000000..009ac3a8 --- /dev/null +++ b/axum-debug/tests/pass/associated_fn_without_self.rs @@ -0,0 +1,10 @@ +use axum_debug::debug_handler; + +struct A; + +impl A { + #[debug_handler] + async fn handler() {} +} + +fn main() {} diff --git a/axum-debug/tests/pass/impl_future.rs b/axum-debug/tests/pass/impl_future.rs new file mode 100644 index 00000000..9259f62a --- /dev/null +++ b/axum-debug/tests/pass/impl_future.rs @@ -0,0 +1,9 @@ +use axum_debug::debug_handler; +use std::future::Future; + +#[debug_handler] +fn handler() -> impl Future { + async {} +} + +fn main() {} diff --git a/axum-debug/tests/pass/impl_into_response.rs b/axum-debug/tests/pass/impl_into_response.rs new file mode 100644 index 00000000..db17a12b --- /dev/null +++ b/axum-debug/tests/pass/impl_into_response.rs @@ -0,0 +1,9 @@ +use axum_debug::debug_handler; +use axum::response::IntoResponse; + +#[debug_handler] +async fn handler() -> impl IntoResponse { + "hi!" +} + +fn main() {} diff --git a/axum-debug/tests/pass/mut_extractor.rs b/axum-debug/tests/pass/mut_extractor.rs new file mode 100644 index 00000000..872e10c5 --- /dev/null +++ b/axum-debug/tests/pass/mut_extractor.rs @@ -0,0 +1,9 @@ +use axum_debug::debug_handler; + +#[debug_handler] +async fn handler(mut foo: String) -> String { + foo += "bar"; + foo +} + +fn main() {} diff --git a/axum-debug/tests/pass/ready.rs b/axum-debug/tests/pass/ready.rs new file mode 100644 index 00000000..a5bc533a --- /dev/null +++ b/axum-debug/tests/pass/ready.rs @@ -0,0 +1,9 @@ +use axum_debug::debug_handler; +use std::future::{Ready, ready}; + +#[debug_handler] +fn handler() -> Ready<()> { + ready(()) +} + +fn main() {} diff --git a/axum-debug/tests/pass/returns_self.rs b/axum-debug/tests/pass/returns_self.rs new file mode 100644 index 00000000..70e7c2a0 --- /dev/null +++ b/axum-debug/tests/pass/returns_self.rs @@ -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; + type BodyError = Infallible; + + fn into_response(self) -> Response { + todo!() + } +} + +fn main() {} diff --git a/axum-debug/tests/fail/self_receiver.rs b/axum-debug/tests/pass/self_receiver.rs similarity index 80% rename from axum-debug/tests/fail/self_receiver.rs rename to axum-debug/tests/pass/self_receiver.rs index 006ceb3e..3f5b68c0 100644 --- a/axum-debug/tests/fail/self_receiver.rs +++ b/axum-debug/tests/pass/self_receiver.rs @@ -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;