Move axum-debug into axum-macros (#724)

* Move axum-debug into axum-macros

* fix ref to axum-macros in changelog

* Apply suggestions from code review

Co-authored-by: Jonas Platte <jplatte@users.noreply.github.com>

Co-authored-by: Jonas Platte <jplatte@users.noreply.github.com>
This commit is contained in:
David Pedersen 2022-01-26 23:27:22 +01:00 committed by GitHub
parent b1283e9708
commit f6fc5ed80c
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
60 changed files with 492 additions and 217 deletions

View file

@ -8,7 +8,6 @@ If your project isn't listed here and you would like it to be, please feel free
- [axum-typed-websockets](https://crates.io/crates/axum-typed-websockets): `axum::extract::ws` with type safe messages. - [axum-typed-websockets](https://crates.io/crates/axum-typed-websockets): `axum::extract::ws` with type safe messages.
- [tower-cookies](https://crates.io/crates/tower-cookies): Cookie manager middleware - [tower-cookies](https://crates.io/crates/tower-cookies): Cookie manager middleware
- [axum-flash](https://crates.io/crates/axum-flash): One-time notifications (aka flash messages) for axum. - [axum-flash](https://crates.io/crates/axum-flash): One-time notifications (aka flash messages) for axum.
- [axum-debug](https://crates.io/crates/axum-debug): Debugging crate that provides better error messages for axum.
- [axum-msgpack](https://crates.io/crates/axum-msgpack): MessagePack Extractors for axum. - [axum-msgpack](https://crates.io/crates/axum-msgpack): MessagePack Extractors for axum.
## Project showcase ## Project showcase

View file

@ -17,8 +17,3 @@ proc-macro = true
proc-macro2 = "1.0" proc-macro2 = "1.0"
quote = "1.0" quote = "1.0"
syn = { version = "1.0", features = ["full"] } syn = { version = "1.0", features = ["full"] }
[dev-dependencies]
axum = { path = "../axum", version = "0.4" }
trybuild = "1.0"
rustversion = "1.0"

View file

@ -7,6 +7,8 @@
This is a debugging crate that provides better error messages for [`axum`] This is a debugging crate that provides better error messages for [`axum`]
framework. framework.
**Note:** this crate is deprecated. Use [axum-macros] instead.
More information about this crate can be found in the [crate documentation][docs]. More information about this crate can be found in the [crate documentation][docs].
## Safety ## Safety
@ -44,3 +46,4 @@ additional terms or conditions.
[docs]: https://docs.rs/axum-debug [docs]: https://docs.rs/axum-debug
[license]: /axum-debug/LICENSE [license]: /axum-debug/LICENSE
[issue]: https://github.com/tokio-rs/axum/issues/new [issue]: https://github.com/tokio-rs/axum/issues/new
[axum-macros]: https://crates.io/crates/axum-macros

View file

@ -1,148 +1,19 @@
//! This is a debugging crate that provides better error messages for [`axum`] framework. //! This is a debugging crate that provides better error messages for [`axum`] framework.
//! //!
//! While using [`axum`], you can get long error messages for simple mistakes. For example: //! **Note:** this crate is deprecated. Use [axum-macros] instead.
//! //!
//! ```rust,compile_fail //! [axum-macros]: https://crates.io/crates/axum-macros
//! use axum::{routing::get, Router};
//! //!
//! #[tokio::main] //! [`axum`]: https://docs.rs/axum/latest
//! async fn main() {
//! let app = Router::new().route("/", get(handler));
//!
//! axum::Server::bind(&"0.0.0.0:3000".parse().unwrap())
//! .serve(app.into_make_service())
//! .await
//! .unwrap();
//! }
//!
//! fn handler() -> &'static str {
//! "Hello, world"
//! }
//! ```
//!
//! You will get a long error message about function not implementing [`Handler`] trait. But why
//! does this function not implement it? To figure it out, the [`debug_handler`] macro can be used.
//!
//! ```rust,compile_fail
//! # use axum::{routing::get, Router};
//! # use axum_debug::debug_handler;
//! #
//! # #[tokio::main]
//! # async fn main() {
//! # let app = Router::new().route("/", get(handler));
//! #
//! # axum::Server::bind(&"0.0.0.0:3000".parse().unwrap())
//! # .serve(app.into_make_service())
//! # .await
//! # .unwrap();
//! # }
//! #
//! #[debug_handler]
//! fn handler() -> &'static str {
//! "Hello, world"
//! }
//! ```
//!
//! ```text
//! error: handlers must be async functions
//! --> main.rs:xx:1
//! |
//! xx | fn handler() -> &'static str {
//! | ^^
//! ```
//!
//! As the error message says, handler function needs to be async.
//!
//! ```rust,compile_fail
//! use axum::{routing::get, Router};
//! use axum_debug::debug_handler;
//!
//! #[tokio::main]
//! async fn main() {
//! let app = Router::new().route("/", get(handler));
//!
//! axum::Server::bind(&"0.0.0.0:3000".parse().unwrap())
//! .serve(app.into_make_service())
//! .await
//! .unwrap();
//! }
//!
//! #[debug_handler]
//! async fn handler() -> &'static str {
//! "Hello, world"
//! }
//! ```
//!
//! # 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`)
//!
//! [`axum`]: https://docs.rs/axum/0.3
//! [`Handler`]: https://docs.rs/axum/0.3/axum/handler/trait.Handler.html
//! [`debug_handler`]: macro@debug_handler
#![warn(
clippy::all,
clippy::dbg_macro,
clippy::todo,
clippy::empty_enum,
clippy::enum_glob_use,
clippy::mem_forget,
clippy::unused_self,
clippy::filter_map_next,
clippy::needless_continue,
clippy::needless_borrow,
clippy::match_wildcard_for_single_variants,
clippy::if_let_mutex,
clippy::mismatched_target_os,
clippy::await_holding_lock,
clippy::match_on_vec_items,
clippy::imprecise_flops,
clippy::suboptimal_flops,
clippy::lossy_float_literal,
clippy::rest_pat_in_fully_bound_structs,
clippy::fn_params_excessive_bools,
clippy::exit,
clippy::inefficient_to_string,
clippy::linkedlist,
clippy::macro_use_imports,
clippy::option_option,
clippy::verbose_file_reads,
clippy::unnested_or_patterns,
clippy::str_to_string,
rust_2018_idioms,
future_incompatible,
nonstandard_style,
missing_debug_implementations,
missing_docs
)]
#![deny(unreachable_pub, private_in_public)]
#![allow(elided_lifetimes_in_paths, clippy::type_complexity)]
#![forbid(unsafe_code)]
#![cfg_attr(docsrs, feature(doc_cfg))]
#![cfg_attr(test, allow(clippy::float_cmp))]
use proc_macro::TokenStream; use proc_macro::TokenStream;
/// Generates better error messages when applied to a handler function. /// Generates better error messages when applied to a handler function.
/// ///
/// See the [module docs](self) for more details. /// Note this crate is deprecated. Use [axum-macros] instead.
///
/// [axum-macros]: https://crates.io/crates/axum-macros
#[deprecated(since = "0.3.3", note = "Use the axum-macros crate instead")]
#[proc_macro_attribute] #[proc_macro_attribute]
pub fn debug_handler(_attr: TokenStream, input: TokenStream) -> TokenStream { pub fn debug_handler(_attr: TokenStream, input: TokenStream) -> TokenStream {
#[cfg(not(debug_assertions))] #[cfg(not(debug_assertions))]
@ -442,18 +313,3 @@ mod debug_handler {
None None
} }
} }
#[test]
fn ui() {
#[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();
}

View file

@ -1,5 +0,0 @@
error: expected `fn`
--> tests/fail/not_a_function.rs:4:1
|
4 | struct A;
| ^^^^^^

View file

@ -23,4 +23,5 @@ syn = { version = "1.0", features = ["full"] }
axum = { path = "../axum", version = "0.4", features = ["headers"] } axum = { path = "../axum", version = "0.4", features = ["headers"] }
rustversion = "1.0" rustversion = "1.0"
serde = { version = "1.0", features = ["derive"] } serde = { version = "1.0", features = ["derive"] }
tokio = { version = "1.0", features = ["full"] }
trybuild = "1.0" trybuild = "1.0"

View file

@ -1,4 +1,4 @@
Copyright 2021 Axum Debug Contributors Copyright 2021 Axum Contributors
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

View file

@ -0,0 +1,288 @@
use proc_macro2::TokenStream;
use quote::{format_ident, quote, quote_spanned};
use syn::{parse::Parse, spanned::Spanned, FnArg, ItemFn, Token, Type};
pub(crate) fn expand(attr: Attrs, item_fn: ItemFn) -> syn::Result<TokenStream> {
check_extractor_count(&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);
let tokens = quote! {
#item_fn
#check_inputs_impls_from_request
#check_output_impls_into_response
#check_future_send
};
Ok(tokens)
}
pub(crate) struct Attrs {
body_ty: Type,
}
impl Parse for Attrs {
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 })
}
}
fn check_extractor_count(item_fn: &ItemFn) -> syn::Result<()> {
let max_extractors = 16;
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_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,
"`#[axum_macros::debug_handler]` doesn't support generic functions",
)
.into_compile_error();
}
item_fn
.sig
.inputs
.iter()
.enumerate()
.map(|(idx, arg)| {
let (span, ty) = match arg {
FnArg::Receiver(receiver) => {
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 name = format_ident!(
"__axum_macros_check_{}_{}_from_request",
item_fn.sig.ident,
idx
);
quote_spanned! {span=>
#[allow(warnings)]
fn #name()
where
#ty: ::axum::extract::FromRequest<#body_ty> + Send,
{}
}
})
.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 declare_inputs = item_fn
.sig
.inputs
.iter()
.filter_map(|arg| match arg {
FnArg::Receiver(_) => None,
FnArg::Typed(pat_ty) => {
let pat = &pat_ty.pat;
let ty = &pat_ty.ty;
Some(quote! {
let #pat: #ty = panic!();
})
}
})
.collect::<TokenStream>();
let block = &item_fn.block;
let make_value_name = format_ident!(
"__axum_macros_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 {
#declare_inputs
#block
}
}
} else {
quote_spanned! {span=>
#[allow(warnings)]
fn #make_value_name() -> #ty {
#declare_inputs
#block
}
}
};
let name = format_ident!("__axum_macros_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_macros_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() {
#[rustversion::stable]
fn go() {
let t = trybuild::TestCases::new();
t.compile_fail("tests/debug_handler/fail/*.rs");
t.pass("tests/debug_handler/pass/*.rs");
}
#[rustversion::not(stable)]
fn go() {}
go();
}

