api-macros: Split response code generation into more files
This commit is contained in:
parent
59d47227a6
commit
e7a31f1fd1
@ -1,11 +1,14 @@
|
|||||||
//! Details of the `response` section of the procedural macro.
|
//! Details of the `response` section of the procedural macro.
|
||||||
|
|
||||||
use proc_macro2::TokenStream;
|
use proc_macro2::TokenStream;
|
||||||
use quote::{quote, quote_spanned};
|
use quote::quote;
|
||||||
use syn::{spanned::Spanned, Attribute, Field, Ident};
|
use syn::{Attribute, Field, Ident};
|
||||||
|
|
||||||
use super::metadata::Metadata;
|
use super::metadata::Metadata;
|
||||||
|
|
||||||
|
mod incoming;
|
||||||
|
mod outgoing;
|
||||||
|
|
||||||
/// The result of processing the `response` section of the macro.
|
/// The result of processing the `response` section of the macro.
|
||||||
pub(crate) struct Response {
|
pub(crate) struct Response {
|
||||||
/// The attributes that will be applied to the struct definition.
|
/// The attributes that will be applied to the struct definition.
|
||||||
@ -26,165 +29,6 @@ impl Response {
|
|||||||
self.fields.iter().any(|field| field.is_header())
|
self.fields.iter().any(|field| field.is_header())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Produces code for a response struct initializer.
|
|
||||||
fn init_fields(&self, ruma_api: &TokenStream) -> TokenStream {
|
|
||||||
let bytes = quote! { #ruma_api::exports::bytes };
|
|
||||||
let http = quote! { #ruma_api::exports::http };
|
|
||||||
|
|
||||||
let mut fields = vec![];
|
|
||||||
let mut new_type_raw_body = None;
|
|
||||||
for response_field in &self.fields {
|
|
||||||
let field = response_field.field();
|
|
||||||
let field_name = field.ident.as_ref().expect("expected field to have an identifier");
|
|
||||||
let span = field.span();
|
|
||||||
let cfg_attrs =
|
|
||||||
field.attrs.iter().filter(|a| a.path.is_ident("cfg")).collect::<Vec<_>>();
|
|
||||||
|
|
||||||
fields.push(match response_field {
|
|
||||||
ResponseField::Body(_) => {
|
|
||||||
quote_spanned! {span=>
|
|
||||||
#( #cfg_attrs )*
|
|
||||||
#field_name: response_body.#field_name
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ResponseField::Header(_, header_name) => {
|
|
||||||
let optional_header = match &field.ty {
|
|
||||||
syn::Type::Path(syn::TypePath {
|
|
||||||
path: syn::Path { segments, .. }, ..
|
|
||||||
}) if segments.last().unwrap().ident == "Option" => {
|
|
||||||
quote! {
|
|
||||||
#field_name: {
|
|
||||||
headers.remove(#http::header::#header_name)
|
|
||||||
.map(|h| h.to_str().map(|s| s.to_owned()))
|
|
||||||
.transpose()?
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
_ => quote! {
|
|
||||||
#field_name: {
|
|
||||||
headers.remove(#http::header::#header_name)
|
|
||||||
.expect("response missing expected header")
|
|
||||||
.to_str()?
|
|
||||||
.to_owned()
|
|
||||||
}
|
|
||||||
},
|
|
||||||
};
|
|
||||||
quote_spanned! {span=> #optional_header }
|
|
||||||
}
|
|
||||||
ResponseField::NewtypeBody(_) => {
|
|
||||||
quote_spanned! {span=>
|
|
||||||
#field_name: response_body.0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// This field must be instantiated last to avoid `use of move value` error.
|
|
||||||
// We are guaranteed only one new body field because of a check in `try_from`.
|
|
||||||
ResponseField::NewtypeRawBody(_) => {
|
|
||||||
new_type_raw_body = Some(quote_spanned! {span=>
|
|
||||||
#field_name: {
|
|
||||||
let mut reader = #bytes::Buf::reader(response.into_body());
|
|
||||||
let mut vec = ::std::vec::Vec::new();
|
|
||||||
::std::io::Read::read_to_end(&mut reader, &mut vec)
|
|
||||||
.expect("reading from a bytes::Buf never fails");
|
|
||||||
vec
|
|
||||||
}
|
|
||||||
});
|
|
||||||
// skip adding to the vec
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
fields.extend(new_type_raw_body);
|
|
||||||
|
|
||||||
quote! {
|
|
||||||
#(#fields,)*
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Produces code to add necessary HTTP headers to an `http::Response`.
|
|
||||||
fn apply_header_fields(&self, ruma_api: &TokenStream) -> TokenStream {
|
|
||||||
let http = quote! { #ruma_api::exports::http };
|
|
||||||
|
|
||||||
let header_calls = self.fields.iter().filter_map(|response_field| {
|
|
||||||
if let ResponseField::Header(ref field, ref header_name) = *response_field {
|
|
||||||
let field_name =
|
|
||||||
field.ident.as_ref().expect("expected field to have an identifier");
|
|
||||||
let span = field.span();
|
|
||||||
|
|
||||||
let optional_header = match &field.ty {
|
|
||||||
syn::Type::Path(syn::TypePath { path: syn::Path { segments, .. }, .. })
|
|
||||||
if segments.last().unwrap().ident == "Option" =>
|
|
||||||
{
|
|
||||||
quote! {
|
|
||||||
if let Some(header) = self.#field_name {
|
|
||||||
headers
|
|
||||||
.insert(
|
|
||||||
#http::header::#header_name,
|
|
||||||
header.parse()?,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
_ => quote! {
|
|
||||||
headers
|
|
||||||
.insert(
|
|
||||||
#http::header::#header_name,
|
|
||||||
self.#field_name.parse()?,
|
|
||||||
);
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
Some(quote_spanned! {span=>
|
|
||||||
#optional_header
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
quote! { #(#header_calls)* }
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Produces code to initialize the struct that will be used to create the response body.
|
|
||||||
fn to_body(&self, ruma_api: &TokenStream) -> TokenStream {
|
|
||||||
let serde_json = quote! { #ruma_api::exports::serde_json };
|
|
||||||
|
|
||||||
if let Some(field) = self.newtype_raw_body_field() {
|
|
||||||
let field_name = field.ident.as_ref().expect("expected field to have an identifier");
|
|
||||||
let span = field.span();
|
|
||||||
return quote_spanned!(span=> self.#field_name);
|
|
||||||
}
|
|
||||||
|
|
||||||
let body = if let Some(field) = self.newtype_body_field() {
|
|
||||||
let field_name = field.ident.as_ref().expect("expected field to have an identifier");
|
|
||||||
let span = field.span();
|
|
||||||
quote_spanned!(span=> self.#field_name)
|
|
||||||
} else {
|
|
||||||
let fields = self.fields.iter().filter_map(|response_field| {
|
|
||||||
if let ResponseField::Body(ref field) = *response_field {
|
|
||||||
let field_name =
|
|
||||||
field.ident.as_ref().expect("expected field to have an identifier");
|
|
||||||
let span = field.span();
|
|
||||||
let cfg_attrs =
|
|
||||||
field.attrs.iter().filter(|a| a.path.is_ident("cfg")).collect::<Vec<_>>();
|
|
||||||
|
|
||||||
Some(quote_spanned! {span=>
|
|
||||||
#( #cfg_attrs )*
|
|
||||||
#field_name: self.#field_name
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
quote! {
|
|
||||||
ResponseBody { #(#fields),* }
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
quote!(#serde_json::to_vec(&#body)?)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Gets the newtype body field, if this response has one.
|
/// Gets the newtype body field, if this response has one.
|
||||||
fn newtype_body_field(&self) -> Option<&Field> {
|
fn newtype_body_field(&self) -> Option<&Field> {
|
||||||
self.fields.iter().find_map(ResponseField::as_newtype_body_field)
|
self.fields.iter().find_map(ResponseField::as_newtype_body_field)
|
||||||
@ -201,51 +45,13 @@ impl Response {
|
|||||||
error_ty: &TokenStream,
|
error_ty: &TokenStream,
|
||||||
ruma_api: &TokenStream,
|
ruma_api: &TokenStream,
|
||||||
) -> TokenStream {
|
) -> TokenStream {
|
||||||
let bytes = quote! { #ruma_api::exports::bytes };
|
|
||||||
let http = quote! { #ruma_api::exports::http };
|
|
||||||
let ruma_serde = quote! { #ruma_api::exports::ruma_serde };
|
let ruma_serde = quote! { #ruma_api::exports::ruma_serde };
|
||||||
let serde = quote! { #ruma_api::exports::serde };
|
let serde = quote! { #ruma_api::exports::serde };
|
||||||
let serde_json = quote! { #ruma_api::exports::serde_json };
|
|
||||||
|
|
||||||
let docs =
|
let docs =
|
||||||
format!("Data in the response from the `{}` API endpoint.", metadata.name.value());
|
format!("Data in the response from the `{}` API endpoint.", metadata.name.value());
|
||||||
let struct_attributes = &self.attributes;
|
let struct_attributes = &self.attributes;
|
||||||
|
|
||||||
let extract_response_headers = if self.has_header_fields() {
|
|
||||||
quote! {
|
|
||||||
let mut headers = response.headers().clone();
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
TokenStream::new()
|
|
||||||
};
|
|
||||||
|
|
||||||
let typed_response_body_decl =
|
|
||||||
if self.has_body_fields() || self.newtype_body_field().is_some() {
|
|
||||||
quote! {
|
|
||||||
let response_body: <
|
|
||||||
ResponseBody
|
|
||||||
as #ruma_serde::Outgoing
|
|
||||||
>::Incoming = {
|
|
||||||
let body = response.into_body();
|
|
||||||
if #bytes::Buf::has_remaining(&body) {
|
|
||||||
#serde_json::from_reader(#bytes::Buf::reader(body))?
|
|
||||||
} else {
|
|
||||||
// If the reponse body is completely empty, pretend it is an empty JSON
|
|
||||||
// object instead. This allows responses with only optional body
|
|
||||||
// parameters to be deserialized in that case.
|
|
||||||
#serde_json::from_str("{}")?
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
TokenStream::new()
|
|
||||||
};
|
|
||||||
|
|
||||||
let response_init_fields = self.init_fields(&ruma_api);
|
|
||||||
let serialize_response_headers = self.apply_header_fields(&ruma_api);
|
|
||||||
|
|
||||||
let body = self.to_body(&ruma_api);
|
|
||||||
|
|
||||||
let response_def = if self.fields.is_empty() {
|
let response_def = if self.fields.is_empty() {
|
||||||
quote!(;)
|
quote!(;)
|
||||||
} else {
|
} else {
|
||||||
@ -273,6 +79,9 @@ impl Response {
|
|||||||
struct ResponseBody #def
|
struct ResponseBody #def
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let outgoing_response_impl = self.expand_outgoing(ruma_api);
|
||||||
|
let incoming_response_impl = self.expand_incoming(error_ty, ruma_api);
|
||||||
|
|
||||||
quote! {
|
quote! {
|
||||||
#[doc = #docs]
|
#[doc = #docs]
|
||||||
#[derive(Debug, Clone, #ruma_serde::Outgoing, #ruma_serde::_FakeDeriveSerde)]
|
#[derive(Debug, Clone, #ruma_serde::Outgoing, #ruma_serde::_FakeDeriveSerde)]
|
||||||
@ -283,59 +92,8 @@ impl Response {
|
|||||||
|
|
||||||
#response_body_struct
|
#response_body_struct
|
||||||
|
|
||||||
#[automatically_derived]
|
#outgoing_response_impl
|
||||||
#[cfg(feature = "server")]
|
#incoming_response_impl
|
||||||
impl #ruma_api::OutgoingResponse for Response {
|
|
||||||
fn try_into_http_response(
|
|
||||||
self,
|
|
||||||
) -> ::std::result::Result<
|
|
||||||
#http::Response<::std::vec::Vec<u8>>,
|
|
||||||
#ruma_api::error::IntoHttpError,
|
|
||||||
> {
|
|
||||||
let mut resp_builder = #http::Response::builder()
|
|
||||||
.header(#http::header::CONTENT_TYPE, "application/json");
|
|
||||||
|
|
||||||
let mut headers = resp_builder
|
|
||||||
.headers_mut()
|
|
||||||
.expect("`http::ResponseBuilder` is in unusable state");
|
|
||||||
#serialize_response_headers
|
|
||||||
|
|
||||||
// This cannot fail because we parse each header value
|
|
||||||
// checking for errors as each value is inserted and
|
|
||||||
// we only allow keys from the `http::header` module.
|
|
||||||
Ok(resp_builder.body(#body).unwrap())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[automatically_derived]
|
|
||||||
#[cfg(feature = "client")]
|
|
||||||
impl #ruma_api::IncomingResponse for Response {
|
|
||||||
type EndpointError = #error_ty;
|
|
||||||
|
|
||||||
fn try_from_http_response<T: #bytes::Buf>(
|
|
||||||
response: #http::Response<T>,
|
|
||||||
) -> ::std::result::Result<
|
|
||||||
Self,
|
|
||||||
#ruma_api::error::FromHttpResponseError<#error_ty>,
|
|
||||||
> {
|
|
||||||
if response.status().as_u16() < 400 {
|
|
||||||
#extract_response_headers
|
|
||||||
|
|
||||||
#typed_response_body_decl
|
|
||||||
|
|
||||||
Ok(Self {
|
|
||||||
#response_init_fields
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
match <#error_ty as #ruma_api::EndpointError>::try_from_response(response) {
|
|
||||||
Ok(err) => Err(#ruma_api::error::ServerError::Known(err).into()),
|
|
||||||
Err(response_err) => {
|
|
||||||
Err(#ruma_api::error::ServerError::Unknown(response_err).into())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -408,15 +166,8 @@ impl ResponseField {
|
|||||||
|
|
||||||
/// The types of fields that a response can have, without their values.
|
/// The types of fields that a response can have, without their values.
|
||||||
pub(crate) enum ResponseFieldKind {
|
pub(crate) enum ResponseFieldKind {
|
||||||
/// See the similarly named variant of `ResponseField`.
|
|
||||||
Body,
|
Body,
|
||||||
|
|
||||||
/// See the similarly named variant of `ResponseField`.
|
|
||||||
Header,
|
Header,
|
||||||
|
|
||||||
/// See the similarly named variant of `ResponseField`.
|
|
||||||
NewtypeBody,
|
NewtypeBody,
|
||||||
|
|
||||||
/// See the similarly named variant of `ResponseField`.
|
|
||||||
NewtypeRawBody,
|
NewtypeRawBody,
|
||||||
}
|
}
|
||||||
|
147
ruma-api-macros/src/api/response/incoming.rs
Normal file
147
ruma-api-macros/src/api/response/incoming.rs
Normal file
@ -0,0 +1,147 @@
|
|||||||
|
use proc_macro2::TokenStream;
|
||||||
|
use quote::quote;
|
||||||
|
|
||||||
|
use super::{Response, ResponseField};
|
||||||
|
|
||||||
|
impl Response {
|
||||||
|
pub fn expand_incoming(&self, error_ty: &TokenStream, ruma_api: &TokenStream) -> TokenStream {
|
||||||
|
let bytes = quote! { #ruma_api::exports::bytes };
|
||||||
|
let http = quote! { #ruma_api::exports::http };
|
||||||
|
let ruma_serde = quote! { #ruma_api::exports::ruma_serde };
|
||||||
|
let serde_json = quote! { #ruma_api::exports::serde_json };
|
||||||
|
|
||||||
|
let extract_response_headers = if self.has_header_fields() {
|
||||||
|
quote! {
|
||||||
|
let mut headers = response.headers().clone();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
TokenStream::new()
|
||||||
|
};
|
||||||
|
|
||||||
|
let typed_response_body_decl =
|
||||||
|
if self.has_body_fields() || self.newtype_body_field().is_some() {
|
||||||
|
quote! {
|
||||||
|
let response_body: <
|
||||||
|
ResponseBody
|
||||||
|
as #ruma_serde::Outgoing
|
||||||
|
>::Incoming = {
|
||||||
|
let body = response.into_body();
|
||||||
|
if #bytes::Buf::has_remaining(&body) {
|
||||||
|
#serde_json::from_reader(#bytes::Buf::reader(body))?
|
||||||
|
} else {
|
||||||
|
// If the reponse body is completely empty, pretend it is an empty JSON
|
||||||
|
// object instead. This allows responses with only optional body
|
||||||
|
// parameters to be deserialized in that case.
|
||||||
|
#serde_json::from_str("{}")?
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
TokenStream::new()
|
||||||
|
};
|
||||||
|
|
||||||
|
let response_init_fields = {
|
||||||
|
let mut fields = vec![];
|
||||||
|
let mut new_type_raw_body = None;
|
||||||
|
|
||||||
|
for response_field in &self.fields {
|
||||||
|
let field = response_field.field();
|
||||||
|
let field_name =
|
||||||
|
field.ident.as_ref().expect("expected field to have an identifier");
|
||||||
|
let cfg_attrs =
|
||||||
|
field.attrs.iter().filter(|a| a.path.is_ident("cfg")).collect::<Vec<_>>();
|
||||||
|
|
||||||
|
fields.push(match response_field {
|
||||||
|
ResponseField::Body(_) => {
|
||||||
|
quote! {
|
||||||
|
#( #cfg_attrs )*
|
||||||
|
#field_name: response_body.#field_name
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ResponseField::Header(_, header_name) => {
|
||||||
|
let optional_header = match &field.ty {
|
||||||
|
syn::Type::Path(syn::TypePath {
|
||||||
|
path: syn::Path { segments, .. },
|
||||||
|
..
|
||||||
|
}) if segments.last().unwrap().ident == "Option" => {
|
||||||
|
quote! {
|
||||||
|
#field_name: {
|
||||||
|
headers.remove(#http::header::#header_name)
|
||||||
|
.map(|h| h.to_str().map(|s| s.to_owned()))
|
||||||
|
.transpose()?
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => quote! {
|
||||||
|
#field_name: {
|
||||||
|
headers.remove(#http::header::#header_name)
|
||||||
|
.expect("response missing expected header")
|
||||||
|
.to_str()?
|
||||||
|
.to_owned()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
};
|
||||||
|
quote! { #optional_header }
|
||||||
|
}
|
||||||
|
ResponseField::NewtypeBody(_) => {
|
||||||
|
quote! {
|
||||||
|
#field_name: response_body.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// This field must be instantiated last to avoid `use of move value` error.
|
||||||
|
// We are guaranteed only one new body field because of a check in `try_from`.
|
||||||
|
ResponseField::NewtypeRawBody(_) => {
|
||||||
|
new_type_raw_body = Some(quote! {
|
||||||
|
#field_name: {
|
||||||
|
let mut reader = #bytes::Buf::reader(response.into_body());
|
||||||
|
let mut vec = ::std::vec::Vec::new();
|
||||||
|
::std::io::Read::read_to_end(&mut reader, &mut vec)
|
||||||
|
.expect("reading from a bytes::Buf never fails");
|
||||||
|
vec
|
||||||
|
}
|
||||||
|
});
|
||||||
|
// skip adding to the vec
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
fields.extend(new_type_raw_body);
|
||||||
|
|
||||||
|
quote! {
|
||||||
|
#(#fields,)*
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
quote! {
|
||||||
|
#[automatically_derived]
|
||||||
|
#[cfg(feature = "client")]
|
||||||
|
impl #ruma_api::IncomingResponse for Response {
|
||||||
|
type EndpointError = #error_ty;
|
||||||
|
|
||||||
|
fn try_from_http_response<T: #bytes::Buf>(
|
||||||
|
response: #http::Response<T>,
|
||||||
|
) -> ::std::result::Result<
|
||||||
|
Self,
|
||||||
|
#ruma_api::error::FromHttpResponseError<#error_ty>,
|
||||||
|
> {
|
||||||
|
if response.status().as_u16() < 400 {
|
||||||
|
#extract_response_headers
|
||||||
|
#typed_response_body_decl
|
||||||
|
|
||||||
|
Ok(Self {
|
||||||
|
#response_init_fields
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
match <#error_ty as #ruma_api::EndpointError>::try_from_response(response) {
|
||||||
|
Ok(err) => Err(#ruma_api::error::ServerError::Known(err).into()),
|
||||||
|
Err(response_err) => {
|
||||||
|
Err(#ruma_api::error::ServerError::Unknown(response_err).into())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
95
ruma-api-macros/src/api/response/outgoing.rs
Normal file
95
ruma-api-macros/src/api/response/outgoing.rs
Normal file
@ -0,0 +1,95 @@
|
|||||||
|
use proc_macro2::TokenStream;
|
||||||
|
use quote::quote;
|
||||||
|
|
||||||
|
use super::{Response, ResponseField};
|
||||||
|
|
||||||
|
impl Response {
|
||||||
|
pub fn expand_outgoing(&self, ruma_api: &TokenStream) -> TokenStream {
|
||||||
|
let http = quote! { #ruma_api::exports::http };
|
||||||
|
let serde_json = quote! { #ruma_api::exports::serde_json };
|
||||||
|
|
||||||
|
let serialize_response_headers = self.fields.iter().map(|response_field| {
|
||||||
|
if let ResponseField::Header(field, header_name) = response_field {
|
||||||
|
let field_name =
|
||||||
|
field.ident.as_ref().expect("expected field to have an identifier");
|
||||||
|
|
||||||
|
match &field.ty {
|
||||||
|
syn::Type::Path(syn::TypePath { path: syn::Path { segments, .. }, .. })
|
||||||
|
if segments.last().unwrap().ident == "Option" =>
|
||||||
|
{
|
||||||
|
quote! {
|
||||||
|
if let Some(header) = self.#field_name {
|
||||||
|
headers.insert(
|
||||||
|
#http::header::#header_name,
|
||||||
|
header.parse()?,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => quote! {
|
||||||
|
headers.insert(
|
||||||
|
#http::header::#header_name,
|
||||||
|
self.#field_name.parse()?,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
TokenStream::new()
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
let body = if let Some(field) = self.newtype_raw_body_field() {
|
||||||
|
let field_name = field.ident.as_ref().expect("expected field to have an identifier");
|
||||||
|
quote! { self.#field_name }
|
||||||
|
} else if let Some(field) = self.newtype_body_field() {
|
||||||
|
let field_name = field.ident.as_ref().expect("expected field to have an identifier");
|
||||||
|
quote! {
|
||||||
|
#serde_json::to_vec(&self.#field_name)?
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
let fields = self.fields.iter().map(|response_field| {
|
||||||
|
if let ResponseField::Body(field) = response_field {
|
||||||
|
let field_name =
|
||||||
|
field.ident.as_ref().expect("expected field to have an identifier");
|
||||||
|
let cfg_attrs = field.attrs.iter().filter(|a| a.path.is_ident("cfg"));
|
||||||
|
|
||||||
|
quote! {
|
||||||
|
#( #cfg_attrs )*
|
||||||
|
#field_name: self.#field_name,
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
TokenStream::new()
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
quote! {
|
||||||
|
#serde_json::to_vec(&ResponseBody { #(#fields)* })?
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
quote! {
|
||||||
|
#[automatically_derived]
|
||||||
|
#[cfg(feature = "server")]
|
||||||
|
impl #ruma_api::OutgoingResponse for Response {
|
||||||
|
fn try_into_http_response(
|
||||||
|
self,
|
||||||
|
) -> ::std::result::Result<
|
||||||
|
#http::Response<::std::vec::Vec<u8>>,
|
||||||
|
#ruma_api::error::IntoHttpError,
|
||||||
|
> {
|
||||||
|
let mut resp_builder = #http::Response::builder()
|
||||||
|
.header(#http::header::CONTENT_TYPE, "application/json");
|
||||||
|
|
||||||
|
let mut headers = resp_builder
|
||||||
|
.headers_mut()
|
||||||
|
.expect("`http::ResponseBuilder` is in unusable state");
|
||||||
|
#(#serialize_response_headers)*
|
||||||
|
|
||||||
|
// This cannot fail because we parse each header value checking for errors as
|
||||||
|
// each value is inserted and we only allow keys from the `http::header` module.
|
||||||
|
Ok(resp_builder.body(#body).unwrap())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user