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 request_type = &self.request;
|
||||||
let response_type = &self.response;
|
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)
|
quote!(IncomingRequest)
|
||||||
} else {
|
} else {
|
||||||
quote!(Request)
|
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)
|
quote!(IncomingResponse)
|
||||||
} else {
|
} else {
|
||||||
quote!(Response)
|
quote!(Response)
|
||||||
@ -222,6 +222,21 @@ impl ToTokens for Api {
|
|||||||
|
|
||||||
let error = &self.error;
|
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! {
|
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.
|
||||||
@ -260,13 +275,13 @@ impl ToTokens for Api {
|
|||||||
#[doc = #response_doc]
|
#[doc = #response_doc]
|
||||||
#response_type
|
#response_type
|
||||||
|
|
||||||
impl ::std::convert::TryFrom<Response>
|
impl #response_lifetimes ::std::convert::TryFrom<Response #response_lifetimes>
|
||||||
for ::ruma_api::exports::http::Response<Vec<u8>>
|
for ::ruma_api::exports::http::Response<Vec<u8>>
|
||||||
{
|
{
|
||||||
type Error = ::ruma_api::error::IntoHttpError;
|
type Error = ::ruma_api::error::IntoHttpError;
|
||||||
|
|
||||||
#[allow(unused_variables)]
|
#[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()
|
let response = ::ruma_api::exports::http::Response::builder()
|
||||||
.header(::ruma_api::exports::http::header::CONTENT_TYPE, "application/json")
|
.header(::ruma_api::exports::http::header::CONTENT_TYPE, "application/json")
|
||||||
#serialize_response_headers
|
#serialize_response_headers
|
||||||
@ -304,8 +319,8 @@ impl ToTokens for Api {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ::ruma_api::Endpoint for Request {
|
impl #endpoint_impl_lifetimes ::ruma_api::Endpoint for Request #request_lifetimes {
|
||||||
type Response = Response;
|
type Response = Response #response_lifetimes;
|
||||||
type ResponseError = #error;
|
type ResponseError = #error;
|
||||||
|
|
||||||
/// Metadata for the `#name` endpoint.
|
/// Metadata for the `#name` endpoint.
|
||||||
|
@ -1,10 +1,10 @@
|
|||||||
//! Details of the `request` section of the procedural macro.
|
//! 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 proc_macro2::TokenStream;
|
||||||
use quote::{quote, quote_spanned, ToTokens};
|
use quote::{quote, quote_spanned, ToTokens};
|
||||||
use syn::{spanned::Spanned, Field, Ident};
|
use syn::{spanned::Spanned, Field, Ident, Lifetime};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
api::{
|
api::{
|
||||||
@ -18,6 +18,9 @@ use crate::{
|
|||||||
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.
|
||||||
|
lifetimes: Vec<Lifetime>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Request {
|
impl Request {
|
||||||
@ -98,9 +101,13 @@ 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 #[wrap_incoming] attribute.
|
/// Whether any field has a lifetime.
|
||||||
pub fn uses_wrap_incoming(&self) -> bool {
|
pub fn contains_lifetimes(&self) -> bool {
|
||||||
self.fields.iter().any(|f| f.has_wrap_incoming_attr())
|
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.
|
/// 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> {
|
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 fields = raw
|
let fields = raw
|
||||||
.fields
|
.fields
|
||||||
@ -202,6 +210,8 @@ 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,
|
||||||
@ -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),* } }
|
quote! { { #(#fields),* } }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let request_generics = util::generics_to_tokens(self.lifetimes.iter());
|
||||||
|
|
||||||
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 body_field.has_wrap_incoming_attr() {
|
let derive_deserialize = if util::has_lifetime(&body_field.field().ty) {
|
||||||
TokenStream::new()
|
TokenStream::new()
|
||||||
} else {
|
} else {
|
||||||
quote!(::ruma_api::exports::serde::Deserialize)
|
quote!(::ruma_api::exports::serde::Deserialize)
|
||||||
@ -313,14 +325,18 @@ impl ToTokens for Request {
|
|||||||
Some((derive_deserialize, quote! { (#field); }))
|
Some((derive_deserialize, quote! { (#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 = if fields.clone().any(|f| f.has_wrap_incoming_attr()) {
|
let (derive_deserialize, lifetimes) =
|
||||||
TokenStream::new()
|
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)
|
(quote!(::ruma_api::exports::serde::Deserialize), TokenStream::new())
|
||||||
};
|
};
|
||||||
let fields = fields.map(RequestField::field);
|
let fields = fields.map(RequestField::field);
|
||||||
|
|
||||||
Some((derive_deserialize, quote! { { #(#fields),* } }))
|
Some((derive_deserialize, quote! { #lifetimes { #(#fields),* } }))
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
@ -339,6 +355,7 @@ 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());
|
||||||
|
|
||||||
quote! {
|
quote! {
|
||||||
/// Data in the request's query string.
|
/// Data in the request's query string.
|
||||||
@ -347,10 +364,11 @@ impl ToTokens for Request {
|
|||||||
::ruma_api::exports::serde::Deserialize,
|
::ruma_api::exports::serde::Deserialize,
|
||||||
::ruma_api::exports::serde::Serialize,
|
::ruma_api::exports::serde::Serialize,
|
||||||
)]
|
)]
|
||||||
struct RequestQuery(#field);
|
struct RequestQuery #lifetime (#field);
|
||||||
}
|
}
|
||||||
} 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));
|
||||||
|
|
||||||
quote! {
|
quote! {
|
||||||
/// Data in the request's query string.
|
/// Data in the request's query string.
|
||||||
@ -359,7 +377,7 @@ impl ToTokens for Request {
|
|||||||
::ruma_api::exports::serde::Deserialize,
|
::ruma_api::exports::serde::Deserialize,
|
||||||
::ruma_api::exports::serde::Serialize,
|
::ruma_api::exports::serde::Serialize,
|
||||||
)]
|
)]
|
||||||
struct RequestQuery {
|
struct RequestQuery #lifetime {
|
||||||
#(#fields),*
|
#(#fields),*
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -370,7 +388,7 @@ impl ToTokens for Request {
|
|||||||
let request = quote! {
|
let request = quote! {
|
||||||
#[derive(Debug, Clone, ::ruma_api::Outgoing)]
|
#[derive(Debug, Clone, ::ruma_api::Outgoing)]
|
||||||
#[incoming_no_deserialize]
|
#[incoming_no_deserialize]
|
||||||
pub struct Request #request_def
|
pub struct Request #request_generics #request_def
|
||||||
|
|
||||||
#request_body_struct
|
#request_body_struct
|
||||||
#request_query_struct
|
#request_query_struct
|
||||||
@ -498,13 +516,6 @@ impl RequestField {
|
|||||||
None
|
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.
|
/// 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.
|
//! 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 proc_macro2::TokenStream;
|
||||||
use quote::{quote, quote_spanned, ToTokens};
|
use quote::{quote, quote_spanned, ToTokens};
|
||||||
use syn::{spanned::Spanned, Field, Ident};
|
use syn::{spanned::Spanned, Field, Ident, Lifetime};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
api::{
|
api::{
|
||||||
@ -18,6 +18,9 @@ use crate::{
|
|||||||
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.
|
||||||
|
lifetimes: Vec<Lifetime>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Response {
|
impl Response {
|
||||||
@ -31,9 +34,13 @@ impl Response {
|
|||||||
self.fields.iter().any(|field| field.is_header())
|
self.fields.iter().any(|field| field.is_header())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Whether any field has a #[wrap_incoming] attribute.
|
/// Whether any field has a lifetime.
|
||||||
pub fn uses_wrap_incoming(&self) -> bool {
|
pub fn contains_lifetimes(&self) -> bool {
|
||||||
self.fields.iter().any(|f| f.has_wrap_incoming_attr())
|
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.
|
/// Produces code for a response struct initializer.
|
||||||
@ -162,6 +169,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 fields = raw
|
let fields = raw
|
||||||
.fields
|
.fields
|
||||||
@ -170,6 +178,8 @@ 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,
|
||||||
@ -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),* } }
|
quote! { { #(#fields),* } }
|
||||||
};
|
};
|
||||||
|
|
||||||
let (derive_deserialize, def) =
|
let (derive_deserialize, def) = if let Some(body_field) =
|
||||||
if let Some(body_field) = self.fields.iter().find(|f| f.is_newtype_body()) {
|
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 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 {
|
} 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() {
|
} 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 = if fields.clone().any(|f| f.has_wrap_incoming_attr()) {
|
let (derive_deserialize, lifetimes) =
|
||||||
TokenStream::new()
|
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)
|
(quote!(::ruma_api::exports::serde::Deserialize), TokenStream::new())
|
||||||
};
|
};
|
||||||
|
|
||||||
let fields = fields.map(ResponseField::field);
|
let fields = fields.map(ResponseField::field);
|
||||||
|
|
||||||
(derive_deserialize, quote!({ #(#fields),* }))
|
(derive_deserialize, quote!( #lifetimes { #(#fields),* }))
|
||||||
} else {
|
} else {
|
||||||
(TokenStream::new(), quote!({}))
|
(TokenStream::new(), quote!({}))
|
||||||
};
|
};
|
||||||
@ -280,10 +300,11 @@ impl ToTokens for Response {
|
|||||||
struct ResponseBody #def
|
struct ResponseBody #def
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let response_generics = util::generics_to_tokens(self.lifetimes.iter());
|
||||||
let response = quote! {
|
let response = quote! {
|
||||||
#[derive(Debug, Clone, ::ruma_api::Outgoing)]
|
#[derive(Debug, Clone, ::ruma_api::Outgoing)]
|
||||||
#[incoming_no_deserialize]
|
#[incoming_no_deserialize]
|
||||||
pub struct Response #response_def
|
pub struct Response #response_generics #response_def
|
||||||
|
|
||||||
#response_body_struct
|
#response_body_struct
|
||||||
};
|
};
|
||||||
@ -353,13 +374,6 @@ impl ResponseField {
|
|||||||
_ => None,
|
_ => 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.
|
/// The types of fields that a response can have, without their values.
|
||||||
|
@ -54,24 +54,24 @@ ruma_api! {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// #[derive(Outgoing)]
|
#[derive(Outgoing)]
|
||||||
// #[incoming_no_deserialize]
|
#[incoming_no_deserialize]
|
||||||
// pub struct Request<'a, T> {
|
pub struct FakeRequest<'a, T> {
|
||||||
// pub abc: &'a str,
|
pub abc: &'a str,
|
||||||
// pub thing: Thing<'a, T>,
|
pub thing: Thing<'a, T>,
|
||||||
// pub device_id: &'a ::ruma_identifiers::DeviceId,
|
pub device_id: &'a ::ruma_identifiers::DeviceId,
|
||||||
// pub user_id: &'a UserId,
|
pub user_id: &'a UserId,
|
||||||
// pub bytes: &'a [u8],
|
pub bytes: &'a [u8],
|
||||||
// pub recursive: &'a [Thing<'a, T>],
|
pub recursive: &'a [Thing<'a, T>],
|
||||||
// pub option: Option<&'a [u8]>,
|
pub option: Option<&'a [u8]>,
|
||||||
// }
|
}
|
||||||
|
|
||||||
// #[derive(Outgoing)]
|
#[derive(Outgoing)]
|
||||||
// #[incoming_no_deserialize]
|
#[incoming_no_deserialize]
|
||||||
// pub enum EnumThing<'a, T> {
|
pub enum EnumThing<'a, T> {
|
||||||
// Abc(&'a str),
|
Abc(&'a str),
|
||||||
// Stuff(Thing<'a, T>),
|
Stuff(Thing<'a, T>),
|
||||||
// Boxy(&'a ::ruma_identifiers::DeviceId),
|
Boxy(&'a ::ruma_identifiers::DeviceId),
|
||||||
// Other(Option<&'a str>),
|
Other(Option<&'a str>),
|
||||||
// StructVar { stuff: &'a str, more: &'a ::ruma_identifiers::ServerName },
|
StructVar { stuff: &'a str, more: &'a ::ruma_identifiers::ServerName },
|
||||||
// }
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user