events-macros: Reorganize event impl generation

This commit is contained in:
Jonas Platte 2021-10-07 00:07:15 +02:00
parent 7a4af83207
commit 9103ad74bb
No known key found for this signature in database
GPG Key ID: CC154DE0E30B7C67
5 changed files with 129 additions and 120 deletions

View File

@ -10,6 +10,7 @@ use syn::{
use crate::{ use crate::{
event_parse::{to_kind_variation, EventKind, EventKindVariation}, event_parse::{to_kind_variation, EventKind, EventKindVariation},
import_ruma_events, import_ruma_events,
util::is_non_stripped_room_event,
}; };
/// Derive `Event` macro code generation. /// Derive `Event` macro code generation.
@ -41,19 +42,26 @@ pub fn expand_event(input: DeriveInput) -> syn::Result<TokenStream> {
)); ));
}; };
let conversion_impl = expand_from_into(&input, kind, var, &fields, &ruma_events); let mut res = TokenStream::new();
let serialize_impl = expand_serialize_event(&input, var, &fields, &ruma_events);
let deserialize_impl = expand_deserialize_event(&input, kind, var, &fields, &ruma_events)?;
let redact_impl = expand_redact_event(&input, kind, var, &fields, &ruma_events);
let eq_impl = expand_eq_ord_event(&input, &fields);
Ok(quote! { res.extend(expand_serialize_event(&input, var, &fields, &ruma_events));
#conversion_impl res.extend(expand_deserialize_event(&input, kind, var, &fields, &ruma_events)?);
#serialize_impl
#deserialize_impl if var.is_sync() {
#redact_impl res.extend(expand_sync_from_into_full(&input, kind, var, &fields, &ruma_events));
#eq_impl }
})
if matches!(kind, EventKind::Message | EventKind::State)
&& matches!(var, EventKindVariation::Full | EventKindVariation::Sync)
{
res.extend(expand_redact_event(&input, kind, var, &fields, &ruma_events));
}
if is_non_stripped_room_event(kind, var) {
res.extend(expand_eq_ord_event(&input));
}
Ok(res)
} }
fn expand_serialize_event( fn expand_serialize_event(
@ -405,17 +413,17 @@ fn expand_redact_event(
var: EventKindVariation, var: EventKindVariation,
fields: &[Field], fields: &[Field],
ruma_events: &TokenStream, ruma_events: &TokenStream,
) -> Option<TokenStream> { ) -> TokenStream {
let ruma_identifiers = quote! { #ruma_events::exports::ruma_identifiers }; let ruma_identifiers = quote! { #ruma_events::exports::ruma_identifiers };
let redacted_type = kind.to_event_ident(var.to_redacted()?)?; let redacted_type = kind.to_event_ident(var.to_redacted().unwrap()).unwrap();
let redacted_content_trait = let redacted_content_trait =
format_ident!("{}Content", kind.to_event_ident(EventKindVariation::Redacted).unwrap()); format_ident!("{}Content", kind.to_event_ident(EventKindVariation::Redacted).unwrap());
let ident = &input.ident; let ident = &input.ident;
let mut generics = input.generics.clone(); let mut generics = input.generics.clone();
if generics.params.is_empty() { if generics.params.is_empty() {
return None; return TokenStream::new();
} }
assert_eq!(generics.params.len(), 1, "expected one generic parameter"); assert_eq!(generics.params.len(), 1, "expected one generic parameter");
@ -450,7 +458,7 @@ fn expand_redact_event(
} }
}); });
Some(quote! { quote! {
#[automatically_derived] #[automatically_derived]
impl #impl_generics #ruma_events::Redact for #ident #ty_gen #where_clause { impl #impl_generics #ruma_events::Redact for #ident #ty_gen #where_clause {
type Redacted = type Redacted =
@ -468,91 +476,83 @@ fn expand_redact_event(
} }
} }
} }
}) }
} }
fn expand_from_into( fn expand_sync_from_into_full(
input: &DeriveInput, input: &DeriveInput,
kind: EventKind, kind: EventKind,
var: EventKindVariation, var: EventKindVariation,
fields: &[Field], fields: &[Field],
ruma_events: &TokenStream, ruma_events: &TokenStream,
) -> Option<TokenStream> { ) -> TokenStream {
let ruma_identifiers = quote! { #ruma_events::exports::ruma_identifiers }; let ruma_identifiers = quote! { #ruma_events::exports::ruma_identifiers };
let ident = &input.ident; let ident = &input.ident;
let full_struct = kind.to_event_ident(var.to_full().unwrap()).unwrap();
let (impl_generics, ty_gen, where_clause) = input.generics.split_for_impl(); let (impl_generics, ty_gen, where_clause) = input.generics.split_for_impl();
let fields: Vec<_> = fields.iter().flat_map(|f| &f.ident).collect(); let fields: Vec<_> = fields.iter().flat_map(|f| &f.ident).collect();
if let EventKindVariation::Sync | EventKindVariation::RedactedSync = var { quote! {
let full_struct = kind.to_event_ident(var.to_full().unwrap()).unwrap(); #[automatically_derived]
Some(quote! { impl #impl_generics ::std::convert::From<#full_struct #ty_gen>
#[automatically_derived] for #ident #ty_gen #where_clause
impl #impl_generics ::std::convert::From<#full_struct #ty_gen> {
for #ident #ty_gen #where_clause fn from(event: #full_struct #ty_gen) -> Self {
{ let #full_struct { #( #fields, )* .. } = event;
fn from(event: #full_struct #ty_gen) -> Self { Self { #( #fields, )* }
let #full_struct { #( #fields, )* .. } = event;
Self { #( #fields, )* }
}
} }
}
#[automatically_derived] #[automatically_derived]
impl #impl_generics #ident #ty_gen #where_clause { impl #impl_generics #ident #ty_gen #where_clause {
/// Convert this sync event into a full event, one with a room_id field. /// Convert this sync event into a full event, one with a room_id field.
pub fn into_full_event( pub fn into_full_event(
self, self,
room_id: #ruma_identifiers::RoomId, room_id: #ruma_identifiers::RoomId,
) -> #full_struct #ty_gen { ) -> #full_struct #ty_gen {
let Self { #( #fields, )* } = self; let Self { #( #fields, )* } = self;
#full_struct { #full_struct {
#( #fields, )* #( #fields, )*
room_id, room_id,
}
}
}
})
} else {
None
}
}
fn expand_eq_ord_event(input: &DeriveInput, fields: &[Field]) -> Option<TokenStream> {
fields.iter().flat_map(|f| f.ident.as_ref()).any(|f| f == "event_id").then(|| {
let ident = &input.ident;
let (impl_gen, ty_gen, where_clause) = input.generics.split_for_impl();
quote! {
#[automatically_derived]
impl #impl_gen ::std::cmp::PartialEq for #ident #ty_gen #where_clause {
/// Checks if two `EventId`s are equal.
fn eq(&self, other: &Self) -> ::std::primitive::bool {
self.event_id == other.event_id
}
}
#[automatically_derived]
impl #impl_gen ::std::cmp::Eq for #ident #ty_gen #where_clause {}
#[automatically_derived]
impl #impl_gen ::std::cmp::PartialOrd for #ident #ty_gen #where_clause {
/// Compares `EventId`s and orders them lexicographically.
fn partial_cmp(&self, other: &Self) -> ::std::option::Option<::std::cmp::Ordering> {
self.event_id.partial_cmp(&other.event_id)
}
}
#[automatically_derived]
impl #impl_gen ::std::cmp::Ord for #ident #ty_gen #where_clause {
/// Compares `EventId`s and orders them lexicographically.
fn cmp(&self, other: &Self) -> ::std::cmp::Ordering {
self.event_id.cmp(&other.event_id)
} }
} }
} }
}) }
}
fn expand_eq_ord_event(input: &DeriveInput) -> TokenStream {
let ident = &input.ident;
let (impl_gen, ty_gen, where_clause) = input.generics.split_for_impl();
quote! {
#[automatically_derived]
impl #impl_gen ::std::cmp::PartialEq for #ident #ty_gen #where_clause {
/// Checks if two `EventId`s are equal.
fn eq(&self, other: &Self) -> ::std::primitive::bool {
self.event_id == other.event_id
}
}
#[automatically_derived]
impl #impl_gen ::std::cmp::Eq for #ident #ty_gen #where_clause {}
#[automatically_derived]
impl #impl_gen ::std::cmp::PartialOrd for #ident #ty_gen #where_clause {
/// Compares `EventId`s and orders them lexicographically.
fn partial_cmp(&self, other: &Self) -> ::std::option::Option<::std::cmp::Ordering> {
self.event_id.partial_cmp(&other.event_id)
}
}
#[automatically_derived]
impl #impl_gen ::std::cmp::Ord for #ident #ty_gen #where_clause {
/// Compares `EventId`s and orders them lexicographically.
fn cmp(&self, other: &Self) -> ::std::cmp::Ordering {
self.event_id.cmp(&other.event_id)
}
}
}
} }
/// CamelCase's a field ident like "foo_bar" to "FooBar". /// CamelCase's a field ident like "foo_bar" to "FooBar".

