From 18728185702c3f64c4e19220676f5e0d08c9885c Mon Sep 17 00:00:00 2001 From: Sergio Benitez Date: Fri, 16 Apr 2021 01:11:57 -0700 Subject: [PATCH] Support type generics in 'Responder' derive. --- core/codegen/src/derive/responder.rs | 7 +++++- core/codegen/src/lib.rs | 37 ++++++++++++++++++++++++++++ 2 files changed, 43 insertions(+), 1 deletion(-) diff --git a/core/codegen/src/derive/responder.rs b/core/codegen/src/derive/responder.rs index 136656c3..6454f5aa 100644 --- a/core/codegen/src/derive/responder.rs +++ b/core/codegen/src/derive/responder.rs @@ -18,13 +18,18 @@ struct FieldAttr { pub fn derive_responder(input: proc_macro::TokenStream) -> TokenStream { 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) + .type_bound(quote!(::rocket::response::Responder<'__r, '__o>)) .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(()) diff --git a/core/codegen/src/lib.rs b/core/codegen/src/lib.rs index 2f4a2794..f2f83367 100644 --- a/core/codegen/src/lib.rs +++ b/core/codegen/src/lib.rs @@ -545,6 +545,8 @@ pub fn derive_from_form(input: TokenStream) -> TokenStream { /// } /// ``` /// +/// # Semantics +/// /// The derive generates an implementation of the [`Responder`] trait for the /// 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 @@ -580,6 +582,8 @@ pub fn derive_from_form(input: TokenStream) -> TokenStream { /// /// Decorating the first field with `#[response(ignore)]` has no effect. /// +/// # Field Attribute +/// /// Additionally, the `response` attribute can be used on named structures and /// enum variants to override the status and/or content-type of the [`Response`] /// 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::set_header()`]: ../rocket/response/struct.Response.html#method.set_header /// [`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); +/// ``` +/// +/// 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); +/// ``` #[proc_macro_derive(Responder, attributes(response))] pub fn derive_responder(input: TokenStream) -> TokenStream { emit!(derive::responder::derive_responder(input))