Add lifetime params to Request/Response when needed
This commit is contained in:
parent
c2342e3ef7
commit
e7f7c3bb9d
@ -102,13 +102,13 @@ impl ToTokens for Api {
|
||||
let request_type = &self.request;
|
||||
let response_type = &self.response;
|
||||
|
||||
let request_try_from_type = if self.request.uses_wrap_incoming() {
|
||||
let request_try_from_type = if self.request.contains_lifetimes() {
|
||||
quote!(IncomingRequest)
|
||||
} else {
|
||||
quote!(Request)
|
||||
};
|
||||
|
||||
let response_try_from_type = if self.response.uses_wrap_incoming() {
|
||||
let response_try_from_type = if self.response.contains_lifetimes() {
|
||||
quote!(IncomingResponse)
|
||||
} else {
|
||||
quote!(Response)
|
||||
@ -222,6 +222,21 @@ impl ToTokens for Api {
|
||||
|
||||
let error = &self.error;
|
||||
|
||||
let res_life = self.response.lifetimes().collect::<Vec<_>>();
|
||||
let req_life = self.request.lifetimes().collect::<Vec<_>>();
|
||||
|
||||
let response_lifetimes = util::generics_to_tokens(res_life.iter().cloned());
|
||||
let request_lifetimes = util::generics_to_tokens(req_life.iter().cloned());
|
||||
|
||||
let endpoint_impl_lifetimes = if res_life != req_life {
|
||||
let diff =
|
||||
res_life.into_iter().filter(|resp| !req_life.contains(resp)).collect::<Vec<_>>();
|
||||
|
||||
util::generics_to_tokens(req_life.iter().cloned().chain(diff))
|
||||
} else {
|
||||
request_lifetimes.clone()
|
||||
};
|
||||
|
||||
let api = quote! {
|
||||
// FIXME: These can't conflict with other imports, but it would still be nice not to
|
||||
// bring anything into scope that code outside the macro could then rely on.
|
||||
@ -260,13 +275,13 @@ impl ToTokens for Api {
|
||||
#[doc = #response_doc]
|
||||
#response_type
|
||||
|
||||
impl ::std::convert::TryFrom<Response>
|
||||
impl #response_lifetimes ::std::convert::TryFrom<Response #response_lifetimes>
|
||||
for ::ruma_api::exports::http::Response<Vec<u8>>
|
||||
{
|
||||
type Error = ::ruma_api::error::IntoHttpError;
|
||||
|
||||
#[allow(unused_variables)]
|
||||
fn try_from(response: Response) -> ::std::result::Result<Self, Self::Error> {
|
||||
fn try_from(response: Response #response_lifetimes) -> ::std::result::Result<Self, Self::Error> {
|
||||
let response = ::ruma_api::exports::http::Response::builder()
|
||||
.header(::ruma_api::exports::http::header::CONTENT_TYPE, "application/json")
|
||||
#serialize_response_headers
|
||||
@ -304,8 +319,8 @@ impl ToTokens for Api {
|
||||
}
|
||||
}
|
||||
|
||||
impl ::ruma_api::Endpoint for Request {
|
||||
type Response = Response;
|
||||
impl #endpoint_impl_lifetimes ::ruma_api::Endpoint for Request #request_lifetimes {
|
||||
type Response = Response #response_lifetimes;
|
||||
type ResponseError = #error;
|
||||
|
||||
/// Metadata for the `#name` endpoint.
|
||||
|
@ -1,10 +1,10 @@
|
||||
//! Details of the `request` section of the procedural macro.
|
||||
|
||||
use std::{convert::TryFrom, mem};
|
||||
use std::{collections::BTreeSet, convert::TryFrom, mem};
|
||||
|
||||
use proc_macro2::TokenStream;
|
||||
use quote::{quote, quote_spanned, ToTokens};
|
||||
use syn::{spanned::Spanned, Field, Ident};
|
||||
use syn::{spanned::Spanned, Field, Ident, Lifetime};
|
||||
|
||||
use crate::{
|
||||
api::{
|
||||
@ -18,6 +18,9 @@ use crate::{
|
||||
pub struct Request {
|
||||
/// The fields of the request.
|
||||
fields: Vec<RequestField>,
|
||||
|
||||
/// The collected lifetime identifiers from the declared fields.
|
||||
lifetimes: Vec<Lifetime>,
|
||||
}
|
||||
|
||||
impl Request {
|
||||
@ -98,9 +101,13 @@ impl Request {
|
||||
self.fields.iter().filter_map(|field| field.as_body_field())
|
||||
}
|
||||
|
||||
/// Whether any field has a #[wrap_incoming] attribute.
|
||||
pub fn uses_wrap_incoming(&self) -> bool {
|
||||
self.fields.iter().any(|f| f.has_wrap_incoming_attr())
|
||||
/// Whether any field has a lifetime.
|
||||
pub fn contains_lifetimes(&self) -> bool {
|
||||
self.fields.iter().any(|f| util::has_lifetime(&f.field().ty))
|
||||
}
|
||||
|
||||
pub fn lifetimes(&self) -> impl Iterator<Item = &Lifetime> {
|
||||
self.lifetimes.iter()
|
||||
}
|
||||
|
||||
/// Produces an iterator over all the header fields.
|
||||
@ -194,6 +201,7 @@ impl TryFrom<RawRequest> for Request {
|
||||
fn try_from(raw: RawRequest) -> syn::Result<Self> {
|
||||
let mut newtype_body_field = None;
|
||||
let mut query_map_field = None;
|
||||
let mut lifetimes = BTreeSet::new();
|
||||
|
||||
let fields = raw
|
||||
.fields
|
||||
@ -202,6 +210,8 @@ impl TryFrom<RawRequest> for Request {
|
||||
let mut field_kind = None;
|
||||
let mut header = None;
|
||||
|
||||
util::copy_lifetime_ident(&mut lifetimes, &field.ty);
|
||||
|
||||
for attr in mem::replace(&mut field.attrs, Vec::new()) {
|
||||
let meta = match Meta::from_attribute(&attr)? {
|
||||
Some(m) => m,
|
||||
@ -287,7 +297,7 @@ impl TryFrom<RawRequest> for Request {
|
||||
));
|
||||
}
|
||||
|
||||
Ok(Self { fields })
|
||||
Ok(Self { fields, lifetimes: lifetimes.into_iter().collect() })
|
||||
}
|
||||
}
|
||||
|
||||
@ -301,10 +311,12 @@ impl ToTokens for Request {
|
||||
quote! { { #(#fields),* } }
|
||||
};
|
||||
|
||||
let request_generics = util::generics_to_tokens(self.lifetimes.iter());
|
||||
|
||||
let request_body_struct =
|
||||
if let Some(body_field) = self.fields.iter().find(|f| f.is_newtype_body()) {
|
||||
let field = Field { ident: None, colon_token: None, ..body_field.field().clone() };
|
||||
let derive_deserialize = if body_field.has_wrap_incoming_attr() {
|
||||
let derive_deserialize = if util::has_lifetime(&body_field.field().ty) {
|
||||
TokenStream::new()
|
||||
} else {
|
||||
quote!(::ruma_api::exports::serde::Deserialize)
|
||||
@ -313,14 +325,18 @@ impl ToTokens for Request {
|
||||
Some((derive_deserialize, quote! { (#field); }))
|
||||
} else if self.has_body_fields() {
|
||||
let fields = self.fields.iter().filter(|f| f.is_body());
|
||||
let derive_deserialize = if fields.clone().any(|f| f.has_wrap_incoming_attr()) {
|
||||
TokenStream::new()
|
||||
let (derive_deserialize, lifetimes) =
|
||||
if fields.clone().any(|f| util::has_lifetime(&f.field().ty)) {
|
||||
(
|
||||
TokenStream::new(),
|
||||
util::collect_generic_idents(fields.clone().map(|f| &f.field().ty)),
|
||||
)
|
||||
} else {
|
||||
quote!(::ruma_api::exports::serde::Deserialize)
|
||||
(quote!(::ruma_api::exports::serde::Deserialize), TokenStream::new())
|
||||
};
|
||||
let fields = fields.map(RequestField::field);
|
||||
|
||||
Some((derive_deserialize, quote! { { #(#fields),* } }))
|
||||
Some((derive_deserialize, quote! { #lifetimes { #(#fields),* } }))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
@ -339,6 +355,7 @@ impl ToTokens for Request {
|
||||
|
||||
let request_query_struct = if let Some(f) = self.query_map_field() {
|
||||
let field = Field { ident: None, colon_token: None, ..f.clone() };
|
||||
let lifetime = util::collect_generic_idents(Some(&field.ty).into_iter());
|
||||
|
||||
quote! {
|
||||
/// Data in the request's query string.
|
||||
@ -347,10 +364,11 @@ impl ToTokens for Request {
|
||||
::ruma_api::exports::serde::Deserialize,
|
||||
::ruma_api::exports::serde::Serialize,
|
||||
)]
|
||||
struct RequestQuery(#field);
|
||||
struct RequestQuery #lifetime (#field);
|
||||
}
|
||||
} else if self.has_query_fields() {
|
||||
let fields = self.fields.iter().filter_map(RequestField::as_query_field);
|
||||
let lifetime = util::collect_generic_idents(fields.clone().map(|f| &f.ty));
|
||||
|
||||
quote! {
|
||||
/// Data in the request's query string.
|
||||
@ -359,7 +377,7 @@ impl ToTokens for Request {
|
||||
::ruma_api::exports::serde::Deserialize,
|
||||
::ruma_api::exports::serde::Serialize,
|
||||
)]
|
||||
struct RequestQuery {
|
||||
struct RequestQuery #lifetime {
|
||||
#(#fields),*
|
||||
}
|
||||
}
|
||||
@ -370,7 +388,7 @@ impl ToTokens for Request {
|
||||
let request = quote! {
|
||||
#[derive(Debug, Clone, ::ruma_api::Outgoing)]
|
||||
#[incoming_no_deserialize]
|
||||
pub struct Request #request_def
|
||||
pub struct Request #request_generics #request_def
|
||||
|
||||
#request_body_struct
|
||||
#request_query_struct
|
||||
@ -498,13 +516,6 @@ impl RequestField {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
/// Whether or not the request field has a #[wrap_incoming] attribute.
|
||||
fn has_wrap_incoming_attr(&self) -> bool {
|
||||
self.field().attrs.iter().any(|attr| {
|
||||
attr.path.segments.len() == 1 && attr.path.segments[0].ident == "wrap_incoming"
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// The types of fields that a request can have, without their values.
|
||||
|
@ -1,10 +1,10 @@
|
||||
//! Details of the `response` section of the procedural macro.
|
||||
|
||||
use std::{convert::TryFrom, mem};
|
||||
use std::{collections::BTreeSet, convert::TryFrom, mem};
|
||||
|
||||
use proc_macro2::TokenStream;
|
||||
use quote::{quote, quote_spanned, ToTokens};
|
||||
use syn::{spanned::Spanned, Field, Ident};
|
||||
use syn::{spanned::Spanned, Field, Ident, Lifetime};
|
||||
|
||||
use crate::{
|
||||
api::{
|
||||
@ -18,6 +18,9 @@ use crate::{
|
||||
pub struct Response {
|
||||
/// The fields of the response.
|
||||
fields: Vec<ResponseField>,
|
||||
|
||||
/// The collected lifetime identifiers from the declared fields.
|
||||
lifetimes: Vec<Lifetime>,
|
||||
}
|
||||
|
||||
impl Response {
|
||||
@ -31,9 +34,13 @@ impl Response {
|
||||
self.fields.iter().any(|field| field.is_header())
|
||||
}
|
||||
|
||||
/// Whether any field has a #[wrap_incoming] attribute.
|
||||
pub fn uses_wrap_incoming(&self) -> bool {
|
||||
self.fields.iter().any(|f| f.has_wrap_incoming_attr())
|
||||
/// Whether any field has a lifetime.
|
||||
pub fn contains_lifetimes(&self) -> bool {
|
||||
self.fields.iter().any(|f| util::has_lifetime(&f.field().ty))
|
||||
}
|
||||
|
||||
pub fn lifetimes(&self) -> impl Iterator<Item = &Lifetime> {
|
||||
self.lifetimes.iter()
|
||||
}
|
||||
|
||||
/// Produces code for a response struct initializer.
|
||||
@ -162,6 +169,7 @@ impl TryFrom<RawResponse> for Response {
|
||||
|
||||
fn try_from(raw: RawResponse) -> syn::Result<Self> {
|
||||
let mut newtype_body_field = None;
|
||||
let mut lifetimes = BTreeSet::new();
|
||||
|
||||
let fields = raw
|
||||
.fields
|
||||
@ -170,6 +178,8 @@ impl TryFrom<RawResponse> for Response {
|
||||
let mut field_kind = None;
|
||||
let mut header = None;
|
||||
|
||||
util::copy_lifetime_ident(&mut lifetimes, &field.ty);
|
||||
|
||||
for attr in mem::replace(&mut field.attrs, Vec::new()) {
|
||||
let meta = match Meta::from_attribute(&attr)? {
|
||||
Some(m) => m,
|
||||
@ -230,7 +240,7 @@ impl TryFrom<RawResponse> for Response {
|
||||
));
|
||||
}
|
||||
|
||||
Ok(Self { fields })
|
||||
Ok(Self { fields, lifetimes: lifetimes.into_iter().collect() })
|
||||
}
|
||||
}
|
||||
|
||||
@ -245,26 +255,36 @@ impl ToTokens for Response {
|
||||
quote! { { #(#fields),* } }
|
||||
};
|
||||
|
||||
let (derive_deserialize, def) =
|
||||
if let Some(body_field) = self.fields.iter().find(|f| f.is_newtype_body()) {
|
||||
let (derive_deserialize, def) = if let Some(body_field) =
|
||||
self.fields.iter().find(|f| f.is_newtype_body())
|
||||
{
|
||||
let field = Field { ident: None, colon_token: None, ..body_field.field().clone() };
|
||||
let derive_deserialize = if body_field.has_wrap_incoming_attr() {
|
||||
TokenStream::new()
|
||||
|
||||
let (derive_deserialize, lifetimes) = if util::has_lifetime(&body_field.field().ty) {
|
||||
(
|
||||
TokenStream::new(),
|
||||
util::collect_generic_idents(Some(&body_field.field().ty).into_iter()),
|
||||
)
|
||||
} else {
|
||||
quote!(::ruma_api::exports::serde::Deserialize)
|
||||
(quote!(::ruma_api::exports::serde::Deserialize), TokenStream::new())
|
||||
};
|
||||
|
||||
(derive_deserialize, quote! { (#field); })
|
||||
(derive_deserialize, quote! { #lifetimes (#field); })
|
||||
} else if self.has_body_fields() {
|
||||
let fields = self.fields.iter().filter(|f| f.is_body());
|
||||
let derive_deserialize = if fields.clone().any(|f| f.has_wrap_incoming_attr()) {
|
||||
TokenStream::new()
|
||||
let (derive_deserialize, lifetimes) =
|
||||
if fields.clone().any(|f| util::has_lifetime(&f.field().ty)) {
|
||||
(
|
||||
TokenStream::new(),
|
||||
util::collect_generic_idents(fields.clone().map(|f| &f.field().ty)),
|
||||
)
|
||||
} else {
|
||||
quote!(::ruma_api::exports::serde::Deserialize)
|
||||
(quote!(::ruma_api::exports::serde::Deserialize), TokenStream::new())
|
||||
};
|
||||
|
||||
let fields = fields.map(ResponseField::field);
|
||||
|
||||
(derive_deserialize, quote!({ #(#fields),* }))
|
||||
(derive_deserialize, quote!( #lifetimes { #(#fields),* }))
|
||||
} else {
|
||||
(TokenStream::new(), quote!({}))
|
||||
};
|
||||
@ -280,10 +300,11 @@ impl ToTokens for Response {
|
||||
struct ResponseBody #def
|
||||
};
|
||||
|
||||
let response_generics = util::generics_to_tokens(self.lifetimes.iter());
|
||||
let response = quote! {
|
||||
#[derive(Debug, Clone, ::ruma_api::Outgoing)]
|
||||
#[incoming_no_deserialize]
|
||||
pub struct Response #response_def
|
||||
pub struct Response #response_generics #response_def
|
||||
|
||||
#response_body_struct
|
||||
};
|
||||
@ -353,13 +374,6 @@ impl ResponseField {
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Whether or not the response field has a #[wrap_incoming] attribute.
|
||||
fn has_wrap_incoming_attr(&self) -> bool {
|
||||
self.field().attrs.iter().any(|attr| {
|
||||
attr.path.segments.len() == 1 && attr.path.segments[0].ident == "wrap_incoming"
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// The types of fields that a response can have, without their values.
|
||||
|
@ -54,24 +54,24 @@ ruma_api! {
|
||||
}
|
||||
}
|
||||
|
||||
// #[derive(Outgoing)]
|
||||
// #[incoming_no_deserialize]
|
||||
// pub struct Request<'a, T> {
|
||||
// pub abc: &'a str,
|
||||
// pub thing: Thing<'a, T>,
|
||||
// pub device_id: &'a ::ruma_identifiers::DeviceId,
|
||||
// pub user_id: &'a UserId,
|
||||
// pub bytes: &'a [u8],
|
||||
// pub recursive: &'a [Thing<'a, T>],
|
||||
// pub option: Option<&'a [u8]>,
|
||||
// }
|
||||
#[derive(Outgoing)]
|
||||
#[incoming_no_deserialize]
|
||||
pub struct FakeRequest<'a, T> {
|
||||
pub abc: &'a str,
|
||||
pub thing: Thing<'a, T>,
|
||||
pub device_id: &'a ::ruma_identifiers::DeviceId,
|
||||
pub user_id: &'a UserId,
|
||||
pub bytes: &'a [u8],
|
||||
pub recursive: &'a [Thing<'a, T>],
|
||||
pub option: Option<&'a [u8]>,
|
||||
}
|
||||
|
||||
// #[derive(Outgoing)]
|
||||
// #[incoming_no_deserialize]
|
||||
// pub enum EnumThing<'a, T> {
|
||||
// Abc(&'a str),
|
||||
// Stuff(Thing<'a, T>),
|
||||
// Boxy(&'a ::ruma_identifiers::DeviceId),
|
||||
// Other(Option<&'a str>),
|
||||
// StructVar { stuff: &'a str, more: &'a ::ruma_identifiers::ServerName },
|
||||
// }
|
||||
#[derive(Outgoing)]
|
||||
#[incoming_no_deserialize]
|
||||
pub enum EnumThing<'a, T> {
|
||||
Abc(&'a str),
|
||||
Stuff(Thing<'a, T>),
|
||||
Boxy(&'a ::ruma_identifiers::DeviceId),
|
||||
Other(Option<&'a str>),
|
||||
StructVar { stuff: &'a str, more: &'a ::ruma_identifiers::ServerName },
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user