Refactor to use Req/ResLifetimes struct for adding lifetimes to req/res
This commit is contained in:
parent
e7f7c3bb9d
commit
0793724264
@ -91,14 +91,6 @@ impl ToTokens for Api {
|
|||||||
let rate_limited = &self.metadata.rate_limited;
|
let rate_limited = &self.metadata.rate_limited;
|
||||||
let requires_authentication = &self.metadata.requires_authentication;
|
let requires_authentication = &self.metadata.requires_authentication;
|
||||||
|
|
||||||
let non_auth_endpoint_impl = if requires_authentication.value {
|
|
||||||
quote! {
|
|
||||||
impl ::ruma_api::NonAuthEndpoint for Request {}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
TokenStream::new()
|
|
||||||
};
|
|
||||||
|
|
||||||
let request_type = &self.request;
|
let request_type = &self.request;
|
||||||
let response_type = &self.response;
|
let response_type = &self.response;
|
||||||
|
|
||||||
@ -163,18 +155,27 @@ impl ToTokens for Api {
|
|||||||
TokenStream::new()
|
TokenStream::new()
|
||||||
};
|
};
|
||||||
|
|
||||||
let extract_request_body =
|
let extract_request_body = if self.request.has_body_fields()
|
||||||
if self.request.has_body_fields() || self.request.newtype_body_field().is_some() {
|
|| self.request.newtype_body_field().is_some()
|
||||||
quote! {
|
{
|
||||||
let request_body: <RequestBody as ::ruma_api::Outgoing>::Incoming =
|
let body_lifetimes = if self.request.has_body_lifetimes() {
|
||||||
::ruma_api::try_deserialize!(
|
// duplicate the anonymous lifetime as many times as needed
|
||||||
request,
|
let anon = quote! { '_, };
|
||||||
::ruma_api::exports::serde_json::from_slice(request.body().as_slice())
|
let lifetimes = vec![&anon].repeat(self.request.body_lifetime_count());
|
||||||
);
|
quote! { < #( #lifetimes )* >}
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
TokenStream::new()
|
TokenStream::new()
|
||||||
};
|
};
|
||||||
|
quote! {
|
||||||
|
let request_body: <RequestBody #body_lifetimes as ::ruma_api::Outgoing>::Incoming =
|
||||||
|
::ruma_api::try_deserialize!(
|
||||||
|
request,
|
||||||
|
::ruma_api::exports::serde_json::from_slice(request.body().as_slice())
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
TokenStream::new()
|
||||||
|
};
|
||||||
|
|
||||||
let parse_request_headers = if self.request.has_header_fields() {
|
let parse_request_headers = if self.request.has_header_fields() {
|
||||||
self.request.parse_headers_from_request()
|
self.request.parse_headers_from_request()
|
||||||
@ -194,19 +195,29 @@ impl ToTokens for Api {
|
|||||||
TokenStream::new()
|
TokenStream::new()
|
||||||
};
|
};
|
||||||
|
|
||||||
let typed_response_body_decl =
|
let typed_response_body_decl = if self.response.has_body_fields()
|
||||||
if self.response.has_body_fields() || self.response.newtype_body_field().is_some() {
|
|| self.response.newtype_body_field().is_some()
|
||||||
quote! {
|
{
|
||||||
let response_body: <ResponseBody as ::ruma_api::Outgoing>::Incoming =
|
let body_lifetimes = if self.response.has_body_lifetimes() {
|
||||||
::ruma_api::try_deserialize!(
|
// duplicate the anonymous lifetime as many times as needed
|
||||||
response,
|
let anon = quote! { '_, };
|
||||||
::ruma_api::exports::serde_json::from_slice(response.body().as_slice()),
|
let lifetimes = vec![&anon].repeat(self.response.body_lifetime_count());
|
||||||
);
|
quote! { < #( #lifetimes )* >}
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
TokenStream::new()
|
TokenStream::new()
|
||||||
};
|
};
|
||||||
|
|
||||||
|
quote! {
|
||||||
|
let response_body: <ResponseBody #body_lifetimes as ::ruma_api::Outgoing>::Incoming =
|
||||||
|
::ruma_api::try_deserialize!(
|
||||||
|
response,
|
||||||
|
::ruma_api::exports::serde_json::from_slice(response.body().as_slice()),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
TokenStream::new()
|
||||||
|
};
|
||||||
|
|
||||||
let response_init_fields = self.response.init_fields();
|
let response_init_fields = self.response.init_fields();
|
||||||
|
|
||||||
let serialize_response_headers = self.response.apply_header_fields();
|
let serialize_response_headers = self.response.apply_header_fields();
|
||||||
@ -222,28 +233,24 @@ impl ToTokens for Api {
|
|||||||
|
|
||||||
let error = &self.error;
|
let error = &self.error;
|
||||||
|
|
||||||
let res_life = self.response.lifetimes().collect::<Vec<_>>();
|
let response_lifetimes = self.response.combine_lifetimes();
|
||||||
let req_life = self.request.lifetimes().collect::<Vec<_>>();
|
let request_lifetimes = self.request.combine_lifetimes();
|
||||||
|
|
||||||
let response_lifetimes = util::generics_to_tokens(res_life.iter().cloned());
|
let non_auth_endpoint_impl = if requires_authentication.value {
|
||||||
let request_lifetimes = util::generics_to_tokens(req_life.iter().cloned());
|
quote! {
|
||||||
|
impl #request_lifetimes ::ruma_api::NonAuthEndpoint for Request #request_lifetimes {}
|
||||||
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 {
|
} else {
|
||||||
request_lifetimes.clone()
|
TokenStream::new()
|
||||||
};
|
};
|
||||||
|
|
||||||
let api = quote! {
|
let api = quote! {
|
||||||
// FIXME: These can't conflict with other imports, but it would still be nice not to
|
// 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.
|
// bring anything into scope that code outside the macro could then rely on.
|
||||||
use ::std::convert::TryInto as _;
|
// use ::std::convert::TryInto as _;
|
||||||
|
|
||||||
use ::ruma_api::exports::serde::de::Error as _;
|
use ::ruma_api::exports::serde::de::Error as _;
|
||||||
use ::ruma_api::exports::serde::Deserialize as _;
|
// use ::ruma_api::exports::serde::Deserialize as _;
|
||||||
use ::ruma_api::Endpoint as _;
|
use ::ruma_api::Endpoint as _;
|
||||||
|
|
||||||
#[doc = #request_doc]
|
#[doc = #request_doc]
|
||||||
@ -319,7 +326,7 @@ impl ToTokens for Api {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl #endpoint_impl_lifetimes ::ruma_api::Endpoint for Request #request_lifetimes {
|
impl #request_lifetimes ::ruma_api::Endpoint for Request #request_lifetimes {
|
||||||
type Response = Response #response_lifetimes;
|
type Response = Response #response_lifetimes;
|
||||||
type ResponseError = #error;
|
type ResponseError = #error;
|
||||||
|
|
||||||
|
@ -14,13 +14,21 @@ use crate::{
|
|||||||
util,
|
util,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#[derive(Debug, Default)]
|
||||||
|
pub struct RequetLifetimes {
|
||||||
|
body: BTreeSet<Lifetime>,
|
||||||
|
path: BTreeSet<Lifetime>,
|
||||||
|
query: BTreeSet<Lifetime>,
|
||||||
|
header: BTreeSet<Lifetime>,
|
||||||
|
}
|
||||||
|
|
||||||
/// The result of processing the `request` section of the macro.
|
/// The result of processing the `request` section of the macro.
|
||||||
pub struct Request {
|
pub struct Request {
|
||||||
/// The fields of the request.
|
/// The fields of the request.
|
||||||
fields: Vec<RequestField>,
|
fields: Vec<RequestField>,
|
||||||
|
|
||||||
/// The collected lifetime identifiers from the declared fields.
|
/// The collected lifetime identifiers from the declared fields.
|
||||||
lifetimes: Vec<Lifetime>,
|
lifetimes: RequetLifetimes,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Request {
|
impl Request {
|
||||||
@ -101,15 +109,58 @@ impl Request {
|
|||||||
self.fields.iter().filter_map(|field| field.as_body_field())
|
self.fields.iter().filter_map(|field| field.as_body_field())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Whether any field has a lifetime.
|
/// The number of unique lifetime annotations for `body` fields.
|
||||||
pub fn contains_lifetimes(&self) -> bool {
|
pub fn body_lifetime_count(&self) -> usize {
|
||||||
self.fields.iter().any(|f| util::has_lifetime(&f.field().ty))
|
self.lifetimes.body.len()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn lifetimes(&self) -> impl Iterator<Item = &Lifetime> {
|
/// Whether any `body` field has a lifetime annotation.
|
||||||
self.lifetimes.iter()
|
pub fn has_body_lifetimes(&self) -> bool {
|
||||||
|
!self.lifetimes.body.is_empty()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Whether any `query` field has a lifetime annotation.
|
||||||
|
pub fn has_query_lifetimes(&self) -> bool {
|
||||||
|
!self.lifetimes.query.is_empty()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Whether any field has a lifetime.
|
||||||
|
pub fn contains_lifetimes(&self) -> bool {
|
||||||
|
!(self.lifetimes.body.is_empty()
|
||||||
|
&& self.lifetimes.path.is_empty()
|
||||||
|
&& self.lifetimes.query.is_empty()
|
||||||
|
&& self.lifetimes.header.is_empty())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The combination of every fields unique lifetime annotation.
|
||||||
|
pub fn combine_lifetimes(&self) -> TokenStream {
|
||||||
|
util::generics_to_tokens(
|
||||||
|
self.lifetimes
|
||||||
|
.body
|
||||||
|
.iter()
|
||||||
|
.chain(self.lifetimes.path.iter())
|
||||||
|
.chain(self.lifetimes.query.iter())
|
||||||
|
.chain(self.lifetimes.header.iter())
|
||||||
|
.collect::<BTreeSet<_>>()
|
||||||
|
.into_iter(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The lifetimes on fields with the `query` attribute.
|
||||||
|
pub fn query_lifetimes(&self) -> TokenStream {
|
||||||
|
util::generics_to_tokens(self.lifetimes.query.iter())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The lifetimes on fields with the `body` attribute.
|
||||||
|
pub fn body_lifetimes(&self) -> TokenStream {
|
||||||
|
util::generics_to_tokens(self.lifetimes.body.iter())
|
||||||
|
}
|
||||||
|
|
||||||
|
// /// The lifetimes on fields with the `header` attribute.
|
||||||
|
// pub fn header_lifetimes(&self) -> TokenStream {
|
||||||
|
// util::generics_to_tokens(self.lifetimes.header.iter())
|
||||||
|
// }
|
||||||
|
|
||||||
/// Produces an iterator over all the header fields.
|
/// Produces an iterator over all the header fields.
|
||||||
pub fn header_fields(&self) -> impl Iterator<Item = &RequestField> {
|
pub fn header_fields(&self) -> impl Iterator<Item = &RequestField> {
|
||||||
self.fields.iter().filter(|field| field.is_header())
|
self.fields.iter().filter(|field| field.is_header())
|
||||||
@ -201,7 +252,7 @@ impl TryFrom<RawRequest> for Request {
|
|||||||
fn try_from(raw: RawRequest) -> syn::Result<Self> {
|
fn try_from(raw: RawRequest) -> syn::Result<Self> {
|
||||||
let mut newtype_body_field = None;
|
let mut newtype_body_field = None;
|
||||||
let mut query_map_field = None;
|
let mut query_map_field = None;
|
||||||
let mut lifetimes = BTreeSet::new();
|
let mut lifetimes = RequetLifetimes::default();
|
||||||
|
|
||||||
let fields = raw
|
let fields = raw
|
||||||
.fields
|
.fields
|
||||||
@ -210,8 +261,6 @@ impl TryFrom<RawRequest> for Request {
|
|||||||
let mut field_kind = None;
|
let mut field_kind = None;
|
||||||
let mut header = None;
|
let mut header = None;
|
||||||
|
|
||||||
util::copy_lifetime_ident(&mut lifetimes, &field.ty);
|
|
||||||
|
|
||||||
for attr in mem::replace(&mut field.attrs, Vec::new()) {
|
for attr in mem::replace(&mut field.attrs, Vec::new()) {
|
||||||
let meta = match Meta::from_attribute(&attr)? {
|
let meta = match Meta::from_attribute(&attr)? {
|
||||||
Some(m) => m,
|
Some(m) => m,
|
||||||
@ -273,6 +322,16 @@ impl TryFrom<RawRequest> for Request {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
match field_kind.unwrap_or(RequestFieldKind::Body) {
|
||||||
|
RequestFieldKind::Header => util::copy_lifetime_ident(&mut lifetimes.header, &field.ty),
|
||||||
|
RequestFieldKind::Body => util::copy_lifetime_ident(&mut lifetimes.body, &field.ty),
|
||||||
|
RequestFieldKind::NewtypeBody => util::copy_lifetime_ident(&mut lifetimes.body, &field.ty),
|
||||||
|
RequestFieldKind::NewtypeRawBody => util::copy_lifetime_ident(&mut lifetimes.body, &field.ty),
|
||||||
|
RequestFieldKind::Path => util::copy_lifetime_ident(&mut lifetimes.path, &field.ty),
|
||||||
|
RequestFieldKind::Query => util::copy_lifetime_ident(&mut lifetimes.query, &field.ty),
|
||||||
|
RequestFieldKind::QueryMap => util::copy_lifetime_ident(&mut lifetimes.query, &field.ty),
|
||||||
|
}
|
||||||
|
|
||||||
Ok(RequestField::new(
|
Ok(RequestField::new(
|
||||||
field_kind.unwrap_or(RequestFieldKind::Body),
|
field_kind.unwrap_or(RequestFieldKind::Body),
|
||||||
field,
|
field,
|
||||||
@ -297,7 +356,7 @@ impl TryFrom<RawRequest> for Request {
|
|||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(Self { fields, lifetimes: lifetimes.into_iter().collect() })
|
Ok(Self { fields, lifetimes })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -311,29 +370,29 @@ impl ToTokens for Request {
|
|||||||
quote! { { #(#fields),* } }
|
quote! { { #(#fields),* } }
|
||||||
};
|
};
|
||||||
|
|
||||||
let request_generics = util::generics_to_tokens(self.lifetimes.iter());
|
let request_generics = self.combine_lifetimes();
|
||||||
|
|
||||||
let request_body_struct =
|
let request_body_struct =
|
||||||
if let Some(body_field) = self.fields.iter().find(|f| f.is_newtype_body()) {
|
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 field = Field { ident: None, colon_token: None, ..body_field.field().clone() };
|
||||||
let derive_deserialize = if util::has_lifetime(&body_field.field().ty) {
|
// Though we don't track the difference between new type body and body
|
||||||
TokenStream::new()
|
// for lifetimes, the outer check and the macro failing if it encounters
|
||||||
|
// an illegal combination of field attributes, is enough to guarantee `body_lifetimes`
|
||||||
|
// correctness.
|
||||||
|
let (derive_deserialize, lifetimes) = if self.has_body_lifetimes() {
|
||||||
|
(TokenStream::new(), self.body_lifetimes())
|
||||||
} else {
|
} else {
|
||||||
quote!(::ruma_api::exports::serde::Deserialize)
|
(quote!(::ruma_api::exports::serde::Deserialize), TokenStream::new())
|
||||||
};
|
};
|
||||||
|
|
||||||
Some((derive_deserialize, quote! { (#field); }))
|
Some((derive_deserialize, quote! { #lifetimes (#field); }))
|
||||||
} else if self.has_body_fields() {
|
} else if self.has_body_fields() {
|
||||||
let fields = self.fields.iter().filter(|f| f.is_body());
|
let fields = self.fields.iter().filter(|f| f.is_body());
|
||||||
let (derive_deserialize, lifetimes) =
|
let (derive_deserialize, lifetimes) = if self.has_body_lifetimes() {
|
||||||
if fields.clone().any(|f| util::has_lifetime(&f.field().ty)) {
|
(TokenStream::new(), self.body_lifetimes())
|
||||||
(
|
} else {
|
||||||
TokenStream::new(),
|
(quote!(::ruma_api::exports::serde::Deserialize), TokenStream::new())
|
||||||
util::collect_generic_idents(fields.clone().map(|f| &f.field().ty)),
|
};
|
||||||
)
|
|
||||||
} else {
|
|
||||||
(quote!(::ruma_api::exports::serde::Deserialize), TokenStream::new())
|
|
||||||
};
|
|
||||||
let fields = fields.map(RequestField::field);
|
let fields = fields.map(RequestField::field);
|
||||||
|
|
||||||
Some((derive_deserialize, quote! { #lifetimes { #(#fields),* } }))
|
Some((derive_deserialize, quote! { #lifetimes { #(#fields),* } }))
|
||||||
@ -355,12 +414,13 @@ impl ToTokens for Request {
|
|||||||
|
|
||||||
let request_query_struct = if let Some(f) = self.query_map_field() {
|
let request_query_struct = if let Some(f) = self.query_map_field() {
|
||||||
let field = Field { ident: None, colon_token: None, ..f.clone() };
|
let field = Field { ident: None, colon_token: None, ..f.clone() };
|
||||||
let lifetime = util::collect_generic_idents(Some(&field.ty).into_iter());
|
let lifetime = self.query_lifetimes();
|
||||||
|
|
||||||
quote! {
|
quote! {
|
||||||
/// Data in the request's query string.
|
/// Data in the request's query string.
|
||||||
#[derive(
|
#[derive(
|
||||||
Debug,
|
Debug,
|
||||||
|
::ruma_api::Outgoing,
|
||||||
::ruma_api::exports::serde::Deserialize,
|
::ruma_api::exports::serde::Deserialize,
|
||||||
::ruma_api::exports::serde::Serialize,
|
::ruma_api::exports::serde::Serialize,
|
||||||
)]
|
)]
|
||||||
@ -368,12 +428,13 @@ impl ToTokens for Request {
|
|||||||
}
|
}
|
||||||
} else if self.has_query_fields() {
|
} else if self.has_query_fields() {
|
||||||
let fields = self.fields.iter().filter_map(RequestField::as_query_field);
|
let fields = self.fields.iter().filter_map(RequestField::as_query_field);
|
||||||
let lifetime = util::collect_generic_idents(fields.clone().map(|f| &f.ty));
|
let lifetime = self.query_lifetimes();
|
||||||
|
|
||||||
quote! {
|
quote! {
|
||||||
/// Data in the request's query string.
|
/// Data in the request's query string.
|
||||||
#[derive(
|
#[derive(
|
||||||
Debug,
|
Debug,
|
||||||
|
::ruma_api::Outgoing,
|
||||||
::ruma_api::exports::serde::Deserialize,
|
::ruma_api::exports::serde::Deserialize,
|
||||||
::ruma_api::exports::serde::Serialize,
|
::ruma_api::exports::serde::Serialize,
|
||||||
)]
|
)]
|
||||||
|
@ -14,13 +14,19 @@ use crate::{
|
|||||||
util,
|
util,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#[derive(Debug, Default)]
|
||||||
|
pub struct ResponseLifetimes {
|
||||||
|
body: BTreeSet<Lifetime>,
|
||||||
|
header: BTreeSet<Lifetime>,
|
||||||
|
}
|
||||||
|
|
||||||
/// The result of processing the `response` section of the macro.
|
/// The result of processing the `response` section of the macro.
|
||||||
pub struct Response {
|
pub struct Response {
|
||||||
/// The fields of the response.
|
/// The fields of the response.
|
||||||
fields: Vec<ResponseField>,
|
fields: Vec<ResponseField>,
|
||||||
|
|
||||||
/// The collected lifetime identifiers from the declared fields.
|
/// The collected lifetime identifiers from the declared fields.
|
||||||
lifetimes: Vec<Lifetime>,
|
lifetimes: ResponseLifetimes,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Response {
|
impl Response {
|
||||||
@ -34,13 +40,34 @@ impl Response {
|
|||||||
self.fields.iter().any(|field| field.is_header())
|
self.fields.iter().any(|field| field.is_header())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Whether any field has a lifetime.
|
/// Whether any `body` field has a lifetime annotation.
|
||||||
pub fn contains_lifetimes(&self) -> bool {
|
pub fn has_body_lifetimes(&self) -> bool {
|
||||||
self.fields.iter().any(|f| util::has_lifetime(&f.field().ty))
|
!self.lifetimes.body.is_empty()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn lifetimes(&self) -> impl Iterator<Item = &Lifetime> {
|
/// The number of unique lifetime annotations for `body` fields.
|
||||||
self.lifetimes.iter()
|
pub fn body_lifetime_count(&self) -> usize {
|
||||||
|
self.lifetimes.body.len()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Whether any field has a lifetime annotation.
|
||||||
|
pub fn contains_lifetimes(&self) -> bool {
|
||||||
|
!(self.lifetimes.body.is_empty() && self.lifetimes.header.is_empty())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn combine_lifetimes(&self) -> TokenStream {
|
||||||
|
util::generics_to_tokens(
|
||||||
|
self.lifetimes
|
||||||
|
.body
|
||||||
|
.iter()
|
||||||
|
.chain(self.lifetimes.header.iter())
|
||||||
|
.collect::<BTreeSet<_>>()
|
||||||
|
.into_iter(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn body_lifetimes(&self) -> TokenStream {
|
||||||
|
util::generics_to_tokens(self.lifetimes.body.iter())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Produces code for a response struct initializer.
|
/// Produces code for a response struct initializer.
|
||||||
@ -169,7 +196,7 @@ impl TryFrom<RawResponse> for Response {
|
|||||||
|
|
||||||
fn try_from(raw: RawResponse) -> syn::Result<Self> {
|
fn try_from(raw: RawResponse) -> syn::Result<Self> {
|
||||||
let mut newtype_body_field = None;
|
let mut newtype_body_field = None;
|
||||||
let mut lifetimes = BTreeSet::new();
|
let mut lifetimes = ResponseLifetimes::default();
|
||||||
|
|
||||||
let fields = raw
|
let fields = raw
|
||||||
.fields
|
.fields
|
||||||
@ -178,8 +205,6 @@ impl TryFrom<RawResponse> for Response {
|
|||||||
let mut field_kind = None;
|
let mut field_kind = None;
|
||||||
let mut header = None;
|
let mut header = None;
|
||||||
|
|
||||||
util::copy_lifetime_ident(&mut lifetimes, &field.ty);
|
|
||||||
|
|
||||||
for attr in mem::replace(&mut field.attrs, Vec::new()) {
|
for attr in mem::replace(&mut field.attrs, Vec::new()) {
|
||||||
let meta = match Meta::from_attribute(&attr)? {
|
let meta = match Meta::from_attribute(&attr)? {
|
||||||
Some(m) => m,
|
Some(m) => m,
|
||||||
@ -222,12 +247,22 @@ impl TryFrom<RawResponse> for Response {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Ok(match field_kind.unwrap_or(ResponseFieldKind::Body) {
|
Ok(match field_kind.unwrap_or(ResponseFieldKind::Body) {
|
||||||
ResponseFieldKind::Body => ResponseField::Body(field),
|
ResponseFieldKind::Body => {
|
||||||
|
util::copy_lifetime_ident(&mut lifetimes.body, &field.ty);
|
||||||
|
ResponseField::Body(field)
|
||||||
|
}
|
||||||
ResponseFieldKind::Header => {
|
ResponseFieldKind::Header => {
|
||||||
|
util::copy_lifetime_ident(&mut lifetimes.header, &field.ty);
|
||||||
ResponseField::Header(field, header.expect("missing header name"))
|
ResponseField::Header(field, header.expect("missing header name"))
|
||||||
}
|
}
|
||||||
ResponseFieldKind::NewtypeBody => ResponseField::NewtypeBody(field),
|
ResponseFieldKind::NewtypeBody => {
|
||||||
ResponseFieldKind::NewtypeRawBody => ResponseField::NewtypeRawBody(field),
|
util::copy_lifetime_ident(&mut lifetimes.body, &field.ty);
|
||||||
|
ResponseField::NewtypeBody(field)
|
||||||
|
}
|
||||||
|
ResponseFieldKind::NewtypeRawBody => {
|
||||||
|
util::copy_lifetime_ident(&mut lifetimes.body, &field.ty);
|
||||||
|
ResponseField::NewtypeRawBody(field)
|
||||||
|
}
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
.collect::<syn::Result<Vec<_>>>()?;
|
.collect::<syn::Result<Vec<_>>>()?;
|
||||||
@ -240,7 +275,7 @@ impl TryFrom<RawResponse> for Response {
|
|||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(Self { fields, lifetimes: lifetimes.into_iter().collect() })
|
Ok(Self { fields, lifetimes })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -255,39 +290,35 @@ impl ToTokens for Response {
|
|||||||
quote! { { #(#fields),* } }
|
quote! { { #(#fields),* } }
|
||||||
};
|
};
|
||||||
|
|
||||||
let (derive_deserialize, def) = if let Some(body_field) =
|
let (derive_deserialize, def) =
|
||||||
self.fields.iter().find(|f| f.is_newtype_body())
|
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 field = Field { ident: None, colon_token: None, ..body_field.field().clone() };
|
|
||||||
|
|
||||||
let (derive_deserialize, lifetimes) = if util::has_lifetime(&body_field.field().ty) {
|
// Though we don't track the difference between new type body and body
|
||||||
(
|
// for lifetimes, the outer check and the macro failing if it encounters
|
||||||
TokenStream::new(),
|
// an illegal combination of field attributes, is enough to guarantee `body_lifetimes`
|
||||||
util::collect_generic_idents(Some(&body_field.field().ty).into_iter()),
|
// correctness.
|
||||||
)
|
let (derive_deserialize, lifetimes) = if self.has_body_lifetimes() {
|
||||||
} else {
|
(TokenStream::new(), self.body_lifetimes())
|
||||||
(quote!(::ruma_api::exports::serde::Deserialize), TokenStream::new())
|
|
||||||
};
|
|
||||||
|
|
||||||
(derive_deserialize, quote! { #lifetimes (#field); })
|
|
||||||
} else if self.has_body_fields() {
|
|
||||||
let fields = self.fields.iter().filter(|f| f.is_body());
|
|
||||||
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 {
|
} else {
|
||||||
(quote!(::ruma_api::exports::serde::Deserialize), TokenStream::new())
|
(quote!(::ruma_api::exports::serde::Deserialize), TokenStream::new())
|
||||||
};
|
};
|
||||||
|
|
||||||
let fields = fields.map(ResponseField::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, lifetimes) = if self.has_body_lifetimes() {
|
||||||
|
(TokenStream::new(), self.body_lifetimes())
|
||||||
|
} else {
|
||||||
|
(quote!(::ruma_api::exports::serde::Deserialize), TokenStream::new())
|
||||||
|
};
|
||||||
|
|
||||||
(derive_deserialize, quote!( #lifetimes { #(#fields),* }))
|
let fields = fields.map(ResponseField::field);
|
||||||
} else {
|
|
||||||
(TokenStream::new(), quote!({}))
|
(derive_deserialize, quote!( #lifetimes { #(#fields),* }))
|
||||||
};
|
} else {
|
||||||
|
(TokenStream::new(), quote!({}))
|
||||||
|
};
|
||||||
|
|
||||||
let response_body_struct = quote! {
|
let response_body_struct = quote! {
|
||||||
/// Data in the response body.
|
/// Data in the response body.
|
||||||
@ -300,7 +331,7 @@ impl ToTokens for Response {
|
|||||||
struct ResponseBody #def
|
struct ResponseBody #def
|
||||||
};
|
};
|
||||||
|
|
||||||
let response_generics = util::generics_to_tokens(self.lifetimes.iter());
|
let response_generics = self.combine_lifetimes();
|
||||||
let response = quote! {
|
let response = quote! {
|
||||||
#[derive(Debug, Clone, ::ruma_api::Outgoing)]
|
#[derive(Debug, Clone, ::ruma_api::Outgoing)]
|
||||||
#[incoming_no_deserialize]
|
#[incoming_no_deserialize]
|
||||||
|
@ -10,35 +10,6 @@ use syn::{
|
|||||||
|
|
||||||
use crate::api::{metadata::Metadata, request::Request};
|
use crate::api::{metadata::Metadata, request::Request};
|
||||||
|
|
||||||
/// Whether or not the request field has a lifetime.
|
|
||||||
pub fn has_lifetime(ty: &Type) -> bool {
|
|
||||||
let mut found_lifetime = false;
|
|
||||||
if let Type::Path(TypePath { path, .. }) = ty {
|
|
||||||
for seg in &path.segments {
|
|
||||||
#[allow(clippy::blocks_in_if_conditions)] // TODO
|
|
||||||
if match &seg.arguments {
|
|
||||||
PathArguments::AngleBracketed(AngleBracketedGenericArguments { args, .. }) => {
|
|
||||||
args.clone().iter().any(|gen| {
|
|
||||||
if let GenericArgument::Type(ty) = gen {
|
|
||||||
has_lifetime(ty)
|
|
||||||
} else {
|
|
||||||
matches!(gen, GenericArgument::Lifetime(_))
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
PathArguments::Parenthesized(ParenthesizedGenericArguments { inputs, .. }) => {
|
|
||||||
inputs.iter().any(|ty| has_lifetime(ty))
|
|
||||||
}
|
|
||||||
_ => false,
|
|
||||||
} {
|
|
||||||
found_lifetime = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
matches!(ty, Type::Reference(_) | Type::Slice(_)) || found_lifetime
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn copy_lifetime_ident(lifetimes: &mut BTreeSet<Lifetime>, ty: &Type) {
|
pub fn copy_lifetime_ident(lifetimes: &mut BTreeSet<Lifetime>, ty: &Type) {
|
||||||
match ty {
|
match ty {
|
||||||
Type::Path(TypePath { path, .. }) => {
|
Type::Path(TypePath { path, .. }) => {
|
||||||
@ -182,7 +153,6 @@ pub(crate) fn request_path_string_and_parse(
|
|||||||
pub(crate) fn build_query_string(request: &Request) -> TokenStream {
|
pub(crate) fn build_query_string(request: &Request) -> TokenStream {
|
||||||
if let Some(field) = request.query_map_field() {
|
if let Some(field) = request.query_map_field() {
|
||||||
let field_name = field.ident.as_ref().expect("expected field to have identifier");
|
let field_name = field.ident.as_ref().expect("expected field to have identifier");
|
||||||
let field_type = &field.ty;
|
|
||||||
|
|
||||||
quote!({
|
quote!({
|
||||||
// This function exists so that the compiler will throw an
|
// This function exists so that the compiler will throw an
|
||||||
@ -195,13 +165,14 @@ pub(crate) fn build_query_string(request: &Request) -> TokenStream {
|
|||||||
//
|
//
|
||||||
// By asserting that it implements the iterator trait, we can
|
// By asserting that it implements the iterator trait, we can
|
||||||
// ensure that it won't fail.
|
// ensure that it won't fail.
|
||||||
fn assert_trait_impl<T>()
|
fn assert_trait_impl<T>(_: &T)
|
||||||
where
|
where
|
||||||
T: ::std::iter::IntoIterator<Item = (::std::string::String, ::std::string::String)>,
|
T: ::std::iter::IntoIterator<Item = (::std::string::String, ::std::string::String)>,
|
||||||
{}
|
{}
|
||||||
assert_trait_impl::<#field_type>();
|
|
||||||
|
|
||||||
let request_query = RequestQuery(self.#field_name);
|
let request_query = RequestQuery(self.#field_name);
|
||||||
|
assert_trait_impl(&request_query.0);
|
||||||
|
|
||||||
format_args!(
|
format_args!(
|
||||||
"?{}",
|
"?{}",
|
||||||
::ruma_api::exports::ruma_serde::urlencoded::to_string(request_query)?
|
::ruma_api::exports::ruma_serde::urlencoded::to_string(request_query)?
|
||||||
@ -237,8 +208,13 @@ pub(crate) fn extract_request_query(request: &Request) -> TokenStream {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
} else if request.has_query_fields() {
|
} else if request.has_query_fields() {
|
||||||
|
let request_query_type = if request.has_query_lifetimes() {
|
||||||
|
quote! { IncomingRequestQuery }
|
||||||
|
} else {
|
||||||
|
quote! { RequestQuery }
|
||||||
|
};
|
||||||
quote! {
|
quote! {
|
||||||
let request_query: RequestQuery = ::ruma_api::try_deserialize!(
|
let request_query: #request_query_type = ::ruma_api::try_deserialize!(
|
||||||
request,
|
request,
|
||||||
::ruma_api::exports::ruma_serde::urlencoded::from_str(
|
::ruma_api::exports::ruma_serde::urlencoded::from_str(
|
||||||
&request.uri().query().unwrap_or("")
|
&request.uri().query().unwrap_or("")
|
||||||
|
Loading…
x
Reference in New Issue
Block a user