events: Support attributes on items in event_enum!

This commit is contained in:
Jonas Platte 2020-11-24 02:28:17 +01:00
parent 1dee89393a
commit b7e15efe43
No known key found for this signature in database
GPG Key ID: 7D261D771D915378
3 changed files with 142 additions and 61 deletions

View File

@ -1,10 +1,10 @@
//! Implementation of event enum and event content enum macros.
use proc_macro2::{Span, TokenStream};
use quote::{format_ident, quote};
use quote::{format_ident, quote, ToTokens};
use syn::{Attribute, Ident, LitStr};
use crate::event_parse::{EventEnumInput, EventKind, EventKindVariation};
use crate::event_parse::{EventEnumEntry, EventEnumInput, EventKind, EventKindVariation};
fn is_non_stripped_room_event(kind: &EventKind, var: &EventKindVariation) -> bool {
matches!(kind, EventKind::Message | EventKind::State)
@ -47,13 +47,14 @@ pub fn expand_event_enum(input: EventEnumInput) -> syn::Result<TokenStream> {
let import_path = crate::import_ruma_events();
let name = &input.name;
let events = &input.events;
let attrs = &input.attrs;
let variants = events.iter().map(to_camel_case).collect::<syn::Result<Vec<_>>>()?;
let events: Vec<_> = input.events.iter().map(|entry| entry.ev_type.clone()).collect();
let variants: Vec<_> =
input.events.iter().map(EventEnumEntry::to_variant).collect::<syn::Result<_>>()?;
let event_enum = expand_any_with_deser(
name,
events,
&events,
attrs,
&variants,
&EventKindVariation::Full,
@ -62,7 +63,7 @@ pub fn expand_event_enum(input: EventEnumInput) -> syn::Result<TokenStream> {
let sync_event_enum = expand_any_with_deser(
name,
events,
&events,
attrs,
&variants,
&EventKindVariation::Sync,
@ -71,7 +72,7 @@ pub fn expand_event_enum(input: EventEnumInput) -> syn::Result<TokenStream> {
let stripped_event_enum = expand_any_with_deser(
name,
events,
&events,
attrs,
&variants,
&EventKindVariation::Stripped,
@ -80,16 +81,16 @@ pub fn expand_event_enum(input: EventEnumInput) -> syn::Result<TokenStream> {
let initial_event_enum = expand_any_with_deser(
name,
events,
&events,
attrs,
&variants,
&EventKindVariation::Initial,
&import_path,
);
let redacted_event_enums = expand_any_redacted(name, events, attrs, &variants, &import_path);
let redacted_event_enums = expand_any_redacted(name, &events, attrs, &variants, &import_path);
let event_content_enum = expand_content_enum(name, events, attrs, &variants, &import_path);
let event_content_enum = expand_content_enum(name, &events, attrs, &variants, &import_path);
Ok(quote! {
#event_enum
@ -110,7 +111,7 @@ fn expand_any_with_deser(
kind: &EventKind,
events: &[LitStr],
attrs: &[Attribute],
variants: &[Ident],
variants: &[EventEnumVariant],
var: &EventKindVariation,
import_path: &TokenStream,
) -> Option<TokenStream> {
@ -124,6 +125,9 @@ fn expand_any_with_deser(
.map(|event| to_event_path(event, &event_struct, import_path))
.collect::<Vec<_>>();
let variant_decls = variants.iter().map(|v| v.decl());
let self_variants = variants.iter().map(|v| v.ctor(quote!(Self)));
let (custom_variant, custom_deserialize) =
generate_custom_variant(&event_struct, var, import_path);
@ -135,12 +139,17 @@ fn expand_any_with_deser(
pub enum #ident {
#(
#[doc = #events]
#variants(#content),
#variant_decls(#content),
)*
#custom_variant
}
};
let variant_attrs = variants.iter().map(|v| {
let attrs = &v.attrs;
quote! { #(#attrs)* }
});
let event_deserialize_impl = quote! {
impl<'de> #import_path::exports::serde::de::Deserialize<'de> for #ident {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
@ -155,10 +164,10 @@ fn expand_any_with_deser(
match ev_type.as_str() {
#(
#events => {
#variant_attrs #events => {
let event = #import_path::exports::serde_json::from_str::<#content>(json.get())
.map_err(D::Error::custom)?;
Ok(Self::#variants(event))
Ok(#self_variants(event))
},
)*
#custom_deserialize
@ -193,17 +202,17 @@ fn expand_any_with_deser(
fn expand_conversion_impl(
kind: &EventKind,
var: &EventKindVariation,
variants: &[Ident],
variants: &[EventEnumVariant],
import_path: &TokenStream,
) -> Option<TokenStream> {
let ident = kind.to_event_enum_ident(var)?;
let variants = &variants
.iter()
.filter(|id| {
.filter(|v| {
// We filter this variant out only for non redacted events.
// The type of the struct held in the enum variant is different in this case
// so we construct the variant manually.
!(id.to_string().as_str() == "RoomRedaction"
!(v.ident == "RoomRedaction"
&& matches!(var, EventKindVariation::Full | EventKindVariation::Sync))
})
.collect::<Vec<_>>();
@ -220,6 +229,9 @@ fn expand_conversion_impl(
let sync = kind.to_event_enum_ident(&variation)?;
let sync_struct = kind.to_event_ident(&variation)?;
let ident_variants = variants.iter().map(|v| v.match_arm(&ident));
let self_variants = variants.iter().map(|v| v.ctor(quote!(Self)));
let redaction = if let (EventKind::Message, EventKindVariation::Full) = (kind, var) {
quote! {
#ident::RoomRedaction(event) => Self::RoomRedaction(
@ -235,8 +247,8 @@ fn expand_conversion_impl(
fn from(event: #ident) -> Self {
match event {
#(
#ident::#variants(event) => {
Self::#variants(#import_path::#sync_struct::from(event))
#ident_variants(event) => {
#self_variants(#import_path::#sync_struct::from(event))
},
)*
#redaction
@ -256,6 +268,9 @@ fn expand_conversion_impl(
};
let full = kind.to_event_enum_ident(&variation)?;
let self_variants = variants.iter().map(|v| v.match_arm(quote!(Self)));
let full_variants = variants.iter().map(|v| v.ctor(&full));
let redaction = if let (EventKind::Message, EventKindVariation::Sync) = (kind, var) {
quote! {
Self::RoomRedaction(event) => {
@ -269,11 +284,14 @@ fn expand_conversion_impl(
Some(quote! {
impl #ident {
/// Convert this sync event into a full event, one with a room_id field.
pub fn into_full_event(self, room_id: #import_path::exports::ruma_identifiers::RoomId) -> #full {
pub fn into_full_event(
self,
room_id: #import_path::exports::ruma_identifiers::RoomId
) -> #full {
match self {
#(
Self::#variants(event) => {
#full::#variants(event.into_full_event(room_id))
#self_variants(event) => {
#full_variants(event.into_full_event(room_id))
},
)*
#redaction
@ -298,7 +316,7 @@ fn expand_any_redacted(
kind: &EventKind,
events: &[LitStr],
attrs: &[Attribute],
variants: &[Ident],
variants: &[EventEnumVariant],
import_path: &TokenStream,
) -> TokenStream {
use EventKindVariation as V;
@ -339,7 +357,7 @@ fn expand_content_enum(
kind: &EventKind,
events: &[LitStr],
attrs: &[Attribute],
variants: &[Ident],
variants: &[EventEnumVariant],
import_path: &TokenStream,
) -> TokenStream {
let ident = kind.to_content_enum();
@ -348,6 +366,8 @@ fn expand_content_enum(
let content =
events.iter().map(|ev| to_event_content_path(ev, import_path)).collect::<Vec<_>>();
let variant_decls = variants.iter().map(|v| v.decl());
let content_enum = quote! {
#( #attrs )*
#[derive(Clone, Debug, #import_path::exports::serde::Serialize)]
@ -356,18 +376,25 @@ fn expand_content_enum(
pub enum #ident {
#(
#[doc = #event_type_str]
#variants(#content),
#variant_decls(#content),
)*
/// Content of an event not defined by the Matrix specification.
Custom(#import_path::custom::CustomEventContent),
}
};
let variant_attrs = variants.iter().map(|v| {
let attrs = &v.attrs;
quote! { #(#attrs)* }
});
let variant_arms = variants.iter().map(|v| v.match_arm(quote!(Self)));
let variant_ctors = variants.iter().map(|v| v.ctor(quote!(Self)));
let event_content_impl = quote! {
impl #import_path::EventContent for #ident {
fn event_type(&self) -> &str {
match self {
#( Self::#variants(content) => content.event_type(), )*
#( #variant_arms(content) => content.event_type(), )*
Self::Custom(content) => content.event_type(),
}
}
@ -377,9 +404,9 @@ fn expand_content_enum(
) -> Result<Self, #import_path::exports::serde_json::Error> {
match event_type {
#(
#event_type_str => {
#variant_attrs #event_type_str => {
let content = #content::from_parts(event_type, input)?;
Ok(Self::#variants(content))
Ok(#variant_ctors(content))
},
)*
ev_type => {
@ -407,7 +434,7 @@ fn expand_redact(
ident: &Ident,
kind: &EventKind,
var: &EventKindVariation,
variants: &[Ident],
variants: &[EventEnumVariant],
import_path: &TokenStream,
) -> Option<TokenStream> {
if let EventKindVariation::Full | EventKindVariation::Sync | EventKindVariation::Stripped = var
@ -440,6 +467,9 @@ fn expand_redact(
_ => return None,
};
let self_variants = variants.iter().map(|v| v.match_arm(quote!(Self)));
let redaction_variants = variants.iter().map(|v| v.ctor(&redaction_enum));
let fields = EVENT_FIELDS.iter().map(|(name, has_field)| {
generate_redacted_fields(name, kind, var, *has_field, import_path)
});
@ -456,9 +486,9 @@ fn expand_redact(
) -> #redaction_enum {
match self {
#(
Self::#variants(event) => {
#self_variants(event) => {
let content = event.content.redact(version);
#redaction_enum::#variants(#redaction_type {
#redaction_variants(#redaction_type {
content,
#fields
})
@ -636,7 +666,7 @@ fn marker_traits(kind: &EventKind, import_path: &TokenStream) -> TokenStream {
fn accessor_methods(
kind: &EventKind,
var: &EventKindVariation,
variants: &[Ident],
variants: &[EventEnumVariant],
import_path: &TokenStream,
) -> Option<TokenStream> {
use EventKindVariation as V;
@ -654,13 +684,14 @@ fn accessor_methods(
let content_enum = kind.to_content_enum();
let self_variants: Vec<_> = variants.iter().map(|v| v.match_arm(quote!(Self))).collect();
let content_variants: Vec<_> = variants.iter().map(|v| v.ctor(&content_enum)).collect();
let content = quote! {
/// Returns the any content enum for this event.
pub fn content(&self) -> #content_enum {
match self {
#(
Self::#variants(event) => #content_enum::#variants(event.content.clone()),
)*
#( #self_variants(event) => #content_variants(event.content.clone()), )*
Self::Custom(event) => #content_enum::Custom(event.content.clone()),
}
}
@ -672,8 +703,8 @@ fn accessor_methods(
pub fn prev_content(&self) -> Option<#content_enum> {
match self {
#(
Self::#variants(event) => {
event.prev_content.as_ref().map(|c| #content_enum::#variants(c.clone()))
#self_variants(event) => {
event.prev_content.as_ref().map(|c| #content_variants(c.clone()))
},
)*
Self::Custom(event) => {
@ -720,7 +751,7 @@ fn inner_enum_idents(kind: &EventKind, var: &EventKindVariation) -> (Option<Iden
fn redacted_accessor_methods(
kind: &EventKind,
var: &EventKindVariation,
variants: &[Ident],
variants: &[EventEnumVariant],
import_path: &TokenStream,
) -> Option<TokenStream> {
// this will never fail as it is called in `expand_any_with_deser`.
@ -830,22 +861,21 @@ fn generate_accessor(
kind: &EventKind,
var: &EventKindVariation,
is_event_kind: EventKindFn,
variants: &[Ident],
variants: &[EventEnumVariant],
import_path: &TokenStream,
) -> TokenStream {
if is_event_kind(kind, var) {
let docs = format!("Returns this event's {} field.", name);
let ident = Ident::new(name, Span::call_site());
let field_type = field_return_type(name, var, import_path);
let variants = variants.iter().map(|v| v.match_arm(quote!(Self)));
let name = Ident::new(name, Span::call_site());
let docs = format!("Returns this events {} field.", name);
quote! {
#[doc = #docs]
pub fn #name(&self) -> &#field_type {
pub fn #ident(&self) -> &#field_type {
match self {
#(
Self::#variants(event) => &event.#name,
)*
Self::Custom(event) => &event.#name,
#( #variants(event) => &event.#ident, )*
Self::Custom(event) => &event.#ident,
}
}
}
@ -875,3 +905,48 @@ fn field_return_type(
_ => panic!("the `ruma_events_macros::event_enum::EVENT_FIELD` const was changed"),
}
}
struct EventEnumVariant {
pub attrs: Vec<Attribute>,
pub ident: Ident,
}
impl EventEnumVariant {
fn to_tokens<T>(&self, prefix: Option<T>, with_attrs: bool) -> TokenStream
where
T: ToTokens,
{
let mut tokens = TokenStream::new();
if with_attrs {
for attr in &self.attrs {
attr.to_tokens(&mut tokens);
}
}
if let Some(p) = prefix {
tokens.extend(quote! { #p :: })
}
self.ident.to_tokens(&mut tokens);
tokens
}
fn decl(&self) -> TokenStream {
self.to_tokens::<TokenStream>(None, true)
}
fn match_arm(&self, prefix: impl ToTokens) -> TokenStream {
self.to_tokens(Some(prefix), true)
}
fn ctor(&self, prefix: impl ToTokens) -> TokenStream {
self.to_tokens(Some(prefix), false)
}
}
impl EventEnumEntry {
fn to_variant(&self) -> syn::Result<EventEnumVariant> {
let attrs = self.attrs.clone();
let ident = to_camel_case(&self.ev_type)?;
Ok(EventEnumVariant { attrs, ident })
}
}

View File

@ -5,8 +5,9 @@ use std::fmt;
use proc_macro2::Span;
use quote::format_ident;
use syn::{
bracketed,
parse::{self, Parse, ParseStream},
Attribute, Expr, ExprLit, Ident, Lit, LitStr, Token,
Attribute, Ident, LitStr, Token,
};
/// Custom keywords for the `event_enum!` macro
@ -193,7 +194,7 @@ pub struct EventEnumInput {
/// variant in `ruma_events::EventType` for this event (converted to a valid Rust-style type
/// name by stripping `m.`, replacing the remaining dots by underscores and then converting from
/// snake_case to CamelCase).
pub events: Vec<LitStr>,
pub events: Vec<EventEnumEntry>,
}
impl Parse for EventEnumInput {
@ -212,20 +213,22 @@ impl Parse for EventEnumInput {
input.parse::<Token![:]>()?;
// an array of event names `["m.room.whatever", ...]`
let ev_array = input.parse::<syn::ExprArray>()?;
let events = ev_array
.elems
.into_iter()
.map(|item| {
if let Expr::Lit(ExprLit { lit: Lit::Str(lit_str), .. }) = item {
Ok(lit_str)
} else {
let msg = "values of field `events` are required to be a string literal";
Err(syn::Error::new_spanned(item, msg))
}
})
.collect::<syn::Result<_>>()?;
let content;
bracketed!(content in input);
let events = content.parse_terminated::<_, Token![,]>(EventEnumEntry::parse)?;
let events = events.into_iter().collect();
Ok(Self { attrs, name, events })
}
}
pub struct EventEnumEntry {
pub attrs: Vec<Attribute>,
pub ev_type: LitStr,
}
impl Parse for EventEnumEntry {
fn parse(input: ParseStream<'_>) -> syn::Result<Self> {
Ok(Self { attrs: input.call(Attribute::parse_outer)?, ev_type: input.parse()? })
}
}

View File

@ -5,10 +5,13 @@ event_enum! {
kind: Basic,
events: [
"m.direct",
#[cfg(test)]
"m.dummy",
"m.ignored_user_list",
"m.push_rules",
"m.room_key",
#[cfg(any())]
"m.ruma_test",
]
}