Allow custom generic bounds in 'Responder' derive.

This commit is contained in:
Sergio Benitez 2021-05-26 02:26:11 -07:00
parent 02d6c4c6f1
commit 41d7138540
14 changed files with 626 additions and 98 deletions

View File

@ -14,7 +14,7 @@ proc-macro = true
[dependencies]
quote = "1.0"
devise = { git = "https://github.com/SergioBenitez/Devise.git", rev = "df00b5" }
devise = { git = "https://github.com/SergioBenitez/Devise.git", rev = "f2431b" }
[dev-dependencies]
version_check = "0.9"

View File

@ -19,12 +19,12 @@ indexmap = "1.0"
quote = "1.0"
syn = { version = "1.0.72", features = ["full", "visit", "visit-mut", "extra-traits"] }
proc-macro2 = "1.0.27"
devise = { git = "https://github.com/SergioBenitez/Devise.git", rev = "df00b5" }
devise = { git = "https://github.com/SergioBenitez/Devise.git", rev = "f2431b" }
rocket_http = { version = "0.5.0-dev", path = "../http/" }
unicode-xid = "0.2"
glob = "0.3"
[dev-dependencies]
rocket = { version = "0.5.0-dev", path = "../lib" }
rocket = { version = "0.5.0-dev", path = "../lib", features = ["json"] }
version_check = "0.9"
trybuild = "1.0"

View File

