Support type generics in 'Responder' derive.

This commit is contained in:
Sergio Benitez 2021-04-16 01:11:57 -07:00
parent fe4d0425e6
commit 1872818570
2 changed files with 43 additions and 1 deletions

View File

@ -18,13 +18,18 @@ struct FieldAttr {
pub fn derive_responder(input: proc_macro::TokenStream) -> TokenStream { pub fn derive_responder(input: proc_macro::TokenStream) -> TokenStream {
DeriveGenerator::build_for(input, quote!(impl<'__r, '__o: '__r> ::rocket::response::Responder<'__r, '__o>)) DeriveGenerator::build_for(input, quote!(impl<'__r, '__o: '__r> ::rocket::response::Responder<'__r, '__o>))
.support(Support::Struct | Support::Enum | Support::Lifetime) .support(Support::Struct | Support::Enum | Support::Lifetime | Support::Type)
.replace_generic(1, 0) .replace_generic(1, 0)
.type_bound(quote!(::rocket::response::Responder<'__r, '__o>))
.validator(ValidatorBuild::new() .validator(ValidatorBuild::new()
.input_validate(|_, i| match i.generics().lifetimes().count() > 1 { .input_validate(|_, i| match i.generics().lifetimes().count() > 1 {
true => Err(i.generics().span().error("only one lifetime is supported")), true => Err(i.generics().span().error("only one lifetime is supported")),
false => Ok(()) 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() { .fields_validate(|_, fields| match fields.is_empty() {
true => return Err(fields.span().error("need at least one field")), true => return Err(fields.span().error("need at least one field")),
false => Ok(()) false => Ok(())

View File

@ -545,6 +545,8 @@ pub fn derive_from_form(input: TokenStream) -> TokenStream {
/// } /// }
/// ``` /// ```
/// ///
/// # Semantics
///
/// The derive generates an implementation of the [`Responder`] trait for the /// The derive generates an implementation of the [`Responder`] trait for the
/// decorated enum or structure. The derive uses the _first_ field of a variant /// decorated enum or structure. The derive uses the _first_ field of a variant
/// or structure to generate a [`Response`]. As such, the type of the first /// or structure to generate a [`Response`]. As such, the type of the first
@ -580,6 +582,8 @@ pub fn derive_from_form(input: TokenStream) -> TokenStream {
/// ///
/// Decorating the first field with `#[response(ignore)]` has no effect. /// Decorating the first field with `#[response(ignore)]` has no effect.
/// ///
/// # Field Attribute
///
/// Additionally, the `response` attribute can be used on named structures and /// Additionally, the `response` attribute can be used on named structures and
/// enum variants to override the status and/or content-type of the [`Response`] /// enum variants to override the status and/or content-type of the [`Response`]
/// produced by the generated implementation. The `response` attribute used in /// produced by the generated implementation. The `response` attribute used in
@ -646,6 +650,39 @@ pub fn derive_from_form(input: TokenStream) -> TokenStream {
/// [`Response`]: ../rocket/struct.Response.html /// [`Response`]: ../rocket/struct.Response.html
/// [`Response::set_header()`]: ../rocket/response/struct.Response.html#method.set_header /// [`Response::set_header()`]: ../rocket/response/struct.Response.html#method.set_header
/// [`ContentType::parse_flexible()`]: ../rocket/http/struct.ContentType.html#method.parse_flexible /// [`ContentType::parse_flexible()`]: ../rocket/http/struct.ContentType.html#method.parse_flexible
///
/// # Generics
///
/// The derive accepts at most one type generic 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`:
///
/// ```rust
/// # #[macro_use] extern crate rocket;
/// #[derive(Responder)]
/// #[response(status = 404, content_type = "html")]
/// struct NotFoundHtml<T>(T);
/// ```
///
/// If a lifetime generic is present, it will be used as the second lifetime
/// paramter `'o` parameter in `impl Responder<'r, 'o>`:
///
/// ```rust
/// # #[macro_use] extern crate rocket;
/// #[derive(Responder)]
/// #[response(status = 404, content_type = "html")]
/// struct NotFoundHtmlString<'o>(&'o str);
/// ```
///
/// Both a type generic and lifetime generic may be used:
///
/// ```rust
/// # #[macro_use] extern crate rocket;
/// # use rocket::response::Responder;
/// #[derive(Responder)]
/// struct SomeResult<'o, T>(Result<T, &'o str>);
/// ```
#[proc_macro_derive(Responder, attributes(response))] #[proc_macro_derive(Responder, attributes(response))]
pub fn derive_responder(input: TokenStream) -> TokenStream { pub fn derive_responder(input: TokenStream) -> TokenStream {
emit!(derive::responder::derive_responder(input)) emit!(derive::responder::derive_responder(input))