151 lines
6.2 KiB
Rust
151 lines
6.2 KiB
Rust
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 response 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")]
|
|
#[allow(clippy::inconsistent_struct_constructor)]
|
|
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_http_response(
|
|
response
|
|
) {
|
|
Ok(err) => Err(#ruma_api::error::ServerError::Known(err).into()),
|
|
Err(response_err) => {
|
|
Err(#ruma_api::error::ServerError::Unknown(response_err).into())
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|