@ -47,8 +47,8 @@ fn context_type(input: Input<'_>) -> (TokenStream, Option<syn::WhereClause>) {
}
let span = input.ident().span();
gen.add_type_bound(&syn::parse_quote!(#_form::FromForm<#lifetime>));
gen.add_type_bound(&syn::TypeParamBound::from(lifetime));
gen.add_type_bound(syn::parse_quote!(#_form::FromForm<#lifetime>));
gen.add_type_bound(syn::TypeParamBound::from(lifetime));
let (_, ty_gen, where_clause) = gen.split_for_impl();
(quote_spanned!(span => FromFormGeneratedContext #ty_gen), where_clause.cloned())
}

View File

@ -1,12 +1,17 @@
use quote::ToTokens;
use devise::{*, ext::{TypeExt, SpanDiagnosticExt}};
use devise::{*, ext::{TypeExt, SpanDiagnosticExt, GenericsExt}};
use proc_macro2::TokenStream;
use syn::punctuated::Punctuated;
use syn::parse::Parser;
use crate::exports::*;
use crate::http_codegen::{ContentType, Status};
type WherePredicates = Punctuated<syn::WherePredicate, syn::Token![,]>;
#[derive(Debug, Default, FromMeta)]
struct ItemAttr {
bound: Option<SpanWrapped<String>>,
content_type: Option<SpanWrapped<ContentType>>,
status: Option<SpanWrapped<Status>>,
}
@ -17,20 +22,32 @@ struct FieldAttr {
}
pub fn derive_responder(input: proc_macro::TokenStream) -> TokenStream {
let impl_tokens = quote!(impl<'__r, '__o: '__r> ::rocket::response::Responder<'__r, '__o>);
let impl_tokens = quote!(impl<'r, 'o: 'r> ::rocket::response::Responder<'r, 'o>);
DeriveGenerator::build_for(input, impl_tokens)
.support(Support::Struct | Support::Enum | Support::Lifetime | Support::Type)
.replace_generic(1, 0)
.type_bound(quote!(::rocket::response::Responder<'__r, '__o>))
.type_bound_mapper(MapperBuild::new()
.try_input_map(|_, input| {
ItemAttr::one_from_attrs("response", input.attrs())?
.and_then(|attr| attr.bound)
.map(|bound| {
let span = bound.span;
let bounds = WherePredicates::parse_terminated.parse_str(&bound)
.map_err(|e| span.error(format!("invalid bound syntax: {}", e)))?;
Ok(quote_respanned!(span => #bounds))
})
.unwrap_or_else(|| {
let bound = quote!(::rocket::response::Responder<'r, 'o>);
let preds = input.generics().parsed_bounded_types(bound)?;
Ok(quote!(#preds))
})
})
)
.validator(ValidatorBuild::new()
.input_validate(|_, i| match i.generics().lifetimes().count() > 1 {
true => Err(i.generics().span().error("only one lifetime is supported")),
false => Ok(())
})
.input_validate(|_, i| match i.generics().type_params().count() > 1 {
true => Err(i.generics().span().error("only one type generic is supported")),
false => Ok(())
})
.fields_validate(|_, fields| match fields.is_empty() {
true => return Err(fields.span().error("need at least one field")),
false => Ok(())
@ -38,7 +55,7 @@ pub fn derive_responder(input: proc_macro::TokenStream) -> TokenStream {
)
.inner_mapper(MapperBuild::new()
.with_output(|_, output| quote! {
fn respond_to(self, __req: &'__r #Request<'_>) -> #_response::Result<'__o> {
fn respond_to(self, __req: &'r #Request<'_>) -> #_response::Result<'o> {
#output
}
})

View File

@ -758,29 +758,52 @@ pub fn derive_from_form(input: TokenStream) -> TokenStream {
///
/// # Generics
///
/// The derive accepts at most one type generic and at most one lifetime
/// The derive accepts any number of type generics and at most one lifetime
/// generic. If a type generic is present, the generated implementation will
/// require a bound of `Responder` for the generic. As such, the generic should
/// be used as a `Responder`:
/// require a bound of `Responder<'r, 'o>` for each generic unless a
/// `#[response(bound = ...)]` attribute as used:
///
/// ```rust
/// # #[macro_use] extern crate rocket;
/// use rocket::serde::Serialize;
/// use rocket::serde::json::Json;
/// use rocket::http::ContentType;
/// use rocket::response::Responder;
///
/// // The bound `T: Responder` will be added to the generated implementation.
/// #[derive(Responder)]
/// #[response(status = 404, content_type = "html")]
/// struct NotFoundHtml<T>(T);
///
/// // The bound `T: Serialize` will be added to the generated implementation.
/// // This would fail to compile otherwise.
/// #[derive(Responder)]
/// #[response(bound = "T: Serialize", status = 404)]
/// struct NotFoundJson<T>(Json<T>);
///
/// // The bounds `T: Serialize, E: Responder` will be added to the generated
/// // implementation. This would fail to compile otherwise.
/// #[derive(Responder)]
/// #[response(bound = "T: Serialize, E: Responder<'r, 'o>")]
/// enum MyResult<T, E> {
/// Ok(Json<T>),
/// #[response(status = 404)]
/// Err(E, ContentType)
/// }
/// ```
///
/// If a lifetime generic is present, it will be used as the second lifetime
/// paramter `'o` parameter in `impl Responder<'r, 'o>`:
/// If a lifetime generic is present, it will be replace with `'o` in the
/// generated implementation `impl Responder<'r, 'o>`:
///
/// ```rust
/// # #[macro_use] extern crate rocket;
/// // Generates `impl<'r, 'o> Responder<'r, 'o> for NotFoundHtmlString<'o>`.
/// #[derive(Responder)]
/// #[response(status = 404, content_type = "html")]
/// struct NotFoundHtmlString<'o>(&'o str);
/// struct NotFoundHtmlString<'a>(&'a str);
/// ```
///
/// Both a type generic and lifetime generic may be used:
/// Both type generics and lifetime generic may be used:
///
/// ```rust
/// # #[macro_use] extern crate rocket;

View File

@ -0,0 +1,60 @@
// Rocket sometimes generates mangled identifiers that activate the
// non_snake_case lint. We deny the lint in this test to ensure that
// code generation uses #[allow(non_snake_case)] in the appropriate places.
#![deny(non_snake_case)]
#[macro_use] extern crate rocket;
use rocket::{Request, Rocket, Build};
use rocket::local::blocking::Client;
use rocket::http::{Status, ContentType};
#[catch(404)] fn not_found_0() -> &'static str { "404-0" }
#[catch(404)] fn not_found_1(_: &Request) -> &'static str { "404-1" }
#[catch(404)] fn not_found_2(_: Status, _: &Request) -> &'static str { "404-2" }
#[catch(default)] fn all(_: Status, r: &Request) -> String { r.uri().to_string() }
#[test]
fn test_simple_catchers() {
fn rocket() -> Rocket<Build> {
rocket::build()
.register("/0", catchers![not_found_0])
.register("/1", catchers![not_found_1])
.register("/2", catchers![not_found_2])
.register("/", catchers![all])
}
let client = Client::debug(rocket()).unwrap();
for i in 0..6 {
let response = client.get(format!("/{}", i)).dispatch();
assert_eq!(response.status(), Status::NotFound);
match i {
0..=2 => assert_eq!(response.into_string().unwrap(), format!("404-{}", i)),
_ => assert_eq!(response.into_string().unwrap(), format!("/{}", i)),
}
}
}
#[get("/<code>")] fn forward(code: u16) -> Status { Status::new(code) }
#[catch(400)] fn forward_400(status: Status, _: &Request) -> String { status.code.to_string() }
#[catch(404)] fn forward_404(status: Status, _: &Request) -> String { status.code.to_string() }
#[catch(444)] fn forward_444(status: Status, _: &Request) -> String { status.code.to_string() }
#[catch(500)] fn forward_500(status: Status, _: &Request) -> String { status.code.to_string() }
#[test]
fn test_status_param() {
fn rocket() -> Rocket<Build> {
rocket::build()
.mount("/", routes![forward])
.register("/", catchers![forward_400, forward_404, forward_444, forward_500])
}
let client = Client::debug(rocket()).unwrap();
for code in &[400, 404, 444, 400, 800, 3480] {
let response = client.get(uri!(forward(*code))).dispatch();
let code = std::cmp::min(*code, 500);
assert_eq!(response.status(), Status::new(code));
assert_eq!(response.into_string().unwrap(), code.to_string());
}
}

View File

@ -1,6 +1,6 @@
use rocket::local::asynchronous::Client;
use rocket::response::Responder;
use rocket::http::{Status, ContentType, Cookie};
use rocket::response::Responder;
#[derive(Responder)]
pub enum Foo<'r> {
@ -107,3 +107,42 @@ async fn responder_baz() {
assert_eq!(r.content_type(), Some(ContentType::new("application", "x-custom")));
assert_eq!(r.body_mut().to_string().await.unwrap(), "just a custom");
}
use rocket::serde::json::Json;
// The bounds `T: Serialize, E: Responder` will be added to the generated
// implementation. This would fail to compile otherwise.
#[derive(Responder)]
#[response(bound = "T: rocket::serde::Serialize, E: Responder<'r, 'o>")]
enum MyResult<'a, T, E> {
Ok(Json<T>),
#[response(status = 404)]
Err(E, ContentType),
#[response(status = 500)]
Other(&'a str),
}
#[rocket::async_test]
async fn generic_responder() {
let client = Client::debug_with(vec![]).await.expect("valid rocket");
let local_req = client.get("/");
let req = local_req.inner();
let v: MyResult<_, ()> = MyResult::Ok(Json("hi"));
let mut r = v.respond_to(req).unwrap();
assert_eq!(r.status(), Status::Ok);
assert_eq!(r.content_type().unwrap(), ContentType::JSON);
assert_eq!(r.body_mut().to_string().await.unwrap(), "\"hi\"");
let v: MyResult<(), &[u8]> = MyResult::Err(&[7, 13, 23], ContentType::JPEG);
let mut r = v.respond_to(req).unwrap();
assert_eq!(r.status(), Status::NotFound);
assert_eq!(r.content_type().unwrap(), ContentType::JPEG);
assert_eq!(r.body_mut().to_bytes().await.unwrap(), vec![7, 13, 23]);
let v: MyResult<(), &[u8]> = MyResult::Other("beep beep");
let mut r = v.respond_to(req).unwrap();
assert_eq!(r.status(), Status::InternalServerError);
assert_eq!(r.content_type().unwrap(), ContentType::Text);
assert_eq!(r.body_mut().to_string().await.unwrap(), "beep beep");
}

View File

@ -0,0 +1 @@
../ui-fail/responder.rs

View File

@ -1,158 +1,217 @@
error: need at least one field
--> $DIR/responder.rs:6:1
--> $DIR/responder.rs:4:1
|
6 | struct Thing1;
4 | struct Thing1;
| ^^^^^^^^^^^^^^
|
note: error occurred while deriving `Responder`
--> $DIR/responder.rs:5:10
--> $DIR/responder.rs:3:10
|
5 | #[derive(Responder)]
3 | #[derive(Responder)]
| ^^^^^^^^^
= note: this error originates in the derive macro `Responder` (in Nightly builds, run with -Z macro-backtrace for more info)
error: need at least one field
--> $DIR/responder.rs:10:14
|
10 | struct Thing2();
| ^^
|
--> $DIR/responder.rs:7:14
|
7 | struct Thing2();
| ^^
|
note: error occurred while deriving `Responder`
--> $DIR/responder.rs:9:10
|
9 | #[derive(Responder)]
| ^^^^^^^^^
--> $DIR/responder.rs:6:10
|
6 | #[derive(Responder)]
| ^^^^^^^^^
= note: this error originates in the derive macro `Responder` (in Nightly builds, run with -Z macro-backtrace for more info)
error: need at least one field
--> $DIR/responder.rs:15:5
--> $DIR/responder.rs:13:12
|
15 | Bark,
| ^^^^
13 | enum Foo { Bark, }
| ^^^^
|
note: error occurred while deriving `Responder`
--> $DIR/responder.rs:13:10
--> $DIR/responder.rs:12:10
|
13 | #[derive(Responder)]
12 | #[derive(Responder)]
| ^^^^^^^^^
= note: this error originates in the derive macro `Responder` (in Nightly builds, run with -Z macro-backtrace for more info)
error: only one lifetime is supported
--> $DIR/responder.rs:20:14
--> $DIR/responder.rs:16:14
|
20 | struct Thing4<'a, 'b>(&'a str, &'b str);
16 | struct Thing4<'a, 'b>(&'a str, &'b str);
| ^^^^^^^^
|
note: error occurred while deriving `Responder`
--> $DIR/responder.rs:19:10
--> $DIR/responder.rs:15:10
|
19 | #[derive(Responder)]
15 | #[derive(Responder)]
| ^^^^^^^^^
= note: this error originates in the derive macro `Responder` (in Nightly builds, run with -Z macro-backtrace for more info)
error: type generics are not supported
--> $DIR/responder.rs:24:15
error: invalid or unknown content type
--> $DIR/responder.rs:25:27
|
24 | struct Thing5<T>(T);
| ^
25 | #[response(content_type = "")]
| ^^
|
note: error occurred while deriving `Responder`
--> $DIR/responder.rs:23:10
--> $DIR/responder.rs:24:10
|
23 | #[derive(Responder)]
24 | #[derive(Responder)]
| ^^^^^^^^^
= note: this error originates in the derive macro `Responder` (in Nightly builds, run with -Z macro-backtrace for more info)
error: type generics are not supported
--> $DIR/responder.rs:28:23
error: invalid or unknown content type
--> $DIR/responder.rs:29:27
|
28 | struct Thing6<'a, 'b, T>(&'a str, &'b str, T);
| ^
29 | #[response(content_type = "idk")]
| ^^^^^
|
note: error occurred while deriving `Responder`
--> $DIR/responder.rs:27:10
--> $DIR/responder.rs:28:10
|
27 | #[derive(Responder)]
28 | #[derive(Responder)]
| ^^^^^^^^^
= note: this error originates in the derive macro `Responder` (in Nightly builds, run with -Z macro-backtrace for more info)
error: invalid or unknown content-type
--> $DIR/responder.rs:33:31
error: invalid value: expected string literal
--> $DIR/responder.rs:33:27
|
33 | #[response(content_type = "")]
| ^^
33 | #[response(content_type = 100)]
| ^^^
|
note: error occurred while deriving `Responder`
--> $DIR/responder.rs:31:10
--> $DIR/responder.rs:32:10
|
31 | #[derive(Responder)]
32 | #[derive(Responder)]
| ^^^^^^^^^
= note: this error originates in the derive macro `Responder` (in Nightly builds, run with -Z macro-backtrace for more info)
error: invalid or unknown content-type
--> $DIR/responder.rs:40:31
error: status must be in range [100, 599]
--> $DIR/responder.rs:37:21
|
40 | #[response(content_type = "idk")]
| ^^^^^
37 | #[response(status = 8)]
| ^
|
note: error occurred while deriving `Responder`
--> $DIR/responder.rs:38:10
--> $DIR/responder.rs:36:10
|
38 | #[derive(Responder)]
36 | #[derive(Responder)]
| ^^^^^^^^^
= note: this error originates in the derive macro `Responder` (in Nightly builds, run with -Z macro-backtrace for more info)
error: invalid value: expected string
--> $DIR/responder.rs:47:31
error: invalid value: expected unsigned integer literal
--> $DIR/responder.rs:41:21
|
47 | #[response(content_type = 100)]
| ^^^
41 | #[response(status = "404")]
| ^^^^^
|
note: error occurred while deriving `Responder`
--> $DIR/responder.rs:45:10
--> $DIR/responder.rs:40:10
|
45 | #[derive(Responder)]
40 | #[derive(Responder)]
| ^^^^^^^^^
= note: this error originates in the derive macro `Responder` (in Nightly builds, run with -Z macro-backtrace for more info)
error: status must be in range [100, 600)
--> $DIR/responder.rs:54:25
error: invalid value: expected unsigned integer literal
--> $DIR/responder.rs:45:21
|
54 | #[response(status = 8)]
| ^
45 | #[response(status = "404", content_type = "html")]
| ^^^^^
|
note: error occurred while deriving `Responder`
--> $DIR/responder.rs:44:10
|
44 | #[derive(Responder)]
| ^^^^^^^^^
= note: this error originates in the derive macro `Responder` (in Nightly builds, run with -Z macro-backtrace for more info)
error: invalid value: expected string literal
--> $DIR/responder.rs:49:41
|
49 | #[response(status = 404, content_type = 120)]
| ^^^
|
note: error occurred while deriving `Responder`
--> $DIR/responder.rs:48:10
|
48 | #[derive(Responder)]
| ^^^^^^^^^
= note: this error originates in the derive macro `Responder` (in Nightly builds, run with -Z macro-backtrace for more info)
error: invalid value: expected string literal
--> $DIR/responder.rs:53:20
|
53 | #[response(bound = 10)]
| ^^
|
note: error occurred while deriving `Responder`
--> $DIR/responder.rs:52:10
|
52 | #[derive(Responder)]
| ^^^^^^^^^
= note: this error originates in the derive macro `Responder` (in Nightly builds, run with -Z macro-backtrace for more info)
error: invalid value: expected unsigned integer
--> $DIR/responder.rs:61:25
error: invalid bound syntax: expected `:`
--> $DIR/responder.rs:65:20
|
61 | #[response(status = "404")]
| ^^^^^
65 | #[response(bound = "ponies are cool")]
| ^^^^^^^^^^^^^^^^^
|
note: error occurred while deriving `Responder`
--> $DIR/responder.rs:59:10
--> $DIR/responder.rs:64:10
|
59 | #[derive(Responder)]
64 | #[derive(Responder)]
| ^^^^^^^^^
= note: this error originates in the derive macro `Responder` (in Nightly builds, run with -Z macro-backtrace for more info)
error: invalid value: expected unsigned integer
--> $DIR/responder.rs:68:25
error: invalid bound syntax: expected `,`
--> $DIR/responder.rs:69:20
|
68 | #[response(status = "404", content_type = "html")]
| ^^^^^
69 | #[response(bound = "T: ROCKETS + ARE COOLER")]
| ^^^^^^^^^^^^^^^^^^^^^^^^^
|
note: error occurred while deriving `Responder`
--> $DIR/responder.rs:66:10
--> $DIR/responder.rs:68:10
|
66 | #[derive(Responder)]
68 | #[derive(Responder)]
| ^^^^^^^^^
= note: this error originates in the derive macro `Responder` (in Nightly builds, run with -Z macro-backtrace for more info)
error: invalid value: expected string
--> $DIR/responder.rs:75:45
error[E0277]: the trait bound `Header<'_>: From<E>` is not satisfied
--> $DIR/responder.rs:22:24
|
75 | #[response(status = 404, content_type = 120)]
| ^^^
22 | struct Thing6<T, E>(T, E); // NO ERROR
| ^ the trait `From<E>` is not implemented for `Header<'_>`
|
note: error occurred while deriving `Responder`
--> $DIR/responder.rs:73:10
= note: required because of the requirements on the impl of `Into<Header<'_>>` for `E`
help: consider extending the `where` bound, but there might be an alternative better way to express this requirement
|
73 | #[derive(Responder)]
| ^^^^^^^^^
21 | #[derive(Responder, Header<'_>: From<E>)]
| ^^^^^^^^^^^^^^^^^^^^^
error: aborting due to 13 previous errors
error[E0277]: the trait bound `T: Responder<'_, '_>` is not satisfied
--> $DIR/responder.rs:58:19
|
58 | struct Thing15<T>(T);
| ^ the trait `Responder<'_, '_>` is not implemented for `T`
|
= note: required by `respond_to`
help: consider further restricting this bound
|
57 | #[response(bound = "T: std::fmt::Display" + rocket::response::Responder<'_, '_>)]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error[E0277]: the trait bound `T: Responder<'_, '_>` is not satisfied
--> $DIR/responder.rs:62:19
|
62 | struct Thing16<T>(T);
| ^ the trait `Responder<'_, '_>` is not implemented for `T`
|
= note: required by `respond_to`
help: consider further restricting this bound
|
61 | #[response(bound = "T: std::fmt::Display" + rocket::response::Responder<'_, '_>)]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

View File

@ -0,0 +1 @@
../ui-fail/responder.rs

View File

@ -0,0 +1,231 @@
error: need at least one field
--> $DIR/responder.rs:4:1
|
4 | struct Thing1;
| ^^^^^^
error: [note] error occurred while deriving `Responder`
--> $DIR/responder.rs:3:10
|
3 | #[derive(Responder)]
| ^^^^^^^^^
|
= note: this error originates in a derive macro (in Nightly builds, run with -Z macro-backtrace for more info)
error: need at least one field
--> $DIR/responder.rs:7:14
|
7 | struct Thing2();
| ^^
error: [note] error occurred while deriving `Responder`
--> $DIR/responder.rs:6:10
|
6 | #[derive(Responder)]
| ^^^^^^^^^
|
= note: this error originates in a derive macro (in Nightly builds, run with -Z macro-backtrace for more info)
error: need at least one field
--> $DIR/responder.rs:13:12
|
13 | enum Foo { Bark, }
| ^^^^
error: [note] error occurred while deriving `Responder`
--> $DIR/responder.rs:12:10
|
12 | #[derive(Responder)]
| ^^^^^^^^^
|
= note: this error originates in a derive macro (in Nightly builds, run with -Z macro-backtrace for more info)
error: only one lifetime is supported
--> $DIR/responder.rs:16:14
|
16 | struct Thing4<'a, 'b>(&'a str, &'b str);
| ^
error: [note] error occurred while deriving `Responder`
--> $DIR/responder.rs:15:10
|
15 | #[derive(Responder)]
| ^^^^^^^^^
|
= note: this error originates in a derive macro (in Nightly builds, run with -Z macro-backtrace for more info)
error: invalid or unknown content type
--> $DIR/responder.rs:25:27
|
25 | #[response(content_type = "")]
| ^^
error: [note] error occurred while deriving `Responder`
--> $DIR/responder.rs:24:10
|
24 | #[derive(Responder)]
| ^^^^^^^^^
|
= note: this error originates in a derive macro (in Nightly builds, run with -Z macro-backtrace for more info)
error: invalid or unknown content type
--> $DIR/responder.rs:29:27
|
29 | #[response(content_type = "idk")]
| ^^^^^
error: [note] error occurred while deriving `Responder`
--> $DIR/responder.rs:28:10
|
28 | #[derive(Responder)]
| ^^^^^^^^^
|
= note: this error originates in a derive macro (in Nightly builds, run with -Z macro-backtrace for more info)
error: invalid value: expected string literal
--> $DIR/responder.rs:33:27
|
33 | #[response(content_type = 100)]
| ^^^
error: [note] error occurred while deriving `Responder`
--> $DIR/responder.rs:32:10
|
32 | #[derive(Responder)]
| ^^^^^^^^^
|
= note: this error originates in a derive macro (in Nightly builds, run with -Z macro-backtrace for more info)
error: status must be in range [100, 599]
--> $DIR/responder.rs:37:21
|
37 | #[response(status = 8)]
| ^
error: [note] error occurred while deriving `Responder`
--> $DIR/responder.rs:36:10
|
36 | #[derive(Responder)]
| ^^^^^^^^^
|
= note: this error originates in a derive macro (in Nightly builds, run with -Z macro-backtrace for more info)
error: invalid value: expected unsigned integer literal
--> $DIR/responder.rs:41:21
|
41 | #[response(status = "404")]
| ^^^^^
error: [note] error occurred while deriving `Responder`
--> $DIR/responder.rs:40:10
|
40 | #[derive(Responder)]
| ^^^^^^^^^
|
= note: this error originates in a derive macro (in Nightly builds, run with -Z macro-backtrace for more info)
error: invalid value: expected unsigned integer literal
--> $DIR/responder.rs:45:21
|
45 | #[response(status = "404", content_type = "html")]
| ^^^^^
error: [note] error occurred while deriving `Responder`
--> $DIR/responder.rs:44:10
|
44 | #[derive(Responder)]
| ^^^^^^^^^
|
= note: this error originates in a derive macro (in Nightly builds, run with -Z macro-backtrace for more info)
error: invalid value: expected string literal
--> $DIR/responder.rs:49:41
|
49 | #[response(status = 404, content_type = 120)]
| ^^^
error: [note] error occurred while deriving `Responder`
--> $DIR/responder.rs:48:10
|
48 | #[derive(Responder)]
| ^^^^^^^^^
|
= note: this error originates in a derive macro (in Nightly builds, run with -Z macro-backtrace for more info)
error: invalid value: expected string literal
--> $DIR/responder.rs:53:20
|
53 | #[response(bound = 10)]
| ^^
error: [note] error occurred while deriving `Responder`
--> $DIR/responder.rs:52:10
|
52 | #[derive(Responder)]
| ^^^^^^^^^
|
= note: this error originates in a derive macro (in Nightly builds, run with -Z macro-backtrace for more info)
error: invalid bound syntax: expected `:`
--> $DIR/responder.rs:65:20
|
65 | #[response(bound = "ponies are cool")]
| ^^^^^^^^^^^^^^^^^
error: [note] error occurred while deriving `Responder`
--> $DIR/responder.rs:64:10
|
64 | #[derive(Responder)]
| ^^^^^^^^^
|
= note: this error originates in a derive macro (in Nightly builds, run with -Z macro-backtrace for more info)
error: invalid bound syntax: expected `,`
--> $DIR/responder.rs:69:20
|
69 | #[response(bound = "T: ROCKETS + ARE COOLER")]
| ^^^^^^^^^^^^^^^^^^^^^^^^^
error: [note] error occurred while deriving `Responder`
--> $DIR/responder.rs:68:10
|
68 | #[derive(Responder)]
| ^^^^^^^^^
|
= note: this error originates in a derive macro (in Nightly builds, run with -Z macro-backtrace for more info)
error[E0277]: the trait bound `Header<'_>: From<E>` is not satisfied
--> $DIR/responder.rs:22:24
|
22 | struct Thing6<T, E>(T, E); // NO ERROR
| ^ the trait `From<E>` is not implemented for `Header<'_>`
|
= note: required because of the requirements on the impl of `Into<Header<'_>>` for `E`
help: consider extending the `where` bound, but there might be an alternative better way to express this requirement
|
21 | #[derive(Responder, Header<'_>: From<E>)]
| ^^^^^^^^^^^^^^^^^^^^^
error[E0277]: the trait bound `T: Responder<'_, '_>` is not satisfied
--> $DIR/responder.rs:58:19
|
58 | struct Thing15<T>(T);
| ^ the trait `Responder<'_, '_>` is not implemented for `T`
|
= note: required by `respond_to`
help: consider further restricting this bound
|
57 | #[response(bound = "T: std::fmt::Display" + Responder<'_, '_>)]
| ^^^^^^^^^^^^^^^^^^^
error[E0277]: the trait bound `T: Responder<'_, '_>` is not satisfied
--> $DIR/responder.rs:62:19
|
62 | struct Thing16<T>(T);
| ^ the trait `Responder<'_, '_>` is not implemented for `T`
|
= note: required by `respond_to`
help: consider further restricting this bound
|
61 | #[response(bound = "T: std::fmt::Display" + Responder<'_, '_>)]
| ^^^^^^^^^^^^^^^^^^^

View File

@ -0,0 +1,72 @@
#[macro_use] extern crate rocket;
#[derive(Responder)]
struct Thing1;
#[derive(Responder)]
struct Thing2();
#[derive(Responder)]
enum Bar { } // NO ERROR
#[derive(Responder)]
enum Foo { Bark, }
#[derive(Responder)]
struct Thing4<'a, 'b>(&'a str, &'b str);
#[derive(Responder)]
struct Thing5<T>(T); // NO ERROR
#[derive(Responder)]
struct Thing6<T, E>(T, E); // NO ERROR
#[derive(Responder)]
#[response(content_type = "")]
struct Thing7(());
#[derive(Responder)]
#[response(content_type = "idk")]
struct Thing8(());
#[derive(Responder)]
#[response(content_type = 100)]
struct Thing9(());
#[derive(Responder)]
#[response(status = 8)]
struct Thing10(());
#[derive(Responder)]
#[response(status = "404")]
struct Thing11(());
#[derive(Responder)]
#[response(status = "404", content_type = "html")]
struct Thing12(());
#[derive(Responder)]
#[response(status = 404, content_type = 120)]
struct Thing13(());
#[derive(Responder)]
#[response(bound = 10)]
struct Thing14(());
#[derive(Responder)]
#[response(bound = "T: std::fmt::Display")]
struct Thing15<T>(T);
#[derive(Responder)]
#[response(bound = "T: std::fmt::Display")]
struct Thing16<T>(T);
#[derive(Responder)]
#[response(bound = "ponies are cool")]
struct Thing17<T>(T);
#[derive(Responder)]
#[response(bound = "T: ROCKETS + ARE COOLER")]
struct Thing18<T>(T);
fn main() {}

View File

@ -0,0 +1,24 @@
#!/usr/bin/env bash
set -e
# Symlinks all of the tests in this directory with those in sibling
# `ui-fail-stable` and `ui-fail-nightly` directories.
SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
stable="${SCRIPT_DIR}/../ui-fail-stable"
nightly="${SCRIPT_DIR}/../ui-fail-nightly"
anchor="$(basename ${SCRIPT_DIR})"
echo ":: Synchronizing..."
echo " stable: ${stable}"
echo " nightly: ${nightly}"
echo " anchor: ${anchor}"
for dir in "${stable}" "${nightly}"; do
find "${dir}" -type l -delete
for file in "${SCRIPT_DIR}"/*.rs; do
ln -s "../${anchor}/$(basename $file)" "${dir}/"
done
done

View File

@ -160,6 +160,7 @@ impl<'r, T: Deserialize<'r>> MsgPack<T> {
Self::from_bytes(local_cache!(req, bytes))
}
}
#[crate::async_trait]
impl<'r, T: Deserialize<'r>> FromData<'r> for MsgPack<T> {
type Error = Error;