Implement #[ruma_api(raw_body)]
This commit is contained in:
parent
b6394a32b7
commit
86aa04bc59
@ -274,26 +274,55 @@ impl ToTokens for Api {
|
|||||||
TokenStream::new()
|
TokenStream::new()
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let extract_request_body =
|
||||||
|
if self.request.has_body_fields() || self.request.newtype_body_field().is_some() {
|
||||||
|
quote! {
|
||||||
|
let request_body: <RequestBody as ruma_api::Outgoing>::Incoming =
|
||||||
|
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()
|
||||||
} else {
|
} else {
|
||||||
TokenStream::new()
|
TokenStream::new()
|
||||||
};
|
};
|
||||||
|
|
||||||
let request_body_initializers = if let Some(field) = self.request.newtype_body_field() {
|
let request_body = if let Some(field) = self.request.newtype_raw_body_field() {
|
||||||
let field_name = field.ident.as_ref().expect("expected field to have an identifier");
|
let field_name = field.ident.as_ref().expect("expected field to have an identifier");
|
||||||
quote! { (request.#field_name) }
|
quote!(request.#field_name)
|
||||||
|
} else if self.request.has_body_fields() || self.request.newtype_body_field().is_some() {
|
||||||
|
let request_body_initializers = if let Some(field) = self.request.newtype_body_field() {
|
||||||
|
let field_name =
|
||||||
|
field.ident.as_ref().expect("expected field to have an identifier");
|
||||||
|
quote! { (request.#field_name) }
|
||||||
|
} else {
|
||||||
|
let initializers = self.request.request_body_init_fields();
|
||||||
|
quote! { { #initializers } }
|
||||||
|
};
|
||||||
|
|
||||||
|
quote! {
|
||||||
|
{
|
||||||
|
let request_body = RequestBody #request_body_initializers;
|
||||||
|
ruma_api::exports::serde_json::to_vec(&request_body)?
|
||||||
|
}
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
let initializers = self.request.request_body_init_fields();
|
quote!(Vec::new())
|
||||||
quote! { { #initializers } }
|
|
||||||
};
|
};
|
||||||
|
|
||||||
let parse_request_body = if let Some(field) = self.request.newtype_body_field() {
|
let parse_request_body = if let Some(field) = self.request.newtype_body_field() {
|
||||||
let field_name = field.ident.as_ref().expect("expected field to have an identifier");
|
let field_name = field.ident.as_ref().expect("expected field to have an identifier");
|
||||||
|
|
||||||
quote! {
|
quote! {
|
||||||
#field_name: request_body.0,
|
#field_name: request_body.0,
|
||||||
}
|
}
|
||||||
|
} else if let Some(field) = self.request.newtype_raw_body_field() {
|
||||||
|
let field_name = field.ident.as_ref().expect("expected field to have an identifier");
|
||||||
|
quote! {
|
||||||
|
#field_name: request.into_body(),
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
self.request.request_init_body_fields()
|
self.request.request_init_body_fields()
|
||||||
};
|
};
|
||||||
@ -306,6 +335,16 @@ impl ToTokens for Api {
|
|||||||
TokenStream::new()
|
TokenStream::new()
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let typed_response_body_decl =
|
||||||
|
if self.response.has_body_fields() || self.response.newtype_body_field().is_some() {
|
||||||
|
quote! {
|
||||||
|
let response_body: <ResponseBody as ruma_api::Outgoing>::Incoming =
|
||||||
|
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();
|
||||||
@ -337,9 +376,7 @@ impl ToTokens for Api {
|
|||||||
#extract_request_path
|
#extract_request_path
|
||||||
#extract_request_query
|
#extract_request_query
|
||||||
#extract_request_headers
|
#extract_request_headers
|
||||||
|
#extract_request_body
|
||||||
let request_body: <RequestBody as ruma_api::Outgoing>::Incoming =
|
|
||||||
ruma_api::exports::serde_json::from_slice(request.body().as_slice())?;
|
|
||||||
|
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
#parse_request_path
|
#parse_request_path
|
||||||
@ -367,11 +404,7 @@ impl ToTokens for Api {
|
|||||||
{ #url_set_path }
|
{ #url_set_path }
|
||||||
{ #url_set_querystring }
|
{ #url_set_querystring }
|
||||||
|
|
||||||
let request_body = RequestBody #request_body_initializers;
|
let mut http_request = ruma_api::exports::http::Request::new(#request_body);
|
||||||
|
|
||||||
let mut http_request = ruma_api::exports::http::Request::new(
|
|
||||||
ruma_api::exports::serde_json::to_vec(&request_body)?,
|
|
||||||
);
|
|
||||||
|
|
||||||
*http_request.method_mut() = ruma_api::exports::http::Method::#method;
|
*http_request.method_mut() = ruma_api::exports::http::Method::#method;
|
||||||
*http_request.uri_mut() = url.into_string().parse().unwrap();
|
*http_request.uri_mut() = url.into_string().parse().unwrap();
|
||||||
@ -393,7 +426,7 @@ impl ToTokens for Api {
|
|||||||
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
|
||||||
.body(ruma_api::exports::serde_json::to_vec(&#body)?)
|
.body(#body)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
Ok(response)
|
Ok(response)
|
||||||
}
|
}
|
||||||
@ -409,10 +442,8 @@ impl ToTokens for Api {
|
|||||||
if http_response.status().is_success() {
|
if http_response.status().is_success() {
|
||||||
#extract_response_headers
|
#extract_response_headers
|
||||||
|
|
||||||
let response_body: <ResponseBody as ruma_api::Outgoing>::Incoming =
|
let response_body = http_response.into_body();
|
||||||
ruma_api::exports::serde_json::from_slice(
|
#typed_response_body_decl
|
||||||
http_response.into_body().as_slice(),
|
|
||||||
)?;
|
|
||||||
|
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
#response_init_fields
|
#response_init_fields
|
||||||
|
@ -118,6 +118,11 @@ impl Request {
|
|||||||
self.fields.iter().find_map(RequestField::as_newtype_body_field)
|
self.fields.iter().find_map(RequestField::as_newtype_body_field)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns the body field.
|
||||||
|
pub fn newtype_raw_body_field(&self) -> Option<&Field> {
|
||||||
|
self.fields.iter().find_map(RequestField::as_newtype_raw_body_field)
|
||||||
|
}
|
||||||
|
|
||||||
/// Returns the query map field.
|
/// Returns the query map field.
|
||||||
pub fn query_map_field(&self) -> Option<&Field> {
|
pub fn query_map_field(&self) -> Option<&Field> {
|
||||||
self.fields.iter().find_map(RequestField::as_query_map_field)
|
self.fields.iter().find_map(RequestField::as_query_map_field)
|
||||||
@ -205,7 +210,7 @@ impl TryFrom<RawRequest> for Request {
|
|||||||
field_kind = Some(match meta {
|
field_kind = Some(match meta {
|
||||||
Meta::Word(ident) => {
|
Meta::Word(ident) => {
|
||||||
match &ident.to_string()[..] {
|
match &ident.to_string()[..] {
|
||||||
"body" => {
|
s @ "body" | s @ "raw_body" => {
|
||||||
if let Some(f) = &newtype_body_field {
|
if let Some(f) = &newtype_body_field {
|
||||||
let mut error = syn::Error::new_spanned(
|
let mut error = syn::Error::new_spanned(
|
||||||
field,
|
field,
|
||||||
@ -219,7 +224,11 @@ impl TryFrom<RawRequest> for Request {
|
|||||||
}
|
}
|
||||||
|
|
||||||
newtype_body_field = Some(field.clone());
|
newtype_body_field = Some(field.clone());
|
||||||
RequestFieldKind::NewtypeBody
|
match s {
|
||||||
|
"body" => RequestFieldKind::NewtypeBody,
|
||||||
|
"raw_body" => RequestFieldKind::NewtypeRawBody,
|
||||||
|
_ => unreachable!(),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
"path" => RequestFieldKind::Path,
|
"path" => RequestFieldKind::Path,
|
||||||
"query" => RequestFieldKind::Query,
|
"query" => RequestFieldKind::Query,
|
||||||
@ -299,7 +308,7 @@ impl ToTokens for Request {
|
|||||||
quote! { { #(#fields),* } }
|
quote! { { #(#fields),* } }
|
||||||
};
|
};
|
||||||
|
|
||||||
let (derive_deserialize, request_body_def) =
|
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 body_field.has_wrap_incoming_attr() {
|
||||||
@ -308,7 +317,7 @@ impl ToTokens for Request {
|
|||||||
quote!(ruma_api::exports::serde::Deserialize)
|
quote!(ruma_api::exports::serde::Deserialize)
|
||||||
};
|
};
|
||||||
|
|
||||||
(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 = if fields.clone().any(|f| f.has_wrap_incoming_attr()) {
|
||||||
@ -318,10 +327,22 @@ impl ToTokens for Request {
|
|||||||
};
|
};
|
||||||
let fields = fields.map(RequestField::field);
|
let fields = fields.map(RequestField::field);
|
||||||
|
|
||||||
(derive_deserialize, quote! { { #(#fields),* } })
|
Some((derive_deserialize, quote! { { #(#fields),* } }))
|
||||||
} else {
|
} else {
|
||||||
(quote!(ruma_api::exports::serde::Deserialize), quote!(;))
|
None
|
||||||
};
|
}
|
||||||
|
.map(|(derive_deserialize, def)| {
|
||||||
|
quote! {
|
||||||
|
/// Data in the request body.
|
||||||
|
#[derive(
|
||||||
|
Debug,
|
||||||
|
ruma_api::Outgoing,
|
||||||
|
ruma_api::exports::serde::Serialize,
|
||||||
|
#derive_deserialize
|
||||||
|
)]
|
||||||
|
struct RequestBody #def
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
let request_path_struct = if self.has_path_fields() {
|
let request_path_struct = if self.has_path_fields() {
|
||||||
let fields = self.fields.iter().filter_map(RequestField::as_path_field);
|
let fields = self.fields.iter().filter_map(RequestField::as_path_field);
|
||||||
@ -376,15 +397,7 @@ impl ToTokens for Request {
|
|||||||
#[incoming_no_deserialize]
|
#[incoming_no_deserialize]
|
||||||
pub struct Request #request_def
|
pub struct Request #request_def
|
||||||
|
|
||||||
/// Data in the request body.
|
#request_body_struct
|
||||||
#[derive(
|
|
||||||
Debug,
|
|
||||||
ruma_api::Outgoing,
|
|
||||||
ruma_api::exports::serde::Serialize,
|
|
||||||
#derive_deserialize
|
|
||||||
)]
|
|
||||||
struct RequestBody #request_body_def
|
|
||||||
|
|
||||||
#request_path_struct
|
#request_path_struct
|
||||||
#request_query_struct
|
#request_query_struct
|
||||||
};
|
};
|
||||||
@ -401,6 +414,8 @@ pub enum RequestField {
|
|||||||
Header(Field, Ident),
|
Header(Field, Ident),
|
||||||
/// A specific data type in the body of the request.
|
/// A specific data type in the body of the request.
|
||||||
NewtypeBody(Field),
|
NewtypeBody(Field),
|
||||||
|
/// Arbitrary bytes in the body of the request.
|
||||||
|
NewtypeRawBody(Field),
|
||||||
/// Data that appears in the URL path.
|
/// Data that appears in the URL path.
|
||||||
Path(Field),
|
Path(Field),
|
||||||
/// Data that appears in the query string.
|
/// Data that appears in the query string.
|
||||||
@ -418,6 +433,7 @@ impl RequestField {
|
|||||||
RequestField::Header(field, header.expect("missing header name"))
|
RequestField::Header(field, header.expect("missing header name"))
|
||||||
}
|
}
|
||||||
RequestFieldKind::NewtypeBody => RequestField::NewtypeBody(field),
|
RequestFieldKind::NewtypeBody => RequestField::NewtypeBody(field),
|
||||||
|
RequestFieldKind::NewtypeRawBody => RequestField::NewtypeRawBody(field),
|
||||||
RequestFieldKind::Path => RequestField::Path(field),
|
RequestFieldKind::Path => RequestField::Path(field),
|
||||||
RequestFieldKind::Query => RequestField::Query(field),
|
RequestFieldKind::Query => RequestField::Query(field),
|
||||||
RequestFieldKind::QueryMap => RequestField::QueryMap(field),
|
RequestFieldKind::QueryMap => RequestField::QueryMap(field),
|
||||||
@ -430,6 +446,7 @@ impl RequestField {
|
|||||||
RequestField::Body(..) => RequestFieldKind::Body,
|
RequestField::Body(..) => RequestFieldKind::Body,
|
||||||
RequestField::Header(..) => RequestFieldKind::Header,
|
RequestField::Header(..) => RequestFieldKind::Header,
|
||||||
RequestField::NewtypeBody(..) => RequestFieldKind::NewtypeBody,
|
RequestField::NewtypeBody(..) => RequestFieldKind::NewtypeBody,
|
||||||
|
RequestField::NewtypeRawBody(..) => RequestFieldKind::NewtypeRawBody,
|
||||||
RequestField::Path(..) => RequestFieldKind::Path,
|
RequestField::Path(..) => RequestFieldKind::Path,
|
||||||
RequestField::Query(..) => RequestFieldKind::Query,
|
RequestField::Query(..) => RequestFieldKind::Query,
|
||||||
RequestField::QueryMap(..) => RequestFieldKind::QueryMap,
|
RequestField::QueryMap(..) => RequestFieldKind::QueryMap,
|
||||||
@ -471,6 +488,11 @@ impl RequestField {
|
|||||||
self.field_of_kind(RequestFieldKind::NewtypeBody)
|
self.field_of_kind(RequestFieldKind::NewtypeBody)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Return the contained field if this request field is a raw body kind.
|
||||||
|
fn as_newtype_raw_body_field(&self) -> Option<&Field> {
|
||||||
|
self.field_of_kind(RequestFieldKind::NewtypeRawBody)
|
||||||
|
}
|
||||||
|
|
||||||
/// Return the contained field if this request field is a path kind.
|
/// Return the contained field if this request field is a path kind.
|
||||||
fn as_path_field(&self) -> Option<&Field> {
|
fn as_path_field(&self) -> Option<&Field> {
|
||||||
self.field_of_kind(RequestFieldKind::Path)
|
self.field_of_kind(RequestFieldKind::Path)
|
||||||
@ -492,6 +514,7 @@ impl RequestField {
|
|||||||
RequestField::Body(field)
|
RequestField::Body(field)
|
||||||
| RequestField::Header(field, _)
|
| RequestField::Header(field, _)
|
||||||
| RequestField::NewtypeBody(field)
|
| RequestField::NewtypeBody(field)
|
||||||
|
| RequestField::NewtypeRawBody(field)
|
||||||
| RequestField::Path(field)
|
| RequestField::Path(field)
|
||||||
| RequestField::Query(field)
|
| RequestField::Query(field)
|
||||||
| RequestField::QueryMap(field) => field,
|
| RequestField::QueryMap(field) => field,
|
||||||
@ -525,6 +548,8 @@ enum RequestFieldKind {
|
|||||||
/// See the similarly named variant of `RequestField`.
|
/// See the similarly named variant of `RequestField`.
|
||||||
NewtypeBody,
|
NewtypeBody,
|
||||||
/// See the similarly named variant of `RequestField`.
|
/// See the similarly named variant of `RequestField`.
|
||||||
|
NewtypeRawBody,
|
||||||
|
/// See the similarly named variant of `RequestField`.
|
||||||
Path,
|
Path,
|
||||||
/// See the similarly named variant of `RequestField`.
|
/// See the similarly named variant of `RequestField`.
|
||||||
Query,
|
Query,
|
||||||
|
@ -60,6 +60,11 @@ impl Response {
|
|||||||
#field_name: response_body.0
|
#field_name: response_body.0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
ResponseField::NewtypeRawBody(_) => {
|
||||||
|
quote_spanned! {span=>
|
||||||
|
#field_name: response_body
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -89,7 +94,17 @@ impl Response {
|
|||||||
|
|
||||||
/// Produces code to initialize the struct that will be used to create the response body.
|
/// Produces code to initialize the struct that will be used to create the response body.
|
||||||
pub fn to_body(&self) -> TokenStream {
|
pub fn to_body(&self) -> TokenStream {
|
||||||
if let Some(field) = self.newtype_body_field() {
|
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=> response.#field_name);
|
||||||
|
}
|
||||||
|
|
||||||
|
if !self.has_body_fields() && self.newtype_body_field().is_none() {
|
||||||
|
return quote!(Vec::new());
|
||||||
|
}
|
||||||
|
|
||||||
|
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 field_name = field.ident.as_ref().expect("expected field to have an identifier");
|
||||||
let span = field.span();
|
let span = field.span();
|
||||||
quote_spanned!(span=> response.#field_name)
|
quote_spanned!(span=> response.#field_name)
|
||||||
@ -111,13 +126,20 @@ impl Response {
|
|||||||
quote! {
|
quote! {
|
||||||
ResponseBody { #(#fields),* }
|
ResponseBody { #(#fields),* }
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
|
quote!(ruma_api::exports::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.
|
||||||
pub fn newtype_body_field(&self) -> Option<&Field> {
|
pub 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)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Gets the newtype raw body field, if this response has one.
|
||||||
|
pub fn newtype_raw_body_field(&self) -> Option<&Field> {
|
||||||
|
self.fields.iter().find_map(ResponseField::as_newtype_raw_body_field)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TryFrom<RawResponse> for Response {
|
impl TryFrom<RawResponse> for Response {
|
||||||
@ -150,29 +172,34 @@ impl TryFrom<RawResponse> for Response {
|
|||||||
}
|
}
|
||||||
|
|
||||||
field_kind = Some(match meta {
|
field_kind = Some(match meta {
|
||||||
Meta::Word(ident) => {
|
Meta::Word(ident) => match &ident.to_string()[..] {
|
||||||
if ident != "body" {
|
s @ "body" | s @ "raw_body" => {
|
||||||
|
if let Some(f) = &newtype_body_field {
|
||||||
|
let mut error = syn::Error::new_spanned(
|
||||||
|
field,
|
||||||
|
"There can only be one newtype body field",
|
||||||
|
);
|
||||||
|
error.combine(syn::Error::new_spanned(
|
||||||
|
f,
|
||||||
|
"Previous newtype body field",
|
||||||
|
));
|
||||||
|
return Err(error);
|
||||||
|
}
|
||||||
|
|
||||||
|
newtype_body_field = Some(field.clone());
|
||||||
|
match s {
|
||||||
|
"body" => ResponseFieldKind::NewtypeBody,
|
||||||
|
"raw_body" => ResponseFieldKind::NewtypeRawBody,
|
||||||
|
_ => unreachable!(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
return Err(syn::Error::new_spanned(
|
return Err(syn::Error::new_spanned(
|
||||||
ident,
|
ident,
|
||||||
"Invalid #[ruma_api] argument with value, expected `body`",
|
"Invalid #[ruma_api] argument with value, expected `body`",
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
},
|
||||||
if let Some(f) = &newtype_body_field {
|
|
||||||
let mut error = syn::Error::new_spanned(
|
|
||||||
field,
|
|
||||||
"There can only be one newtype body field",
|
|
||||||
);
|
|
||||||
error.combine(syn::Error::new_spanned(
|
|
||||||
f,
|
|
||||||
"Previous newtype body field",
|
|
||||||
));
|
|
||||||
return Err(error);
|
|
||||||
}
|
|
||||||
|
|
||||||
newtype_body_field = Some(field.clone());
|
|
||||||
ResponseFieldKind::NewtypeBody
|
|
||||||
}
|
|
||||||
Meta::NameValue(MetaNameValue { name, value }) => {
|
Meta::NameValue(MetaNameValue { name, value }) => {
|
||||||
if name != "header" {
|
if name != "header" {
|
||||||
return Err(syn::Error::new_spanned(
|
return Err(syn::Error::new_spanned(
|
||||||
@ -193,6 +220,7 @@ impl TryFrom<RawResponse> for Response {
|
|||||||
ResponseField::Header(field, header.expect("missing header name"))
|
ResponseField::Header(field, header.expect("missing header name"))
|
||||||
}
|
}
|
||||||
ResponseFieldKind::NewtypeBody => ResponseField::NewtypeBody(field),
|
ResponseFieldKind::NewtypeBody => ResponseField::NewtypeBody(field),
|
||||||
|
ResponseFieldKind::NewtypeRawBody => ResponseField::NewtypeRawBody(field),
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
.collect::<syn::Result<Vec<_>>>()?;
|
.collect::<syn::Result<Vec<_>>>()?;
|
||||||
@ -220,7 +248,7 @@ impl ToTokens for Response {
|
|||||||
quote! { { #(#fields),* } }
|
quote! { { #(#fields),* } }
|
||||||
};
|
};
|
||||||
|
|
||||||
let (derive_deserialize, response_body_def) =
|
let response_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 body_field.has_wrap_incoming_attr() {
|
||||||
@ -229,7 +257,7 @@ impl ToTokens for Response {
|
|||||||
quote!(ruma_api::exports::serde::Deserialize)
|
quote!(ruma_api::exports::serde::Deserialize)
|
||||||
};
|
};
|
||||||
|
|
||||||
(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 = if fields.clone().any(|f| f.has_wrap_incoming_attr()) {
|
||||||
@ -239,24 +267,29 @@ impl ToTokens for Response {
|
|||||||
};
|
};
|
||||||
let fields = fields.map(ResponseField::field);
|
let fields = fields.map(ResponseField::field);
|
||||||
|
|
||||||
(derive_deserialize, quote!({ #(#fields),* }))
|
Some((derive_deserialize, quote!({ #(#fields),* })))
|
||||||
} else {
|
} else {
|
||||||
(quote!(ruma_api::exports::serde::Deserialize), quote!(;))
|
None
|
||||||
};
|
}
|
||||||
|
.map(|(derive_deserialize, def)| {
|
||||||
|
quote! {
|
||||||
|
/// Data in the response body.
|
||||||
|
#[derive(
|
||||||
|
Debug,
|
||||||
|
ruma_api::Outgoing,
|
||||||
|
ruma_api::exports::serde::Serialize,
|
||||||
|
#derive_deserialize
|
||||||
|
)]
|
||||||
|
struct ResponseBody #def
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
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_def
|
||||||
|
|
||||||
/// Data in the response body.
|
#response_body_struct
|
||||||
#[derive(
|
|
||||||
Debug,
|
|
||||||
ruma_api::Outgoing,
|
|
||||||
ruma_api::exports::serde::Serialize,
|
|
||||||
#derive_deserialize
|
|
||||||
)]
|
|
||||||
struct ResponseBody #response_body_def
|
|
||||||
};
|
};
|
||||||
|
|
||||||
response.to_tokens(tokens);
|
response.to_tokens(tokens);
|
||||||
@ -271,6 +304,8 @@ pub enum ResponseField {
|
|||||||
Header(Field, Ident),
|
Header(Field, Ident),
|
||||||
/// A specific data type in the body of the response.
|
/// A specific data type in the body of the response.
|
||||||
NewtypeBody(Field),
|
NewtypeBody(Field),
|
||||||
|
/// Arbitrary bytes in the body of the response.
|
||||||
|
NewtypeRawBody(Field),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ResponseField {
|
impl ResponseField {
|
||||||
@ -279,7 +314,8 @@ impl ResponseField {
|
|||||||
match self {
|
match self {
|
||||||
ResponseField::Body(field)
|
ResponseField::Body(field)
|
||||||
| ResponseField::Header(field, _)
|
| ResponseField::Header(field, _)
|
||||||
| ResponseField::NewtypeBody(field) => field,
|
| ResponseField::NewtypeBody(field)
|
||||||
|
| ResponseField::NewtypeRawBody(field) => field,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -317,6 +353,14 @@ impl ResponseField {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Return the contained field if this response field is a newtype raw body kind.
|
||||||
|
fn as_newtype_raw_body_field(&self) -> Option<&Field> {
|
||||||
|
match self {
|
||||||
|
ResponseField::NewtypeRawBody(field) => Some(field),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Whether or not the reponse field has a #[wrap_incoming] attribute.
|
/// Whether or not the reponse field has a #[wrap_incoming] attribute.
|
||||||
fn has_wrap_incoming_attr(&self) -> bool {
|
fn has_wrap_incoming_attr(&self) -> bool {
|
||||||
self.field().attrs.iter().any(|attr| {
|
self.field().attrs.iter().any(|attr| {
|
||||||
@ -333,4 +377,6 @@ enum ResponseFieldKind {
|
|||||||
Header,
|
Header,
|
||||||
/// See the similarly named variant of `ResponseField`.
|
/// See the similarly named variant of `ResponseField`.
|
||||||
NewtypeBody,
|
NewtypeBody,
|
||||||
|
/// See the similarly named variant of `ResponseField`.
|
||||||
|
NewtypeRawBody,
|
||||||
}
|
}
|
||||||
|
@ -198,11 +198,9 @@ use serde_urlencoded;
|
|||||||
///
|
///
|
||||||
/// ## Fallible deserialization
|
/// ## Fallible deserialization
|
||||||
///
|
///
|
||||||
/// All request and response types also derive [`Outgoing`][]. As such, to allow fallible
|
/// All request and response types also derive [`Outgoing`][Outgoing]. As such, to allow fallible
|
||||||
/// deserialization, you can use the `#[wrap_incoming]` attribute. For details, see the
|
/// deserialization, you can use the `#[wrap_incoming]` attribute. For details, see the
|
||||||
/// documentation for [`Outgoing`][].
|
/// documentation for [the derive macro](derive.Outgoing.html).
|
||||||
///
|
|
||||||
/// [`Outgoing`]: derive.Outgoing.html
|
|
||||||
// TODO: Explain the concept of fallible deserialization before jumping to `ruma_api::Outgoing`
|
// TODO: Explain the concept of fallible deserialization before jumping to `ruma_api::Outgoing`
|
||||||
#[cfg(feature = "with-ruma-api-macros")]
|
#[cfg(feature = "with-ruma-api-macros")]
|
||||||
pub use ruma_api_macros::ruma_api;
|
pub use ruma_api_macros::ruma_api;
|
||||||
|
@ -86,12 +86,42 @@ pub mod newtype_body_endpoint {
|
|||||||
|
|
||||||
request {
|
request {
|
||||||
#[ruma_api(body)]
|
#[ruma_api(body)]
|
||||||
pub file: Vec<u8>,
|
pub list_of_custom_things: Vec<MyCustomType>,
|
||||||
}
|
}
|
||||||
|
|
||||||
response {
|
response {
|
||||||
#[ruma_api(body)]
|
#[ruma_api(body)]
|
||||||
pub my_custom_type: MyCustomType,
|
pub my_custom_thing: MyCustomType,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub mod newtype_raw_body_endpoint {
|
||||||
|
use ruma_api_macros::ruma_api;
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)]
|
||||||
|
pub struct MyCustomType {
|
||||||
|
pub foo: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
ruma_api! {
|
||||||
|
metadata {
|
||||||
|
description: "Does something.",
|
||||||
|
method: PUT,
|
||||||
|
name: "newtype_body_endpoint",
|
||||||
|
path: "/_matrix/some/newtype/body/endpoint",
|
||||||
|
rate_limited: false,
|
||||||
|
requires_authentication: false,
|
||||||
|
}
|
||||||
|
|
||||||
|
request {
|
||||||
|
#[ruma_api(raw_body)]
|
||||||
|
pub file: Vec<u8>,
|
||||||
|
}
|
||||||
|
|
||||||
|
response {
|
||||||
|
#[ruma_api(raw_body)]
|
||||||
|
pub file: Vec<u8>,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user