From 30a0a39d2b2dd2f319bd69ae0e8b7cd40602a614 Mon Sep 17 00:00:00 2001 From: Jonas Platte Date: Mon, 17 Aug 2020 22:08:56 +0200 Subject: [PATCH] Outgoing derive: Replace incoming_no_deserialize with incoming_derive(!Deserialize) --- ruma-api-macros/src/api/request.rs | 2 +- ruma-api-macros/src/api/response.rs | 2 +- ruma-api-macros/src/derive_outgoing.rs | 83 +++++++++++++------ ruma-api-macros/src/lib.rs | 2 +- ruma-api/tests/outgoing.rs | 4 +- .../src/r0/message/send_message_event.rs | 4 +- .../state/send_state_event_for_empty_key.rs | 4 +- .../src/r0/state/send_state_event_for_key.rs | 4 +- 8 files changed, 70 insertions(+), 35 deletions(-) diff --git a/ruma-api-macros/src/api/request.rs b/ruma-api-macros/src/api/request.rs index 913d58bd..545b4058 100644 --- a/ruma-api-macros/src/api/request.rs +++ b/ruma-api-macros/src/api/request.rs @@ -494,7 +494,7 @@ impl ToTokens for Request { let request = quote! { #[derive(Debug, Clone, #import_path::Outgoing)] - #[incoming_no_deserialize] + #[incoming_derive(!Deserialize)] #( #struct_attributes )* pub struct Request #request_generics #request_def diff --git a/ruma-api-macros/src/api/response.rs b/ruma-api-macros/src/api/response.rs index 913b8d9c..99568ab8 100644 --- a/ruma-api-macros/src/api/response.rs +++ b/ruma-api-macros/src/api/response.rs @@ -283,7 +283,7 @@ impl ToTokens for Response { let response = quote! { #[derive(Debug, Clone, #import_path::Outgoing)] - #[incoming_no_deserialize] + #[incoming_derive(!Deserialize)] #( #struct_attributes )* pub struct Response #response_def diff --git a/ruma-api-macros/src/derive_outgoing.rs b/ruma-api-macros/src/derive_outgoing.rs index 2ed6e30c..2df1ff1a 100644 --- a/ruma-api-macros/src/derive_outgoing.rs +++ b/ruma-api-macros/src/derive_outgoing.rs @@ -1,10 +1,12 @@ use proc_macro2::{Span, TokenStream}; use quote::{format_ident, quote}; use syn::{ - parse_quote, AngleBracketedGenericArguments, Attribute, Data, DeriveInput, Field, Fields, - GenericArgument, GenericParam, Generics, ImplGenerics, Meta, MetaList, - ParenthesizedGenericArguments, PathArguments, Type, TypeGenerics, TypePath, TypeReference, - TypeSlice, Variant, + parse::{Parse, ParseStream}, + parse_quote, + punctuated::Punctuated, + AngleBracketedGenericArguments, Attribute, Data, DeriveInput, Field, Fields, GenericArgument, + GenericParam, Generics, Ident, ImplGenerics, ParenthesizedGenericArguments, PathArguments, + Token, Type, TypeGenerics, TypePath, TypeReference, TypeSlice, Variant, }; use crate::util::import_ruma_api; @@ -24,29 +26,28 @@ pub fn expand_derive_outgoing(input: DeriveInput) -> syn::Result { let import_path = import_ruma_api(); let mut derives = vec![quote! { Debug }]; - if !no_deserialize_in_attrs(&input.attrs) { - derives.push(quote! { #import_path::exports::serde::Deserialize }); - } + let mut derive_deserialize = true; + derives.extend( input .attrs .iter() .filter(|attr| attr.path.is_ident("incoming_derive")) - .map(|attr| { - let meta = attr.parse_meta()?; - match meta { - Meta::List(MetaList { nested, .. }) => Ok(nested), - _ => Err(syn::Error::new_spanned( - meta, - "incoming_derive should be used as `#[incoming_derive(A, B, C)]`", - )), - } - }) - .collect::>>()? + .map(|attr| attr.parse_args()) + .collect::>>()? .into_iter() - .flatten() - .map(|derive_mac| quote! { #derive_mac }), + .flat_map(|meta| meta.derive_macs) + .filter_map(|derive_mac| match derive_mac { + DeriveMac::Regular(id) => Some(quote! { #id }), + DeriveMac::NegativeDeserialize => { + derive_deserialize = false; + None + } + }), ); + if derive_deserialize { + derives.push(quote! { #import_path::exports::serde::Deserialize }); + } let input_attrs = input.attrs.iter().filter(|attr| filter_input_attrs(attr)).collect::>(); @@ -149,10 +150,6 @@ fn filter_input_attrs(attr: &Attribute) -> bool { attr.path.is_ident("serde") || attr.path.is_ident("non_exhaustive") } -fn no_deserialize_in_attrs(attrs: &[Attribute]) -> bool { - attrs.iter().any(|attr| attr.path.is_ident("incoming_no_deserialize")) -} - fn impl_outgoing_with_incoming_self(input: &DeriveInput, import_path: &TokenStream) -> TokenStream { let ident = &input.ident; let (impl_gen, ty_gen, _) = input.generics.split_for_impl(); @@ -283,3 +280,41 @@ fn strip_lifetimes(field_type: &mut Type) -> bool { _ => false, } } + +pub struct Meta { + derive_macs: Vec, +} + +impl Parse for Meta { + fn parse(input: ParseStream) -> syn::Result { + Ok(Self { + derive_macs: Punctuated::<_, Token![,]>::parse_terminated(input)?.into_iter().collect(), + }) + } +} + +pub enum DeriveMac { + Regular(Ident), + NegativeDeserialize, +} + +impl Parse for DeriveMac { + fn parse(input: ParseStream) -> syn::Result { + if input.peek(Token![!]) { + let _: Token![!] = input.parse()?; + let mac: Ident = input.parse()?; + + if mac != "Deserialize" { + return Err(syn::Error::new_spanned( + mac, + "Negative incoming_derive can only be used for Deserialize", + )); + } + + Ok(Self::NegativeDeserialize) + } else { + let mac = input.parse()?; + Ok(Self::Regular(mac)) + } + } +} diff --git a/ruma-api-macros/src/lib.rs b/ruma-api-macros/src/lib.rs index 02a120e1..f83be65d 100644 --- a/ruma-api-macros/src/lib.rs +++ b/ruma-api-macros/src/lib.rs @@ -76,7 +76,7 @@ struct IncomingMyType { ``` */ -#[proc_macro_derive(Outgoing, attributes(incoming_derive, incoming_no_deserialize))] +#[proc_macro_derive(Outgoing, attributes(incoming_derive))] pub fn derive_outgoing(input: TokenStream) -> TokenStream { let input = parse_macro_input!(input as DeriveInput); expand_derive_outgoing(input).unwrap_or_else(|err| err.to_compile_error()).into() diff --git a/ruma-api/tests/outgoing.rs b/ruma-api/tests/outgoing.rs index 94699b0c..ff4b4c5a 100644 --- a/ruma-api/tests/outgoing.rs +++ b/ruma-api/tests/outgoing.rs @@ -21,7 +21,7 @@ pub struct OtherThing<'t> { } #[derive(Outgoing)] -#[incoming_no_deserialize] +#[incoming_derive(!Deserialize)] pub struct FakeRequest<'a, T> { pub abc: &'a str, pub thing: Thing<'a, T>, @@ -35,7 +35,7 @@ pub struct FakeRequest<'a, T> { } #[derive(Outgoing)] -#[incoming_no_deserialize] +#[incoming_derive(!Deserialize)] pub enum EnumThing<'a, T> { Abc(&'a str), Stuff(Thing<'a, T>), diff --git a/ruma-client-api/src/r0/message/send_message_event.rs b/ruma-client-api/src/r0/message/send_message_event.rs index 14b1a595..84a6e854 100644 --- a/ruma-client-api/src/r0/message/send_message_event.rs +++ b/ruma-client-api/src/r0/message/send_message_event.rs @@ -19,7 +19,7 @@ use serde_json::value::RawValue as RawJsonValue; /// Send a message event to a room. #[derive(Clone, Debug, Outgoing)] #[non_exhaustive] -#[incoming_no_deserialize] +#[incoming_derive(!Deserialize)] pub struct Request<'a> { /// The room to send the event to. pub room_id: &'a RoomId, @@ -45,7 +45,7 @@ impl<'a> Request<'a> { /// Data in the response from the `send_message_event` API endpoint. #[derive(Clone, Debug, Outgoing)] #[non_exhaustive] -#[incoming_no_deserialize] +#[incoming_derive(!Deserialize)] pub struct Response { /// A unique identifier for the event. pub event_id: EventId, diff --git a/ruma-client-api/src/r0/state/send_state_event_for_empty_key.rs b/ruma-client-api/src/r0/state/send_state_event_for_empty_key.rs index 1fb87852..d6d24c93 100644 --- a/ruma-client-api/src/r0/state/send_state_event_for_empty_key.rs +++ b/ruma-client-api/src/r0/state/send_state_event_for_empty_key.rs @@ -19,7 +19,7 @@ use serde_json::value::RawValue as RawJsonValue; /// Send a state event to a room associated with the empty state key. #[derive(Clone, Debug, Outgoing)] #[non_exhaustive] -#[incoming_no_deserialize] +#[incoming_derive(!Deserialize)] pub struct Request<'a> { /// The room to set the state in. pub room_id: &'a RoomId, @@ -38,7 +38,7 @@ impl<'a> Request<'a> { /// Data in the response from the `send_state_event_for_empty_key` API endpoint. #[derive(Clone, Debug, Outgoing)] #[non_exhaustive] -#[incoming_no_deserialize] +#[incoming_derive(!Deserialize)] pub struct Response { /// A unique identifier for the event. pub event_id: EventId, diff --git a/ruma-client-api/src/r0/state/send_state_event_for_key.rs b/ruma-client-api/src/r0/state/send_state_event_for_key.rs index d93df602..d6c4726f 100644 --- a/ruma-client-api/src/r0/state/send_state_event_for_key.rs +++ b/ruma-client-api/src/r0/state/send_state_event_for_key.rs @@ -19,7 +19,7 @@ use serde_json::value::RawValue as RawJsonValue; /// Send a state event to a room associated with a given state key. #[derive(Clone, Debug, Outgoing)] #[non_exhaustive] -#[incoming_no_deserialize] +#[incoming_derive(!Deserialize)] pub struct Request<'a> { /// The room to set the state in. pub room_id: &'a RoomId, @@ -41,7 +41,7 @@ impl<'a> Request<'a> { /// Data in the response from the `send_message_event` API endpoint. #[derive(Clone, Debug, Outgoing)] #[non_exhaustive] -#[incoming_no_deserialize] +#[incoming_derive(!Deserialize)] pub struct Response { /// A unique identifier for the event. pub event_id: EventId,