View file

@ -508,8 +508,8 @@ fn ui() {
#[rustversion::stable] #[rustversion::stable]
fn go() { fn go() {
let t = trybuild::TestCases::new(); let t = trybuild::TestCases::new();
t.compile_fail("tests/fail/*.rs"); t.compile_fail("tests/from_request/fail/*.rs");
t.pass("tests/pass/*.rs"); t.pass("tests/from_request/pass/*.rs");
} }
#[rustversion::not(stable)] #[rustversion::not(stable)]

View file

@ -43,9 +43,11 @@
#![cfg_attr(docsrs, feature(doc_cfg))] #![cfg_attr(docsrs, feature(doc_cfg))]
#![cfg_attr(test, allow(clippy::float_cmp))] #![cfg_attr(test, allow(clippy::float_cmp))]
use proc_macro::TokenStream;
use quote::{quote, ToTokens}; use quote::{quote, ToTokens};
use syn::parse::Parse; use syn::parse::Parse;
mod debug_handler;
mod from_request; mod from_request;
/// Derive an implementation of [`FromRequest`]. /// Derive an implementation of [`FromRequest`].
@ -235,17 +237,148 @@ mod from_request;
/// [`FromRequest`]: https://docs.rs/axum/latest/axum/extract/trait.FromRequest.html /// [`FromRequest`]: https://docs.rs/axum/latest/axum/extract/trait.FromRequest.html
/// [`axum::extract::rejection::ExtensionRejection`]: https://docs.rs/axum/latest/axum/extract/rejection/enum.ExtensionRejection.html /// [`axum::extract::rejection::ExtensionRejection`]: https://docs.rs/axum/latest/axum/extract/rejection/enum.ExtensionRejection.html
#[proc_macro_derive(FromRequest, attributes(from_request))] #[proc_macro_derive(FromRequest, attributes(from_request))]
pub fn derive_from_request(item: proc_macro::TokenStream) -> proc_macro::TokenStream { pub fn derive_from_request(item: TokenStream) -> TokenStream {
expand_with(item, from_request::expand) expand_with(item, from_request::expand)
} }
fn expand_with<F, T, K>(input: proc_macro::TokenStream, f: F) -> proc_macro::TokenStream /// Generates better error messages when applied handler functions.
///
/// While using [`axum`], you can get long error messages for simple mistakes. For example:
///
/// ```compile_fail
/// use axum::{routing::get, Router};
///
/// #[tokio::main]
/// async fn main() {
/// let app = Router::new().route("/", get(handler));
///
/// axum::Server::bind(&"0.0.0.0:3000".parse().unwrap())
/// .serve(app.into_make_service())
/// .await
/// .unwrap();
/// }
///
/// fn handler() -> &'static str {
/// "Hello, world"
/// }
/// ```
///
/// You will get a long error message about function not implementing [`Handler`] trait. But why
/// does this function not implement it? To figure it out, the [`debug_handler`] macro can be used.
///
/// ```compile_fail
/// # use axum::{routing::get, Router};
/// # use axum_macros::debug_handler;
/// #
/// # #[tokio::main]
/// # async fn main() {
/// # let app = Router::new().route("/", get(handler));
/// #
/// # axum::Server::bind(&"0.0.0.0:3000".parse().unwrap())
/// # .serve(app.into_make_service())
/// # .await
/// # .unwrap();
/// # }
/// #
/// #[debug_handler]
/// fn handler() -> &'static str {
/// "Hello, world"
/// }
/// ```
///
/// ```text
/// error: handlers must be async functions
/// --> main.rs:xx:1
/// |
/// xx | fn handler() -> &'static str {
/// | ^^
/// ```
///
/// As the error message says, handler function needs to be async.
///
/// ```
/// use axum::{routing::get, Router};
/// use axum_macros::debug_handler;
///
/// #[tokio::main]
/// async fn main() {
/// # async {
/// let app = Router::new().route("/", get(handler));
///
/// axum::Server::bind(&"0.0.0.0:3000".parse().unwrap())
/// .serve(app.into_make_service())
/// .await
/// .unwrap();
/// # };
/// }
///
/// #[debug_handler]
/// async fn handler() -> &'static str {
/// "Hello, world"
/// }
/// ```
///
/// # 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:
///
/// ```
/// use axum::{body::BoxBody, http::Request};
/// # use axum_macros::debug_handler;
///
/// #[debug_handler(body = BoxBody)]
/// async fn handler(request: Request<BoxBody>) {}
/// ```
///
/// # Performance
///
/// This macro has no effect when compiled with the release profile. (eg. `cargo build --release`)
///
/// [`axum`]: https://docs.rs/axum/latest
/// [`Handler`]: https://docs.rs/axum/latest/axum/handler/trait.Handler.html
/// [`debug_handler`]: macro@debug_handler
#[proc_macro_attribute]
pub fn debug_handler(_attr: TokenStream, input: TokenStream) -> TokenStream {
#[cfg(not(debug_assertions))]
return input;
#[cfg(debug_assertions)]
return expand_attr_with(_attr, input, debug_handler::expand);
}
fn expand_with<F, I, K>(input: TokenStream, f: F) -> TokenStream
where where
F: FnOnce(T) -> syn::Result<K>, F: FnOnce(I) -> syn::Result<K>,
T: Parse, I: Parse,
K: ToTokens, K: ToTokens,
{ {
match syn::parse(input).and_then(f) { expand(syn::parse(input).and_then(f))
}
fn expand_attr_with<F, A, I, K>(attr: TokenStream, input: TokenStream, f: F) -> TokenStream
where
F: FnOnce(A, I) -> syn::Result<K>,
A: Parse,
I: Parse,
K: ToTokens,
{
let expand_result = (|| {
let attr = syn::parse(attr)?;
let input = syn::parse(input)?;
f(attr, input)
})();
expand(expand_result)
}
fn expand<T>(result: syn::Result<T>) -> TokenStream
where
T: ToTokens,
{
match result {
Ok(tokens) => { Ok(tokens) => {
let tokens = (quote! { #tokens }).into(); let tokens = (quote! { #tokens }).into();
if std::env::var_os("AXUM_MACROS_DEBUG").is_some() { if std::env::var_os("AXUM_MACROS_DEBUG").is_some() {

View file

@ -1,4 +1,4 @@
use axum_debug::debug_handler; use axum_macros::debug_handler;
#[debug_handler] #[debug_handler]
async fn handler(foo: bool) {} async fn handler(foo: bool) {}

View file

@ -1,5 +1,5 @@
error[E0277]: the trait bound `bool: FromRequest<Body>` is not satisfied error[E0277]: the trait bound `bool: FromRequest<Body>` is not satisfied
--> tests/fail/argument_not_extractor.rs:4:23 --> tests/debug_handler/fail/argument_not_extractor.rs:4:23
| |
4 | async fn handler(foo: bool) {} 4 | async fn handler(foo: bool) {}
| ^^^^ the trait `FromRequest<Body>` is not implemented for `bool` | ^^^^ the trait `FromRequest<Body>` is not implemented for `bool`

View file

@ -2,7 +2,7 @@ use axum::{
async_trait, async_trait,
extract::{FromRequest, RequestParts}, extract::{FromRequest, RequestParts},
}; };
use axum_debug::debug_handler; use axum_macros::debug_handler;
struct A; struct A;

View file

@ -1,5 +1,5 @@
error: Handlers must only take owned values error: Handlers must only take owned values
--> tests/fail/extract_self_mut.rs:23:22 --> tests/debug_handler/fail/extract_self_mut.rs:23:22
| |
23 | async fn handler(&mut self) {} 23 | async fn handler(&mut self) {}
| ^^^^^^^^^ | ^^^^^^^^^

View file

@ -2,7 +2,7 @@ use axum::{
async_trait, async_trait,
extract::{FromRequest, RequestParts}, extract::{FromRequest, RequestParts},
}; };
use axum_debug::debug_handler; use axum_macros::debug_handler;
struct A; struct A;

View file

@ -1,5 +1,5 @@
error: Handlers must only take owned values error: Handlers must only take owned values
--> tests/fail/extract_self_ref.rs:23:22 --> tests/debug_handler/fail/extract_self_ref.rs:23:22
| |
23 | async fn handler(&self) {} 23 | async fn handler(&self) {}
| ^^^^^ | ^^^^^

View file

@ -1,4 +1,4 @@
use axum_debug::debug_handler; use axum_macros::debug_handler;
#[debug_handler] #[debug_handler]
async fn handler<T>() {} async fn handler<T>() {}

View file

@ -1,11 +1,11 @@
error: `#[axum_debug::debug_handler]` doesn't support generic functions error: `#[axum_macros::debug_handler]` doesn't support generic functions
--> tests/fail/generics.rs:4:17 --> tests/debug_handler/fail/generics.rs:4:17
| |
4 | async fn handler<T>() {} 4 | async fn handler<T>() {}
| ^^^ | ^^^
error[E0282]: type annotations needed error[E0282]: type annotations needed
--> tests/fail/generics.rs:4:10 --> tests/debug_handler/fail/generics.rs:4:10
| |
4 | async fn handler<T>() {} 4 | async fn handler<T>() {}
| ----- ^^^^^^^ cannot infer type for type parameter `T` declared on the function `handler` | ----- ^^^^^^^ cannot infer type for type parameter `T` declared on the function `handler`

View file

@ -1,4 +1,4 @@
use axum_debug::debug_handler; use axum_macros::debug_handler;
#[debug_handler(foo)] #[debug_handler(foo)]
async fn handler() {} async fn handler() {}

View file

@ -1,5 +1,5 @@
error: unknown argument error: unknown argument
--> tests/fail/invalid_attrs.rs:3:17 --> tests/debug_handler/fail/invalid_attrs.rs:3:17
| |
3 | #[debug_handler(foo)] 3 | #[debug_handler(foo)]
| ^^^ | ^^^

View file

@ -1,4 +1,4 @@
use axum_debug::debug_handler; use axum_macros::debug_handler;
#[debug_handler] #[debug_handler]
struct A; struct A;

View file

@ -0,0 +1,5 @@
error: expected `fn`
--> tests/debug_handler/fail/not_a_function.rs:4:1
|
4 | struct A;
| ^^^^^^

View file

@ -1,4 +1,4 @@
use axum_debug::debug_handler; use axum_macros::debug_handler;
#[debug_handler] #[debug_handler]
fn handler() {} fn handler() {}

View file

@ -1,5 +1,5 @@
error: Handlers must be `async fn`s error: Handlers must be `async fn`s
--> tests/fail/not_async.rs:4:1 --> tests/debug_handler/fail/not_async.rs:4:1
| |
4 | fn handler() {} 4 | fn handler() {}
| ^^ | ^^

View file

@ -1,4 +1,4 @@
use axum_debug::debug_handler; use axum_macros::debug_handler;
#[debug_handler] #[debug_handler]
async fn handler() { async fn handler() {

View file

@ -1,12 +1,12 @@
error: future cannot be sent between threads safely error: future cannot be sent between threads safely
--> tests/fail/not_send.rs:4:1 --> tests/debug_handler/fail/not_send.rs:4:1
| |
4 | async fn handler() { 4 | async fn handler() {
| ^^^^^ future returned by `handler` is not `Send` | ^^^^^ future returned by `handler` is not `Send`
| |
= help: within `impl Future<Output = ()>`, the trait `Send` is not implemented for `Rc<()>` = help: within `impl Future<Output = ()>`, the trait `Send` is not implemented for `Rc<()>`
note: future is not `Send` as this value is used across an await note: future is not `Send` as this value is used across an await
--> tests/fail/not_send.rs:6:5 --> tests/debug_handler/fail/not_send.rs:6:5
| |
5 | let rc = std::rc::Rc::new(()); 5 | let rc = std::rc::Rc::new(());
| -- has type `Rc<()>` which is not `Send` | -- has type `Rc<()>` which is not `Send`
@ -15,7 +15,7 @@ note: future is not `Send` as this value is used across an await
7 | } 7 | }
| - `rc` is later dropped here | - `rc` is later dropped here
note: required by a bound in `check` note: required by a bound in `check`
--> tests/fail/not_send.rs:4:1 --> tests/debug_handler/fail/not_send.rs:4:1
| |
4 | async fn handler() { 4 | async fn handler() {
| ^^^^^ required by this bound in `check` | ^^^^^ required by this bound in `check`

View file

@ -1,4 +1,4 @@
use axum_debug::debug_handler; use axum_macros::debug_handler;
#[debug_handler] #[debug_handler]
async fn handler( async fn handler(

View file

@ -1,5 +1,5 @@
error: Handlers cannot take more than 16 arguments. Use `(a, b): (ExtractorA, ExtractorA)` to further nest extractors 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 --> tests/debug_handler/fail/too_many_extractors.rs:5:5
| |
5 | / e1: String, 5 | / e1: String,
6 | | e2: String, 6 | | e2: String,

View file

@ -1,4 +1,4 @@
use axum_debug::debug_handler; use axum_macros::debug_handler;
#[debug_handler] #[debug_handler]
async fn handler() -> bool { async fn handler() -> bool {

View file

@ -1,11 +1,11 @@
error[E0277]: the trait bound `bool: IntoResponse` is not satisfied error[E0277]: the trait bound `bool: IntoResponse` is not satisfied
--> tests/fail/wrong_return_type.rs:4:23 --> tests/debug_handler/fail/wrong_return_type.rs:4:23
| |
4 | async fn handler() -> bool { 4 | async fn handler() -> bool {
| ^^^^ the trait `IntoResponse` is not implemented for `bool` | ^^^^ the trait `IntoResponse` is not implemented for `bool`
| |
note: required by a bound in `__axum_debug_check_handler_into_response::{closure#0}::check` note: required by a bound in `__axum_macros_check_handler_into_response::{closure#0}::check`
--> tests/fail/wrong_return_type.rs:4:23 --> tests/debug_handler/fail/wrong_return_type.rs:4:23
| |
4 | async fn handler() -> bool { 4 | async fn handler() -> bool {
| ^^^^ required by this bound in `__axum_debug_check_handler_into_response::{closure#0}::check` | ^^^^ required by this bound in `__axum_macros_check_handler_into_response::{closure#0}::check`

View file

@ -1,4 +1,4 @@
use axum_debug::debug_handler; use axum_macros::debug_handler;
struct A; struct A;

View file

@ -1,5 +1,5 @@
use axum::{body::BoxBody, http::Request}; use axum::{body::BoxBody, http::Request};
use axum_debug::debug_handler; use axum_macros::debug_handler;
#[debug_handler(body = BoxBody)] #[debug_handler(body = BoxBody)]
async fn handler(_: Request<BoxBody>) {} async fn handler(_: Request<BoxBody>) {}

View file

@ -1,4 +1,4 @@
use axum_debug::debug_handler; use axum_macros::debug_handler;
use std::future::Future; use std::future::Future;
#[debug_handler] #[debug_handler]

View file

@ -1,4 +1,4 @@
use axum_debug::debug_handler; use axum_macros::debug_handler;
use axum::response::IntoResponse; use axum::response::IntoResponse;
#[debug_handler] #[debug_handler]

View file

@ -1,4 +1,4 @@
use axum_debug::debug_handler; use axum_macros::debug_handler;
#[debug_handler] #[debug_handler]
async fn handler(_one: String, _two: String, _three: String) {} async fn handler(_one: String, _two: String, _three: String) {}

View file

@ -1,4 +1,4 @@
use axum_debug::debug_handler; use axum_macros::debug_handler;
#[debug_handler] #[debug_handler]
async fn handler(mut foo: String) -> String { async fn handler(mut foo: String) -> String {

View file

@ -1,4 +1,4 @@
use axum_debug::debug_handler; use axum_macros::debug_handler;
use std::future::{Ready, ready}; use std::future::{Ready, ready};
#[debug_handler] #[debug_handler]

View file

@ -3,7 +3,7 @@ use axum::{
extract::{FromRequest, RequestParts}, extract::{FromRequest, RequestParts},
response::IntoResponse, response::IntoResponse,
}; };
use axum_debug::debug_handler; use axum_macros::debug_handler;
fn main() {} fn main() {}

View file

@ -1,5 +1,5 @@
use axum::response::{IntoResponse, Response}; use axum::response::{IntoResponse, Response};
use axum_debug::debug_handler; use axum_macros::debug_handler;
struct A; struct A;

View file

@ -2,7 +2,7 @@ use axum::{
async_trait, async_trait,
extract::{FromRequest, RequestParts}, extract::{FromRequest, RequestParts},
}; };
use axum_debug::debug_handler; use axum_macros::debug_handler;
struct A; struct A;

View file

@ -1,11 +1,11 @@
error: `via` specified more than once error: `via` specified more than once
--> tests/fail/double_via_attr.rs:5:49 --> tests/from_request/fail/double_via_attr.rs:5:49
| |
5 | struct Extractor(#[from_request(via(Extension), via(Extension))] State); 5 | struct Extractor(#[from_request(via(Extension), via(Extension))] State);
| ^^^ | ^^^
warning: unused import: `axum::extract::Extension` warning: unused import: `axum::extract::Extension`
--> tests/fail/double_via_attr.rs:2:5 --> tests/from_request/fail/double_via_attr.rs:2:5
| |
2 | use axum::extract::Extension; 2 | use axum::extract::Extension;
| ^^^^^^^^^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^^^^^^^^^

View file

@ -1,5 +1,5 @@
error: `#[derive(FromRequest)] doesn't support generics error: `#[derive(FromRequest)] doesn't support generics
--> tests/fail/generic.rs:4:17 --> tests/from_request/fail/generic.rs:4:17
| |
4 | struct Extractor<T>(Option<T>); 4 | struct Extractor<T>(Option<T>);
| ^^^ | ^^^

View file

@ -1,5 +1,5 @@
error: expected `via` error: expected `via`
--> tests/fail/unknown_attr.rs:4:33 --> tests/from_request/fail/unknown_attr.rs:4:33
| |
4 | struct Extractor(#[from_request(foo)] String); 4 | struct Extractor(#[from_request(foo)] String);
| ^^^ | ^^^

View file

@ -1,11 +1,11 @@
error: `#[from_request(via(...))]` on a field cannot be used together with `#[from_request(...)]` on the container error: `#[from_request(via(...))]` on a field cannot be used together with `#[from_request(...)]` on the container
--> tests/fail/via_on_container_and_field.rs:6:33 --> tests/from_request/fail/via_on_container_and_field.rs:6:33
| |
6 | struct Extractor(#[from_request(via(Extension))] State); 6 | struct Extractor(#[from_request(via(Extension))] State);
| ^^^ | ^^^
warning: unused import: `axum::extract::Extension` warning: unused import: `axum::extract::Extension`
--> tests/fail/via_on_container_and_field.rs:2:5 --> tests/from_request/fail/via_on_container_and_field.rs:2:5
| |
2 | use axum::extract::Extension; 2 | use axum::extract::Extension;
| ^^^^^^^^^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^^^^^^^^^

View file

@ -5,4 +5,4 @@ can be converted [into a response](crate::response).
Handlers is where your application logic lives and axum applications are built Handlers is where your application logic lives and axum applications are built
by routing between handlers. by routing between handlers.
[`debug_handler`]: https://docs.rs/axum-debug/latest/axum_debug/attr.debug_handler.html [`debug_handler`]: https://docs.rs/axum-macros/latest/axum_macros/attr.debug_handler.html

View file

@ -66,9 +66,9 @@
//! //!
//! This error doesn't tell you _why_ your function doesn't implement //! This error doesn't tell you _why_ your function doesn't implement
//! [`Handler`]. It's possible to improve the error with the [`debug_handler`] //! [`Handler`]. It's possible to improve the error with the [`debug_handler`]
//! proc-macro from the [axum-debug] crate. //! proc-macro from the [axum-macros] crate.
//! //!
//! [axum-debug]: https://docs.rs/axum-debug //! [axum-macros]: https://docs.rs/axum-macros
use crate::{ use crate::{
body::{boxed, Body, Bytes, HttpBody}, body::{boxed, Body, Bytes, HttpBody},

View file

@ -406,8 +406,8 @@
//! [`HeaderMap`]: http::header::HeaderMap //! [`HeaderMap`]: http::header::HeaderMap
//! [`Request`]: http::Request //! [`Request`]: http::Request
//! [customize-extractor-error]: https://github.com/tokio-rs/axum/blob/main/examples/customize-extractor-error/src/main.rs //! [customize-extractor-error]: https://github.com/tokio-rs/axum/blob/main/examples/customize-extractor-error/src/main.rs
//! [axum-debug]: https://docs.rs/axum-debug //! [axum-macros]: https://docs.rs/axum-macros
//! [`debug_handler`]: https://docs.rs/axum-debug/latest/axum_debug/attr.debug_handler.html //! [`debug_handler`]: https://docs.rs/axum-macros/latest/axum_macros/attr.debug_handler.html
//! [`Handler`]: crate::handler::Handler //! [`Handler`]: crate::handler::Handler
//! [`Infallible`]: std::convert::Infallible //! [`Infallible`]: std::convert::Infallible
//! [load shed]: tower::load_shed //! [load shed]: tower::load_shed