View File

@ -4,43 +4,10 @@ use proc_macro2::{Span, TokenStream};
use quote::{format_ident, quote, ToTokens}; use quote::{format_ident, quote, ToTokens};
use syn::{Attribute, Data, DataEnum, DeriveInput, Ident, LitStr}; use syn::{Attribute, Data, DataEnum, DeriveInput, Ident, LitStr};
use crate::event_parse::{EventEnumDecl, EventEnumEntry, EventKind, EventKindVariation}; use crate::{
event_parse::{EventEnumDecl, EventEnumEntry, EventKind, EventKindVariation},
fn is_non_stripped_room_event(kind: EventKind, var: EventKindVariation) -> bool { util::{has_prev_content_field, EVENT_FIELDS},
matches!(kind, EventKind::Message | EventKind::State) };
&& matches!(
var,
EventKindVariation::Full
| EventKindVariation::Sync
| EventKindVariation::Redacted
| EventKindVariation::RedactedSync
)
}
fn has_prev_content_field(kind: EventKind, var: EventKindVariation) -> bool {
matches!(kind, EventKind::State)
&& matches!(var, EventKindVariation::Full | EventKindVariation::Sync)
}
type EventKindFn = fn(EventKind, EventKindVariation) -> bool;
/// This const is used to generate the accessor methods for the `Any*Event` enums.
///
/// DO NOT alter the field names unless the structs in `ruma_events::event_kinds` have changed.
const EVENT_FIELDS: &[(&str, EventKindFn)] = &[
("origin_server_ts", is_non_stripped_room_event),
("room_id", |kind, var| {
matches!(kind, EventKind::Message | EventKind::State | EventKind::Ephemeral)
&& matches!(var, EventKindVariation::Full | EventKindVariation::Redacted)
}),
("event_id", is_non_stripped_room_event),
("sender", |kind, var| {
matches!(kind, EventKind::Message | EventKind::State | EventKind::ToDevice)
&& var != EventKindVariation::Initial
}),
("state_key", |kind, _| matches!(kind, EventKind::State)),
("unsigned", is_non_stripped_room_event),
];
/// Create a content enum from `EventEnumInput`. /// Create a content enum from `EventEnumInput`.
pub fn expand_event_enums(input: &EventEnumDecl) -> syn::Result<TokenStream> { pub fn expand_event_enums(input: &EventEnumDecl) -> syn::Result<TokenStream> {

View File

@ -45,6 +45,10 @@ impl EventKindVariation {
matches!(self, Self::Redacted | Self::RedactedSync) matches!(self, Self::Redacted | Self::RedactedSync)
} }
pub fn is_sync(self) -> bool {
matches!(self, Self::Sync | Self::RedactedSync)
}
pub fn to_redacted(self) -> Option<Self> { pub fn to_redacted(self) -> Option<Self> {
match self { match self {
EventKindVariation::Full => Some(EventKindVariation::Redacted), EventKindVariation::Full => Some(EventKindVariation::Redacted),

View File

@ -27,6 +27,7 @@ mod event_content;
mod event_enum; mod event_enum;
mod event_parse; mod event_parse;
mod event_type; mod event_type;
mod util;
/// Generates an enum to represent the various Matrix event types. /// Generates an enum to represent the various Matrix event types.
/// ///

View File

@ -0,0 +1,37 @@
use crate::event_parse::{EventKind, EventKindVariation};
pub(crate) fn is_non_stripped_room_event(kind: EventKind, var: EventKindVariation) -> bool {
matches!(kind, EventKind::Message | EventKind::State)
&& matches!(
var,
EventKindVariation::Full
| EventKindVariation::Sync
| EventKindVariation::Redacted
| EventKindVariation::RedactedSync
)
}
pub(crate) fn has_prev_content_field(kind: EventKind, var: EventKindVariation) -> bool {
matches!(kind, EventKind::State)
&& matches!(var, EventKindVariation::Full | EventKindVariation::Sync)
}
pub(crate) type EventKindFn = fn(EventKind, EventKindVariation) -> bool;
/// This const is used to generate the accessor methods for the `Any*Event` enums.
///
/// DO NOT alter the field names unless the structs in `ruma_events::event_kinds` have changed.
pub(crate) const EVENT_FIELDS: &[(&str, EventKindFn)] = &[
("origin_server_ts", is_non_stripped_room_event),
("room_id", |kind, var| {
matches!(kind, EventKind::Message | EventKind::State | EventKind::Ephemeral)
&& matches!(var, EventKindVariation::Full | EventKindVariation::Redacted)
}),
("event_id", is_non_stripped_room_event),
("sender", |kind, var| {
matches!(kind, EventKind::Message | EventKind::State | EventKind::ToDevice)
&& var != EventKindVariation::Initial
}),
("state_key", |kind, _| matches!(kind, EventKind::State)),
("unsigned", is_non_stripped_room_event),
];