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,
};
use crate::event_parse::EventKind;
mod kw {
// This `content` field is kept when the event is redacted.
syn::custom_keyword!(skip_redaction);
// Do not emit any redacted event code.
syn::custom_keyword!(custom_redacted);
// The kind of event content this is.
syn::custom_keyword!(kind);
}
/// Parses attributes for `*EventContent` derives.
@ -21,6 +25,8 @@ enum EventMeta {
/// Variant holds the "m.whatever" event type.
Type(LitStr),
Kind(EventKind),
/// Fields marked with `#[ruma_event(skip_redaction)]` are kept when the event is
/// redacted.
SkipRedacted,
@ -38,6 +44,14 @@ impl EventMeta {
None
}
}
fn get_event_kind(&self) -> Option<&EventKind> {
if let Self::Kind(lit) = self {
Some(lit)
} else {
None
}
}
}
impl Parse for EventMeta {
@ -47,6 +61,10 @@ impl Parse for EventMeta {
let _: Token![type] = input.parse()?;
let _: Token![=] = input.parse()?;
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) {
let _: kw::skip_redaction = input.parse()?;
Ok(EventMeta::SkipRedacted)
@ -69,6 +87,10 @@ impl MetaAttrs {
fn get_event_type(&self) -> Option<&LitStr> {
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 {
@ -81,7 +103,6 @@ impl Parse for MetaAttrs {
/// Create an `EventContent` implementation for a struct.
pub fn expand_event_content(
input: &DeriveInput,
emit_redacted: bool,
ruma_events: &TokenStream,
) -> syn::Result<TokenStream> {
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 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";
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 redacted_ident = format_ident!("Redacted{}", ident);
let kept_redacted_fields = if let syn::Data::Struct(syn::DataStruct {
@ -182,6 +206,21 @@ pub fn expand_event_content(
let redacted_event_content =
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! {
// this is the non redacted event content's impl
#[automatically_derived]
@ -220,106 +259,68 @@ pub fn expand_event_content(
#has_deserialize_fields
}
}
#redacted_event_content_derive
}
} else {
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);
Ok(quote! {
#event_content
#event_content_derive
#redacted
})
}
/// Create a `EphemeralRoomEventContent` implementation for a struct
pub fn expand_ephemeral_room_event_content(
input: &DeriveInput,
fn generate_event_content_derives(
content_attr: &[&EventKind],
ident: &Ident,
ruma_events: &TokenStream,
) -> syn::Result<TokenStream> {
let ident = input.ident.clone();
let event_content_impl = expand_event_content(input, false, ruma_events)?;
Ok(quote! {
#event_content_impl
#[automatically_derived]
impl #ruma_events::EphemeralRoomEventContent for #ident {}
})
}
/// Create a `RoomEventContent` implementation for a struct.
pub fn expand_room_event_content(
input: &DeriveInput,
ruma_events: &TokenStream,
) -> syn::Result<TokenStream> {
let ident = input.ident.clone();
let event_content_impl = expand_event_content(input, true, ruma_events)?;
Ok(quote! {
#event_content_impl
#[automatically_derived]
impl #ruma_events::RoomEventContent for #ident {}
})
}
/// Create a `MessageEventContent` implementation for a struct
pub fn expand_message_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{}", &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
})
let msg = "valid event kinds are GlobalAccountData, RoomAccountData, \
EphemeralRoom, Message, State, ToDevice";
content_attr
.iter()
.map(|kind| {
Ok(match kind {
EventKind::GlobalAccountData => quote! {
// 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?
},
EventKind::Ephemeral => quote! {
#[automatically_derived]
impl #ruma_events::EphemeralRoomEventContent for #ident {}
},
EventKind::Message => quote! {
#[automatically_derived]
impl #ruma_events::RoomEventContent for #ident {}
#[automatically_derived]
impl #ruma_events::MessageEventContent for #ident {}
},
EventKind::State => quote! {
#[automatically_derived]
impl #ruma_events::RoomEventContent for #ident {}
#[automatically_derived]
impl #ruma_events::StateEventContent for #ident {}
},
EventKind::ToDevice => quote! {
// TODO: will this be it's own trait at some point?
},
EventKind::Redaction => return Err(syn::Error::new(ident.span(), msg)),
EventKind::Presence => return Err(syn::Error::new(ident.span(), msg)),
})
})
.collect()
}
fn generate_event_content_impl(
@ -359,7 +360,3 @@ fn needs_redacted(input: &[MetaAttrs]) -> bool {
// needs a redacted struct generated.
!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 self::{
event::expand_event,
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::expand_event, event_content::expand_event_content, event_enum::expand_event_enum,
event_parse::EventEnumInput,
};
@ -58,53 +53,7 @@ pub fn derive_event_content(input: TokenStream) -> TokenStream {
let ruma_events = import_ruma_events();
let input = parse_macro_input!(input as DeriveInput);
expand_event_content(&input, true, &ruma_events)
.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()
expand_event_content(&input, &ruma_events).unwrap_or_else(syn::Error::into_compile_error).into()
}
/// Generates implementations needed to serialize and deserialize Matrix events.

View File

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