Specify content traits in attribute, remove derives

This commit is contained in:
Devin Ragotzy 2021-05-13 15:35:26 -04:00 committed by Jonas Platte
parent 6c167fca38
commit c7c1251f3f
3 changed files with 94 additions and 150 deletions

View File

@ -7,11 +7,15 @@ use syn::{
DeriveInput, Ident, LitStr, Token, DeriveInput, Ident, LitStr, Token,
}; };
use crate::event_parse::EventKind;
mod kw { mod kw {
// This `content` field is kept when the event is redacted. // This `content` field is kept when the event is redacted.
syn::custom_keyword!(skip_redaction); syn::custom_keyword!(skip_redaction);
// Do not emit any redacted event code. // Do not emit any redacted event code.
syn::custom_keyword!(custom_redacted); syn::custom_keyword!(custom_redacted);
// The kind of event content this is.
syn::custom_keyword!(kind);
} }
/// Parses attributes for `*EventContent` derives. /// Parses attributes for `*EventContent` derives.
@ -21,6 +25,8 @@ enum EventMeta {
/// Variant holds the "m.whatever" event type. /// Variant holds the "m.whatever" event type.
Type(LitStr), Type(LitStr),
Kind(EventKind),
/// Fields marked with `#[ruma_event(skip_redaction)]` are kept when the event is /// Fields marked with `#[ruma_event(skip_redaction)]` are kept when the event is
/// redacted. /// redacted.
SkipRedacted, SkipRedacted,
@ -38,6 +44,14 @@ impl EventMeta {
None None
} }
} }
fn get_event_kind(&self) -> Option<&EventKind> {
if let Self::Kind(lit) = self {
Some(lit)
} else {
None
}
}
} }
impl Parse for EventMeta { impl Parse for EventMeta {
@ -47,6 +61,10 @@ impl Parse for EventMeta {
let _: Token![type] = input.parse()?; let _: Token![type] = input.parse()?;
let _: Token![=] = input.parse()?; let _: Token![=] = input.parse()?;
input.parse().map(EventMeta::Type) input.parse().map(EventMeta::Type)
} else if lookahead.peek(kw::kind) {
let _: kw::kind = input.parse()?;
let _: Token![=] = input.parse()?;
EventKind::parse(input).map(EventMeta::Kind)
} else if lookahead.peek(kw::skip_redaction) { } else if lookahead.peek(kw::skip_redaction) {
let _: kw::skip_redaction = input.parse()?; let _: kw::skip_redaction = input.parse()?;
Ok(EventMeta::SkipRedacted) Ok(EventMeta::SkipRedacted)
@ -69,6 +87,10 @@ impl MetaAttrs {
fn get_event_type(&self) -> Option<&LitStr> { fn get_event_type(&self) -> Option<&LitStr> {
self.0.iter().find_map(|a| a.get_event_type()) self.0.iter().find_map(|a| a.get_event_type())
} }
fn get_event_kinds(&self) -> Vec<&EventKind> {
self.0.iter().filter_map(|a| a.get_event_kind()).collect()
}
} }
impl Parse for MetaAttrs { impl Parse for MetaAttrs {
@ -81,7 +103,6 @@ impl Parse for MetaAttrs {
/// Create an `EventContent` implementation for a struct. /// Create an `EventContent` implementation for a struct.
pub fn expand_event_content( pub fn expand_event_content(
input: &DeriveInput, input: &DeriveInput,
emit_redacted: bool,
ruma_events: &TokenStream, ruma_events: &TokenStream,
) -> syn::Result<TokenStream> { ) -> syn::Result<TokenStream> {
let ruma_identifiers = quote! { #ruma_events::exports::ruma_identifiers }; let ruma_identifiers = quote! { #ruma_events::exports::ruma_identifiers };
@ -99,13 +120,16 @@ pub fn expand_event_content(
let event_type = content_attr.iter().find_map(|a| a.get_event_type()).ok_or_else(|| { let event_type = content_attr.iter().find_map(|a| a.get_event_type()).ok_or_else(|| {
let msg = "no event type attribute found, \ let msg = "no event type attribute found, \
add `#[ruma_event(type = \"any.room.event\")]` \ add `#[ruma_event(type = \"any.room.event\", kind = Kind)]` \
below the event content derive"; below the event content derive";
syn::Error::new(Span::call_site(), msg) syn::Error::new(Span::call_site(), msg)
})?; })?;
let redacted = if emit_redacted && needs_redacted(&content_attr) { let content_derives =
content_attr.iter().flat_map(|args| args.get_event_kinds()).collect::<Vec<_>>();
let redacted = if needs_redacted(&content_attr) {
let doc = format!("The payload for a redacted `{}`", ident); let doc = format!("The payload for a redacted `{}`", ident);
let redacted_ident = format_ident!("Redacted{}", ident); let redacted_ident = format_ident!("Redacted{}", ident);
let kept_redacted_fields = if let syn::Data::Struct(syn::DataStruct { let kept_redacted_fields = if let syn::Data::Struct(syn::DataStruct {
@ -182,6 +206,21 @@ pub fn expand_event_content(
let redacted_event_content = let redacted_event_content =
generate_event_content_impl(&redacted_ident, event_type, ruma_events); generate_event_content_impl(&redacted_ident, event_type, ruma_events);
let redacted_event_content_derive = content_derives
.iter()
.map(|kind| match kind {
EventKind::Message => quote! {
#[automatically_derived]
impl #ruma_events::RedactedMessageEventContent for #redacted_ident {}
},
EventKind::State => quote! {
#[automatically_derived]
impl #ruma_events::RedactedStateEventContent for #redacted_ident {}
},
_ => TokenStream::new(),
})
.collect::<TokenStream>();
quote! { quote! {
// this is the non redacted event content's impl // this is the non redacted event content's impl
#[automatically_derived] #[automatically_derived]
@ -220,106 +259,68 @@ pub fn expand_event_content(
#has_deserialize_fields #has_deserialize_fields
} }
} }
#redacted_event_content_derive
} }
} else { } else {
TokenStream::new() TokenStream::new()
}; };
let event_content_derive =
generate_event_content_derives(&content_derives, ident, ruma_events)?;
let event_content = generate_event_content_impl(ident, event_type, ruma_events); let event_content = generate_event_content_impl(ident, event_type, ruma_events);
Ok(quote! { Ok(quote! {
#event_content #event_content
#event_content_derive
#redacted #redacted
}) })
} }
/// Create a `EphemeralRoomEventContent` implementation for a struct fn generate_event_content_derives(
pub fn expand_ephemeral_room_event_content( content_attr: &[&EventKind],
input: &DeriveInput, ident: &Ident,
ruma_events: &TokenStream, ruma_events: &TokenStream,
) -> syn::Result<TokenStream> { ) -> syn::Result<TokenStream> {
let ident = input.ident.clone(); let msg = "valid event kinds are GlobalAccountData, RoomAccountData, \
let event_content_impl = expand_event_content(input, false, ruma_events)?; EphemeralRoom, Message, State, ToDevice";
content_attr
Ok(quote! { .iter()
#event_content_impl .map(|kind| {
Ok(match kind {
#[automatically_derived] EventKind::GlobalAccountData => quote! {
impl #ruma_events::EphemeralRoomEventContent for #ident {} // TODO: will this be it's own trait at some point?
}) },
} EventKind::RoomAccountData => quote! {
// TODO: will this be it's own trait at some point?
/// Create a `RoomEventContent` implementation for a struct. },
pub fn expand_room_event_content( EventKind::Ephemeral => quote! {
input: &DeriveInput, #[automatically_derived]
ruma_events: &TokenStream, impl #ruma_events::EphemeralRoomEventContent for #ident {}
) -> syn::Result<TokenStream> { },
let ident = input.ident.clone(); EventKind::Message => quote! {
let event_content_impl = expand_event_content(input, true, ruma_events)?; #[automatically_derived]
impl #ruma_events::RoomEventContent for #ident {}
Ok(quote! { #[automatically_derived]
#event_content_impl impl #ruma_events::MessageEventContent for #ident {}
},
#[automatically_derived] EventKind::State => quote! {
impl #ruma_events::RoomEventContent for #ident {} #[automatically_derived]
}) impl #ruma_events::RoomEventContent for #ident {}
} #[automatically_derived]
impl #ruma_events::StateEventContent for #ident {}
/// Create a `MessageEventContent` implementation for a struct },
pub fn expand_message_event_content( EventKind::ToDevice => quote! {
input: &DeriveInput, // TODO: will this be it's own trait at some point?
ruma_events: &TokenStream, },
) -> syn::Result<TokenStream> { EventKind::Redaction => return Err(syn::Error::new(ident.span(), msg)),
let ident = input.ident.clone(); EventKind::Presence => return Err(syn::Error::new(ident.span(), msg)),
let room_ev_content = expand_room_event_content(input, ruma_events)?; })
})
let redacted_marker_trait = if needs_redacted_from_input(input) { .collect()
let ident = format_ident!("Redacted{}", &ident);
quote! {
#[automatically_derived]
impl #ruma_events::RedactedMessageEventContent for #ident {}
}
} else {
TokenStream::new()
};
Ok(quote! {
#room_ev_content
#[automatically_derived]
impl #ruma_events::MessageEventContent for #ident {}
#redacted_marker_trait
})
}
/// Create a `StateEventContent` implementation for a struct
pub fn expand_state_event_content(
input: &DeriveInput,
ruma_events: &TokenStream,
) -> syn::Result<TokenStream> {
let ident = input.ident.clone();
let room_ev_content = expand_room_event_content(input, ruma_events)?;
let redacted_marker_trait = if needs_redacted_from_input(input) {
let ident = format_ident!("Redacted{}", input.ident);
quote! {
#[automatically_derived]
impl #ruma_events::RedactedStateEventContent for #ident {}
}
} else {
TokenStream::new()
};
Ok(quote! {
#room_ev_content
#[automatically_derived]
impl #ruma_events::StateEventContent for #ident {}
#redacted_marker_trait
})
} }
fn generate_event_content_impl( fn generate_event_content_impl(
@ -359,7 +360,3 @@ fn needs_redacted(input: &[MetaAttrs]) -> bool {
// needs a redacted struct generated. // needs a redacted struct generated.
!input.iter().any(|a| a.is_custom()) !input.iter().any(|a| a.is_custom())
} }
fn needs_redacted_from_input(input: &DeriveInput) -> bool {
!input.attrs.iter().flat_map(|a| a.parse_args::<MetaAttrs>().ok()).any(|a| a.is_custom())
}

View File

@ -15,12 +15,7 @@ use quote::quote;
use syn::{parse_macro_input, DeriveInput, Ident}; use syn::{parse_macro_input, DeriveInput, Ident};
use self::{ use self::{
event::expand_event, event::expand_event, event_content::expand_event_content, event_enum::expand_event_enum,
event_content::{
expand_ephemeral_room_event_content, expand_event_content, expand_message_event_content,
expand_room_event_content, expand_state_event_content,
},
event_enum::expand_event_enum,
event_parse::EventEnumInput, event_parse::EventEnumInput,
}; };
@ -58,53 +53,7 @@ pub fn derive_event_content(input: TokenStream) -> TokenStream {
let ruma_events = import_ruma_events(); let ruma_events = import_ruma_events();
let input = parse_macro_input!(input as DeriveInput); let input = parse_macro_input!(input as DeriveInput);
expand_event_content(&input, true, &ruma_events) expand_event_content(&input, &ruma_events).unwrap_or_else(syn::Error::into_compile_error).into()
.unwrap_or_else(syn::Error::into_compile_error)
.into()
}
/// Generates an implementation of `ruma_events::RoomEventContent` and it's super traits.
#[proc_macro_derive(RoomEventContent, attributes(ruma_event))]
pub fn derive_room_event_content(input: TokenStream) -> TokenStream {
let ruma_events = import_ruma_events();
let input = parse_macro_input!(input as DeriveInput);
expand_room_event_content(&input, &ruma_events)
.unwrap_or_else(syn::Error::into_compile_error)
.into()
}
/// Generates an implementation of `ruma_events::MessageEventContent` and it's super traits.
#[proc_macro_derive(MessageEventContent, attributes(ruma_event))]
pub fn derive_message_event_content(input: TokenStream) -> TokenStream {
let ruma_events = import_ruma_events();
let input = parse_macro_input!(input as DeriveInput);
expand_message_event_content(&input, &ruma_events)
.unwrap_or_else(syn::Error::into_compile_error)
.into()
}
/// Generates an implementation of `ruma_events::StateEventContent` and it's super traits.
#[proc_macro_derive(StateEventContent, attributes(ruma_event))]
pub fn derive_state_event_content(input: TokenStream) -> TokenStream {
let ruma_events = import_ruma_events();
let input = parse_macro_input!(input as DeriveInput);
expand_state_event_content(&input, &ruma_events)
.unwrap_or_else(syn::Error::into_compile_error)
.into()
}
/// Generates an implementation of `ruma_events::EphemeralRoomEventContent` and it's super traits.
#[proc_macro_derive(EphemeralRoomEventContent, attributes(ruma_event))]
pub fn derive_ephemeral_room_event_content(input: TokenStream) -> TokenStream {
let ruma_events = import_ruma_events();
let input = parse_macro_input!(input as DeriveInput);
expand_ephemeral_room_event_content(&input, &ruma_events)
.unwrap_or_else(syn::Error::into_compile_error)
.into()
} }
/// Generates implementations needed to serialize and deserialize Matrix events. /// Generates implementations needed to serialize and deserialize Matrix events.

View File

@ -36,7 +36,7 @@
//! would work. //! would work.
//! //!
//! ```rust //! ```rust
//! use ruma_events::{macros::MessageEventContent, SyncMessageEvent}; //! use ruma_events::{macros::EventContent, SyncMessageEvent};
//! use ruma_identifiers::EventId; //! use ruma_identifiers::EventId;
//! use serde::{Deserialize, Serialize}; //! use serde::{Deserialize, Serialize};
//! //!
@ -58,8 +58,8 @@
//! } //! }
//! //!
//! /// The payload for our reaction event. //! /// The payload for our reaction event.
//! #[derive(Clone, Debug, Deserialize, Serialize, MessageEventContent)] //! #[derive(Clone, Debug, Deserialize, Serialize, EventContent)]
//! #[ruma_event(type = "m.reaction")] //! #[ruma_event(type = "m.reaction", kind = Message)]
//! pub struct ReactionEventContent { //! pub struct ReactionEventContent {
//! #[serde(rename = "m.relates_to")] //! #[serde(rename = "m.relates_to")]
//! pub relates_to: RelatesTo, //! pub relates_to: RelatesTo,
@ -151,9 +151,7 @@ pub mod exports {
/// Re-export of all the derives needed to create your own event types. /// Re-export of all the derives needed to create your own event types.
pub mod macros { pub mod macros {
pub use ruma_events_macros::{ pub use ruma_events_macros::{Event, EventContent};
EphemeralRoomEventContent, Event, MessageEventContent, StateEventContent,
};
} }
pub mod call; pub mod call;