api: Add manual_body_serde derive attribute for responses

This commit is contained in:
Jonas Platte 2022-01-28 23:52:25 +01:00
parent 82becb86c8
commit e0d7ea3ed1
No known key found for this signature in database
GPG Key ID: 7D261D771D915378
2 changed files with 25 additions and 13 deletions

View File

@ -43,15 +43,15 @@ impl<V: Parse> Parse for MetaNameValue<V> {
} }
/// Like syn::Meta, but only parses ruma_api attributes /// Like syn::Meta, but only parses ruma_api attributes
pub enum Meta { pub enum Meta<T> {
/// A single word, like `query` in `#[ruma_api(query)]` /// A single word, like `query` in `#[ruma_api(query)]`
Word(Ident), Word(Ident),
/// A name-value pair, like `header = CONTENT_TYPE` in `#[ruma_api(header = CONTENT_TYPE)]` /// A name-value pair, like `header = CONTENT_TYPE` in `#[ruma_api(header = CONTENT_TYPE)]`
NameValue(MetaNameValue<Ident>), NameValue(MetaNameValue<T>),
} }
impl Meta { impl<T: Parse> Meta<T> {
/// Check if the given attribute is a ruma_api attribute. /// Check if the given attribute is a ruma_api attribute.
/// ///
/// If it is, parse it. /// If it is, parse it.
@ -64,7 +64,7 @@ impl Meta {
} }
} }
impl Parse for Meta { impl<T: Parse> Parse for Meta<T> {
fn parse(input: ParseStream<'_>) -> syn::Result<Self> { fn parse(input: ParseStream<'_>) -> syn::Result<Self> {
let ident = input.parse()?; let ident = input.parse()?;

View File

@ -1,6 +1,7 @@
use std::{ use std::{
convert::{TryFrom, TryInto}, convert::{TryFrom, TryInto},
mem, mem,
ops::Not,
}; };
use proc_macro2::TokenStream; use proc_macro2::TokenStream;
@ -13,7 +14,7 @@ use syn::{
}; };
use crate::{ use crate::{
attribute::{Meta, MetaNameValue, MetaValue}, attribute::{Meta, MetaNameValue},
util, util,
}; };
@ -27,19 +28,25 @@ pub fn expand_derive_response(input: DeriveInput) -> syn::Result<TokenStream> {
}; };
let fields = fields.into_iter().map(ResponseField::try_from).collect::<syn::Result<_>>()?; let fields = fields.into_iter().map(ResponseField::try_from).collect::<syn::Result<_>>()?;
let mut manual_body_serde = false;
let mut error_ty = None; let mut error_ty = None;
for attr in input.attrs { for attr in input.attrs {
if !attr.path.is_ident("ruma_api") { if !attr.path.is_ident("ruma_api") {
continue; continue;
} }
let meta = attr.parse_args_with(Punctuated::<_, Token![,]>::parse_terminated)?; let metas = attr.parse_args_with(Punctuated::<Meta<Type>, Token![,]>::parse_terminated)?;
for MetaNameValue { name, value } in meta { for meta in metas {
match value { match meta {
MetaValue::Type(t) if name == "error_ty" => { Meta::Word(w) if w == "manual_body_serde" => {
error_ty = Some(t); manual_body_serde = true;
}
Meta::NameValue(MetaNameValue { name, value }) if name == "error_ty" => {
error_ty = Some(value);
}
Meta::Word(name) | Meta::NameValue(MetaNameValue { name, .. }) => {
unreachable!("invalid ruma_api({}) attribute", name);
} }
_ => unreachable!("invalid ruma_api({}) attribute", name),
} }
} }
} }
@ -48,6 +55,7 @@ pub fn expand_derive_response(input: DeriveInput) -> syn::Result<TokenStream> {
ident: input.ident, ident: input.ident,
generics: input.generics, generics: input.generics,
fields, fields,
manual_body_serde,
error_ty: error_ty.unwrap(), error_ty: error_ty.unwrap(),
}; };
@ -59,6 +67,7 @@ struct Response {
ident: Ident, ident: Ident,
generics: Generics, generics: Generics,
fields: Vec<ResponseField>, fields: Vec<ResponseField>,
manual_body_serde: bool,
error_ty: Type, error_ty: Type,
} }
@ -92,6 +101,10 @@ impl Response {
let serde = quote! { #ruma_api::exports::serde }; let serde = quote! { #ruma_api::exports::serde };
let response_body_struct = (!self.has_raw_body()).then(|| { let response_body_struct = (!self.has_raw_body()).then(|| {
let serde_derives = self.manual_body_serde.not().then(|| {
quote! { #serde::Deserialize, #serde::Serialize }
});
let serde_attr = self.has_newtype_body().then(|| quote! { #[serde(transparent)] }); let serde_attr = self.has_newtype_body().then(|| quote! { #[serde(transparent)] });
let fields = self.fields.iter().filter_map(ResponseField::as_body_field); let fields = self.fields.iter().filter_map(ResponseField::as_body_field);
@ -101,8 +114,7 @@ impl Response {
Debug, Debug,
#ruma_api_macros::_FakeDeriveRumaApi, #ruma_api_macros::_FakeDeriveRumaApi,
#ruma_serde::Outgoing, #ruma_serde::Outgoing,
#serde::Deserialize, #serde_derives
#serde::Serialize,
)] )]
#serde_attr #serde_attr
struct ResponseBody { #(#fields),* } struct ResponseBody { #(#fields),* }