Add field accessor code-gen for the event_enum! macro
This commit is contained in:
parent
5376a3fc6e
commit
41b8bd77f5
@ -1,28 +1,67 @@
|
|||||||
//! Implementation of event enum and event content enum macros.
|
//! Implementation of event enum and event content enum macros.
|
||||||
|
|
||||||
use proc_macro2::TokenStream;
|
use proc_macro2::{Span, TokenStream};
|
||||||
use quote::quote;
|
use quote::quote;
|
||||||
use syn::{
|
use syn::{
|
||||||
parse::{self, Parse, ParseStream},
|
parse::{self, Parse, ParseStream},
|
||||||
Attribute, Expr, ExprLit, Ident, Lit, LitStr, Token,
|
Attribute, Expr, ExprLit, Ident, Lit, LitStr, Token,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
use crate::event_names::{
|
||||||
|
ANY_BASIC_EVENT, ANY_EPHEMERAL_EVENT, ANY_MESSAGE_EVENT, ANY_STATE_EVENT,
|
||||||
|
ANY_STRIPPED_STATE_EVENT, ANY_SYNC_MESSAGE_EVENT, ANY_SYNC_STATE_EVENT, ANY_TO_DEVICE_EVENT,
|
||||||
|
};
|
||||||
|
|
||||||
|
// Arrays of event enum names grouped by a field they share in common.
|
||||||
|
const ROOM_EVENT_KIND: &[&str] =
|
||||||
|
&[ANY_MESSAGE_EVENT, ANY_SYNC_MESSAGE_EVENT, ANY_STATE_EVENT, ANY_SYNC_STATE_EVENT];
|
||||||
|
|
||||||
|
const ROOM_ID_KIND: &[&str] = &[ANY_MESSAGE_EVENT, ANY_STATE_EVENT, ANY_EPHEMERAL_EVENT];
|
||||||
|
|
||||||
|
const EVENT_ID_KIND: &[&str] =
|
||||||
|
&[ANY_MESSAGE_EVENT, ANY_SYNC_MESSAGE_EVENT, ANY_STATE_EVENT, ANY_SYNC_STATE_EVENT];
|
||||||
|
|
||||||
|
const SENDER_KIND: &[&str] = &[
|
||||||
|
ANY_MESSAGE_EVENT,
|
||||||
|
ANY_STATE_EVENT,
|
||||||
|
ANY_SYNC_STATE_EVENT,
|
||||||
|
ANY_TO_DEVICE_EVENT,
|
||||||
|
ANY_SYNC_MESSAGE_EVENT,
|
||||||
|
ANY_STRIPPED_STATE_EVENT,
|
||||||
|
];
|
||||||
|
|
||||||
|
const PREV_CONTENT_KIND: &[&str] = &[ANY_STATE_EVENT, ANY_SYNC_STATE_EVENT];
|
||||||
|
|
||||||
|
const STATE_KEY_KIND: &[&str] = &[ANY_STATE_EVENT, ANY_SYNC_STATE_EVENT, ANY_STRIPPED_STATE_EVENT];
|
||||||
|
|
||||||
|
/// 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, &[&str])] = &[
|
||||||
|
("origin_server_ts", ROOM_EVENT_KIND),
|
||||||
|
("room_id", ROOM_ID_KIND),
|
||||||
|
("event_id", EVENT_ID_KIND),
|
||||||
|
("sender", SENDER_KIND),
|
||||||
|
("state_key", STATE_KEY_KIND),
|
||||||
|
("unsigned", ROOM_EVENT_KIND),
|
||||||
|
];
|
||||||
|
|
||||||
/// Create a content enum from `EventEnumInput`.
|
/// Create a content enum from `EventEnumInput`.
|
||||||
pub fn expand_event_enum(input: EventEnumInput) -> syn::Result<TokenStream> {
|
pub fn expand_event_enum(input: EventEnumInput) -> syn::Result<TokenStream> {
|
||||||
let ident = &input.name;
|
let ident = &input.name;
|
||||||
|
|
||||||
let event_enum = expand_any_enum_with_deserialize(&input, ident)?;
|
let event_enum = expand_any_enum_with_deserialize(&input, ident)?;
|
||||||
|
|
||||||
let needs_event_content = ident == "AnyStateEvent"
|
let needs_event_content = ident == ANY_STATE_EVENT
|
||||||
|| ident == "AnyMessageEvent"
|
|| ident == ANY_MESSAGE_EVENT
|
||||||
|| ident == "AnyToDeviceEvent"
|
|| ident == ANY_TO_DEVICE_EVENT
|
||||||
|| ident == "AnyEphemeralRoomEvent"
|
|| ident == ANY_EPHEMERAL_EVENT
|
||||||
|| ident == "AnyBasicEvent";
|
|| ident == ANY_BASIC_EVENT;
|
||||||
|
|
||||||
let needs_event_stub =
|
let needs_event_stub =
|
||||||
ident == "AnyStateEvent" || ident == "AnyMessageEvent" || ident == "AnyEphemeralRoomEvent";
|
ident == ANY_STATE_EVENT || ident == ANY_MESSAGE_EVENT || ident == ANY_EPHEMERAL_EVENT;
|
||||||
|
|
||||||
let needs_stripped_event = ident == "AnyStateEvent";
|
let needs_stripped_event = ident == ANY_STATE_EVENT;
|
||||||
|
|
||||||
let event_stub_enum =
|
let event_stub_enum =
|
||||||
if needs_event_stub { expand_stub_enum(&input)? } else { TokenStream::new() };
|
if needs_event_stub { expand_stub_enum(&input)? } else { TokenStream::new() };
|
||||||
@ -175,9 +214,13 @@ fn expand_any_enum_with_deserialize(
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let field_accessor_impl = accessor_methods(ident, &variants);
|
||||||
|
|
||||||
Ok(quote! {
|
Ok(quote! {
|
||||||
#any_enum
|
#any_enum
|
||||||
|
|
||||||
|
#field_accessor_impl
|
||||||
|
|
||||||
#event_deserialize_impl
|
#event_deserialize_impl
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -202,6 +245,57 @@ fn marker_traits(ident: &Ident) -> TokenStream {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn accessor_methods(ident: &Ident, variants: &[Ident]) -> TokenStream {
|
||||||
|
let fields = EVENT_FIELDS
|
||||||
|
.iter()
|
||||||
|
.map(|(name, has_field)| generate_accessor(name, ident, *has_field, variants));
|
||||||
|
|
||||||
|
let any_content = ident.to_string().replace("Stub", "").replace("Stripped", "");
|
||||||
|
let content_enum = Ident::new(&format!("{}Content", any_content), ident.span());
|
||||||
|
|
||||||
|
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::Custom(event) => #content_enum::Custom(event.content.clone()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let prev_content = if PREV_CONTENT_KIND.contains(&ident.to_string().as_str()) {
|
||||||
|
quote! {
|
||||||
|
/// Returns the any content enum for this events prev_content.
|
||||||
|
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::Custom(event) => {
|
||||||
|
event.prev_content.as_ref().map(|c| #content_enum::Custom(c.clone()))
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
TokenStream::new()
|
||||||
|
};
|
||||||
|
|
||||||
|
quote! {
|
||||||
|
impl #ident {
|
||||||
|
#content
|
||||||
|
|
||||||
|
#prev_content
|
||||||
|
|
||||||
|
#( #fields )*
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn to_event_path(name: &LitStr, struct_name: &Ident) -> TokenStream {
|
fn to_event_path(name: &LitStr, struct_name: &Ident) -> TokenStream {
|
||||||
let span = name.span();
|
let span = name.span();
|
||||||
let name = name.value();
|
let name = name.value();
|
||||||
@ -280,6 +374,45 @@ pub(crate) fn to_camel_case(name: &LitStr) -> syn::Result<Ident> {
|
|||||||
Ok(Ident::new(&s, span))
|
Ok(Ident::new(&s, span))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn generate_accessor(
|
||||||
|
name: &str,
|
||||||
|
ident: &Ident,
|
||||||
|
event_kind_list: &[&str],
|
||||||
|
variants: &[Ident],
|
||||||
|
) -> TokenStream {
|
||||||
|
if event_kind_list.contains(&ident.to_string().as_str()) {
|
||||||
|
let field_type = field_return_type(name);
|
||||||
|
|
||||||
|
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 {
|
||||||
|
match self {
|
||||||
|
#(
|
||||||
|
Self::#variants(event) => &event.#name,
|
||||||
|
)*
|
||||||
|
Self::Custom(event) => &event.#name,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
TokenStream::new()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn field_return_type(name: &str) -> TokenStream {
|
||||||
|
match name {
|
||||||
|
"origin_server_ts" => quote! { ::std::time::SystemTime },
|
||||||
|
"room_id" => quote! { ::ruma_identifiers::RoomId },
|
||||||
|
"event_id" => quote! { ::ruma_identifiers::EventId },
|
||||||
|
"sender" => quote! { ::ruma_identifiers::UserId },
|
||||||
|
"state_key" => quote! { str },
|
||||||
|
"unsigned" => quote! { ::ruma_events::UnsignedData },
|
||||||
|
_ => panic!("the `ruma_events_macros::event_enum::EVENT_FIELD` const was changed"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Custom keywords for the `event_enum!` macro
|
/// Custom keywords for the `event_enum!` macro
|
||||||
mod kw {
|
mod kw {
|
||||||
syn::custom_keyword!(name);
|
syn::custom_keyword!(name);
|
||||||
|
28
ruma-events-macros/src/event_names.rs
Normal file
28
ruma-events-macros/src/event_names.rs
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
//! The names of the `Any*Event` enums. The event_enum! macro uses these names to generate
|
||||||
|
//! certain code for certain enums. If the names change this is the one source of truth,
|
||||||
|
//! most comparisons and branching uses these constants.
|
||||||
|
|
||||||
|
// State events
|
||||||
|
pub const ANY_STATE_EVENT: &str = "AnyStateEvent";
|
||||||
|
|
||||||
|
pub const ANY_SYNC_STATE_EVENT: &str = "AnyStateEventStub";
|
||||||
|
|
||||||
|
pub const ANY_STRIPPED_STATE_EVENT: &str = "AnyStrippedStateEventStub";
|
||||||
|
|
||||||
|
// Message events
|
||||||
|
pub const ANY_MESSAGE_EVENT: &str = "AnyMessageEvent";
|
||||||
|
|
||||||
|
pub const ANY_SYNC_MESSAGE_EVENT: &str = "AnyMessageEventStub";
|
||||||
|
|
||||||
|
// Ephemeral events
|
||||||
|
pub const ANY_EPHEMERAL_EVENT: &str = "AnyEphemeralRoomEvent";
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
|
// This is currently not used but, left for completeness sake.
|
||||||
|
pub const ANY_SYNC_EPHEMERAL_EVENT: &str = "AnyEphemeralRoomEventStub";
|
||||||
|
|
||||||
|
// Basic event
|
||||||
|
pub const ANY_BASIC_EVENT: &str = "AnyBasicEvent";
|
||||||
|
|
||||||
|
// To device event
|
||||||
|
pub const ANY_TO_DEVICE_EVENT: &str = "AnyToDeviceEvent";
|
@ -22,6 +22,7 @@ use self::{
|
|||||||
mod event;
|
mod event;
|
||||||
mod event_content;
|
mod event_content;
|
||||||
mod event_enum;
|
mod event_enum;
|
||||||
|
mod event_names;
|
||||||
|
|
||||||
/// Generates an enum to represent the various Matrix event types.
|
/// Generates an enum to represent the various Matrix event types.
|
||||||
///
|
///
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
use std::convert::TryFrom;
|
use std::convert::TryFrom;
|
||||||
|
|
||||||
use matches::assert_matches;
|
use matches::assert_matches;
|
||||||
use ruma_identifiers::RoomAliasId;
|
use ruma_identifiers::{EventId, RoomAliasId, RoomId, UserId};
|
||||||
use serde_json::{from_value as from_json_value, json, Value as JsonValue};
|
use serde_json::{from_value as from_json_value, json, Value as JsonValue};
|
||||||
|
|
||||||
use ruma_events::{
|
use ruma_events::{
|
||||||
@ -11,7 +11,8 @@ use ruma_events::{
|
|||||||
power_levels::PowerLevelsEventContent,
|
power_levels::PowerLevelsEventContent,
|
||||||
},
|
},
|
||||||
AnyEvent, AnyMessageEvent, AnyMessageEventStub, AnyRoomEvent, AnyRoomEventStub, AnyStateEvent,
|
AnyEvent, AnyMessageEvent, AnyMessageEventStub, AnyRoomEvent, AnyRoomEventStub, AnyStateEvent,
|
||||||
AnyStateEventStub, MessageEvent, MessageEventStub, StateEvent, StateEventStub,
|
AnyStateEventContent, AnyStateEventStub, MessageEvent, MessageEventStub, StateEvent,
|
||||||
|
StateEventStub,
|
||||||
};
|
};
|
||||||
|
|
||||||
fn message_event() -> JsonValue {
|
fn message_event() -> JsonValue {
|
||||||
@ -243,3 +244,24 @@ fn alias_event_deserialization() {
|
|||||||
if aliases == vec![ RoomAliasId::try_from("#somewhere:localhost").unwrap() ]
|
if aliases == vec![ RoomAliasId::try_from("#somewhere:localhost").unwrap() ]
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn alias_event_field_access() {
|
||||||
|
let json_data = aliases_event();
|
||||||
|
|
||||||
|
assert_matches!(
|
||||||
|
from_json_value::<AnyEvent>(json_data.clone()),
|
||||||
|
Ok(AnyEvent::State(state_event))
|
||||||
|
if state_event.state_key() == ""
|
||||||
|
&& state_event.room_id() == &RoomId::try_from("!room:room.com").unwrap()
|
||||||
|
&& state_event.event_id() == &EventId::try_from("$152037280074GZeOm:localhost").unwrap()
|
||||||
|
&& state_event.sender() == &UserId::try_from("@example:localhost").unwrap()
|
||||||
|
);
|
||||||
|
|
||||||
|
let deser = from_json_value::<AnyStateEvent>(json_data).unwrap();
|
||||||
|
if let AnyStateEventContent::RoomAliases(AliasesEventContent { aliases }) = deser.content() {
|
||||||
|
assert_eq!(aliases, vec![RoomAliasId::try_from("#somewhere:localhost").unwrap()])
|
||||||
|
} else {
|
||||||
|
panic!("the `Any*Event` enum's accessor methods may have been altered")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user