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 <[email protected]>

Co-authored-by: Jonas Platte <[email protected]>
This commit is contained in:
David Pedersen
2022-02-22 13:33:10 +01:00
co-authored by Jonas Platte
parent 28832ad8b1
commit 3d2f4e4a35
57 changed files with 488 additions and 212 deletions
+1
View File
@@ -23,4 +23,5 @@ syn = { version = "1.0", features = ["full"] }
axum = { path = "../axum", version = "0.4", features = ["headers"] }
rustversion = "1.0"
serde = { version = "1.0", features = ["derive"] }
tokio = { version = "1.0", features = ["full"] }
trybuild = "1.0"
+1 -1
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:
+288
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();
}
+2 -2
View File
@@ -508,8 +508,8 @@ fn ui() {
#[rustversion::stable]
fn go() {
let t = trybuild::TestCases::new();
t.compile_fail("tests/fail/*.rs");
t.pass("tests/pass/*.rs");
t.compile_fail("tests/from_request/fail/*.rs");
t.pass("tests/from_request/pass/*.rs");
}
#[rustversion::not(stable)]
+138 -5
View File
@@ -43,9 +43,11 @@
#![cfg_attr(docsrs, feature(doc_cfg))]
#![cfg_attr(test, allow(clippy::float_cmp))]
use proc_macro::TokenStream;
use quote::{quote, ToTokens};
use syn::parse::Parse;
mod debug_handler;
mod from_request;
/// Derive an implementation of [`FromRequest`].
@@ -235,17 +237,148 @@ mod from_request;
/// [`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
#[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)
}
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
F: FnOnce(T) -> syn::Result<K>,
T: Parse,
F: FnOnce(I) -> syn::Result<K>,
I: Parse,
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) => {
let tokens = (quote! { #tokens }).into();
if std::env::var_os("AXUM_MACROS_DEBUG").is_some() {
@@ -0,0 +1,6 @@
use axum_macros::debug_handler;
#[debug_handler]
async fn handler(foo: bool) {}
fn main() {}
@@ -0,0 +1,7 @@
error[E0277]: the trait bound `bool: FromRequest<Body>` is not satisfied
--> tests/debug_handler/fail/argument_not_extractor.rs:4:23
|
4 | async fn handler(foo: bool) {}
| ^^^^ the trait `FromRequest<Body>` is not implemented for `bool`
|
= help: see issue #48214
@@ -0,0 +1,26 @@
use axum::{
async_trait,
extract::{FromRequest, RequestParts},
};
use axum_macros::debug_handler;
struct A;
#[async_trait]
impl<B> FromRequest<B> for A
where
B: Send + 'static,
{
type Rejection = ();
async fn from_request(_req: &mut RequestParts<B>) -> Result<Self, Self::Rejection> {
unimplemented!()
}
}
impl A {
#[debug_handler]
async fn handler(&mut self) {}
}
fn main() {}
@@ -0,0 +1,5 @@
error: Handlers must only take owned values
--> tests/debug_handler/fail/extract_self_mut.rs:23:22
|
23 | async fn handler(&mut self) {}
| ^^^^^^^^^
@@ -0,0 +1,26 @@
use axum::{
async_trait,
extract::{FromRequest, RequestParts},
};
use axum_macros::debug_handler;
struct A;
#[async_trait]
impl<B> FromRequest<B> for A
where
B: Send + 'static,
{
type Rejection = ();
async fn from_request(_req: &mut RequestParts<B>) -> Result<Self, Self::Rejection> {
unimplemented!()
}
}
impl A {
#[debug_handler]
async fn handler(&self) {}
}
fn main() {}
@@ -0,0 +1,5 @@
error: Handlers must only take owned values
--> tests/debug_handler/fail/extract_self_ref.rs:23:22
|
23 | async fn handler(&self) {}
| ^^^^^
@@ -0,0 +1,6 @@
use axum_macros::debug_handler;
#[debug_handler]
async fn handler<T>() {}
fn main() {}
@@ -0,0 +1,13 @@
error: `#[axum_macros::debug_handler]` doesn't support generic functions
--> tests/debug_handler/fail/generics.rs:4:17
|
4 | async fn handler<T>() {}
| ^^^
error[E0282]: type annotations needed
--> tests/debug_handler/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
@@ -0,0 +1,6 @@
use axum_macros::debug_handler;
#[debug_handler(foo)]
async fn handler() {}
fn main() {}
@@ -0,0 +1,5 @@
error: unknown argument
--> tests/debug_handler/fail/invalid_attrs.rs:3:17
|
3 | #[debug_handler(foo)]
| ^^^
@@ -0,0 +1,6 @@
use axum_macros::debug_handler;
#[debug_handler]
struct A;
fn main() {}
@@ -0,0 +1,5 @@
error: expected `fn`
--> tests/debug_handler/fail/not_a_function.rs:4:1
|
4 | struct A;
| ^^^^^^
@@ -0,0 +1,6 @@
use axum_macros::debug_handler;
#[debug_handler]
fn handler() {}
fn main() {}
@@ -0,0 +1,5 @@
error: Handlers must be `async fn`s
--> tests/debug_handler/fail/not_async.rs:4:1
|
4 | fn handler() {}
| ^^
@@ -0,0 +1,9 @@
use axum_macros::debug_handler;
#[debug_handler]
async fn handler() {
let rc = std::rc::Rc::new(());
async {}.await;
}
fn main() {}
@@ -0,0 +1,21 @@
error: future cannot be sent between threads safely
--> tests/debug_handler/fail/not_send.rs:4:1
|
4 | async fn handler() {
| ^^^^^ future returned by `handler` is not `Send`
|
= 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
--> tests/debug_handler/fail/not_send.rs:6:5
|
5 | let rc = std::rc::Rc::new(());
| -- has type `Rc<()>` which is not `Send`
6 | async {}.await;
| ^^^^^^^^^^^^^^ await occurs here, with `rc` maybe used later
7 | }
| - `rc` is later dropped here
note: required by a bound in `check`
--> tests/debug_handler/fail/not_send.rs:4:1
|
4 | async fn handler() {
| ^^^^^ required by this bound in `check`
@@ -0,0 +1,24 @@
use axum_macros::debug_handler;
#[debug_handler]
async fn handler(
e1: String,
e2: String,
e3: String,
e4: String,
e5: String,
e6: String,
e7: String,
e8: String,
e9: String,
e10: String,
e11: String,
e12: String,
e13: String,
e14: String,
e15: String,
e16: String,
e17: String,
) {}
fn main() {}
@@ -0,0 +1,11 @@
error: Handlers cannot take more than 16 arguments. Use `(a, b): (ExtractorA, ExtractorA)` to further nest extractors
--> tests/debug_handler/fail/too_many_extractors.rs:5:5
|
5 | / e1: String,
6 | | e2: String,
7 | | e3: String,
8 | | e4: String,
... |
20 | | e16: String,
21 | | e17: String,
| |________________^
@@ -0,0 +1,8 @@
use axum_macros::debug_handler;
#[debug_handler]
async fn handler() -> bool {
false
}
fn main() {}
@@ -0,0 +1,11 @@
error[E0277]: the trait bound `bool: IntoResponse` is not satisfied
--> tests/debug_handler/fail/wrong_return_type.rs:4:23
|
4 | async fn handler() -> bool {
| ^^^^ the trait `IntoResponse` is not implemented for `bool`
|
note: required by a bound in `__axum_macros_check_handler_into_response::{closure#0}::check`
--> tests/debug_handler/fail/wrong_return_type.rs:4:23
|
4 | async fn handler() -> bool {
| ^^^^ required by this bound in `__axum_macros_check_handler_into_response::{closure#0}::check`
@@ -0,0 +1,10 @@
use axum_macros::debug_handler;
struct A;
impl A {
#[debug_handler]
async fn handler() {}
}
fn main() {}
@@ -0,0 +1,10 @@
use axum::{body::BoxBody, http::Request};
use axum_macros::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() {}
@@ -0,0 +1,9 @@
use axum_macros::debug_handler;
use std::future::Future;
#[debug_handler]
fn handler() -> impl Future<Output = ()> {
async {}
}
fn main() {}
@@ -0,0 +1,9 @@
use axum_macros::debug_handler;
use axum::response::IntoResponse;
#[debug_handler]
async fn handler() -> impl IntoResponse {
"hi!"
}
fn main() {}
@@ -0,0 +1,6 @@
use axum_macros::debug_handler;
#[debug_handler]
async fn handler(_one: String, _two: String, _three: String) {}
fn main() {}
@@ -0,0 +1,9 @@
use axum_macros::debug_handler;
#[debug_handler]
async fn handler(mut foo: String) -> String {
foo += "bar";
foo
}
fn main() {}
@@ -0,0 +1,9 @@
use axum_macros::debug_handler;
use std::future::{Ready, ready};
#[debug_handler]
fn handler() -> Ready<()> {
ready(())
}
fn main() {}
@@ -0,0 +1,132 @@
use axum::{
async_trait,
extract::{FromRequest, RequestParts},
response::IntoResponse,
};
use axum_macros::debug_handler;
fn main() {}
#[debug_handler]
fn concrete_future() -> std::future::Ready<Result<impl IntoResponse, ()>> {
std::future::ready(Ok(()))
}
#[debug_handler]
fn impl_future() -> impl std::future::Future<Output = Result<impl IntoResponse, ()>> {
std::future::ready(Ok(()))
}
// === no args ===
#[debug_handler]
async fn handler_no_arg_one() -> Result<impl IntoResponse, ()> {
Ok(())
}
#[debug_handler]
async fn handler_no_arg_two() -> Result<(), impl IntoResponse> {
Err(())
}
#[debug_handler]
async fn handler_no_arg_three() -> Result<impl IntoResponse, impl IntoResponse> {
Ok::<_, ()>(())
}
#[debug_handler]
async fn handler_no_arg_four() -> Result<impl IntoResponse, impl IntoResponse> {
Err::<(), _>(())
}
// === args ===
#[debug_handler]
async fn handler_one(foo: String) -> Result<impl IntoResponse, ()> {
dbg!(foo);
Ok(())
}
#[debug_handler]
async fn handler_two(foo: String) -> Result<(), impl IntoResponse> {
dbg!(foo);
Err(())
}
#[debug_handler]
async fn handler_three(foo: String) -> Result<impl IntoResponse, impl IntoResponse> {
dbg!(foo);
Ok::<_, ()>(())
}
#[debug_handler]
async fn handler_four(foo: String) -> Result<impl IntoResponse, impl IntoResponse> {
dbg!(foo);
Err::<(), _>(())
}
// === no args with receiver ===
struct A;
impl A {
#[debug_handler]
async fn handler_no_arg_one(self) -> Result<impl IntoResponse, ()> {
Ok(())
}
#[debug_handler]
async fn handler_no_arg_two(self) -> Result<(), impl IntoResponse> {
Err(())
}
#[debug_handler]
async fn handler_no_arg_three(self) -> Result<impl IntoResponse, impl IntoResponse> {
Ok::<_, ()>(())
}
#[debug_handler]
async fn handler_no_arg_four(self) -> Result<impl IntoResponse, impl IntoResponse> {
Err::<(), _>(())
}
}
// === args with receiver ===
impl A {
#[debug_handler]
async fn handler_one(self, foo: String) -> Result<impl IntoResponse, ()> {
dbg!(foo);
Ok(())
}
#[debug_handler]
async fn handler_two(self, foo: String) -> Result<(), impl IntoResponse> {
dbg!(foo);
Err(())
}
#[debug_handler]
async fn handler_three(self, foo: String) -> Result<impl IntoResponse, impl IntoResponse> {
dbg!(foo);
Ok::<_, ()>(())
}
#[debug_handler]
async fn handler_four(self, foo: String) -> Result<impl IntoResponse, impl IntoResponse> {
dbg!(foo);
Err::<(), _>(())
}
}
#[async_trait]
impl<B> FromRequest<B> for A
where
B: Send + 'static,
{
type Rejection = ();
async fn from_request(_req: &mut RequestParts<B>) -> Result<Self, Self::Rejection> {
unimplemented!()
}
}
@@ -0,0 +1,19 @@
use axum::response::{IntoResponse, Response};
use axum_macros::debug_handler;
struct A;
impl A {
#[debug_handler]
async fn handler() -> Self {
A
}
}
impl IntoResponse for A {
fn into_response(self) -> Response {
todo!()
}
}
fn main() {}
@@ -0,0 +1,26 @@
use axum::{
async_trait,
extract::{FromRequest, RequestParts},
};
use axum_macros::debug_handler;
struct A;
#[async_trait]
impl<B> FromRequest<B> for A
where
B: Send + 'static,
{
type Rejection = ();
async fn from_request(_req: &mut RequestParts<B>) -> Result<Self, Self::Rejection> {
unimplemented!()
}
}
impl A {
#[debug_handler]
async fn handler(self) {}
}
fn main() {}
@@ -1,11 +1,11 @@
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);
| ^^^
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;
| ^^^^^^^^^^^^^^^^^^^^^^^^
@@ -1,5 +1,5 @@
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>);
| ^^^
@@ -1,5 +1,5 @@
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);
| ^^^
@@ -1,11 +1,11 @@
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);
| ^^^
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;
| ^^^^^^^^^^^^^^^^^^^^^^^^