Add support for events with custom types.
This commit is contained in:
parent
44a13e6515
commit
553d9c05cd
130
src/gen.rs
130
src/gen.rs
@ -30,6 +30,9 @@ pub struct RumaEvent {
|
||||
/// Struct fields of the event.
|
||||
fields: Vec<Field>,
|
||||
|
||||
/// Whether or not the event type is `EventType::Custom`.
|
||||
is_custom: bool,
|
||||
|
||||
/// The kind of event.
|
||||
kind: EventKind,
|
||||
|
||||
@ -42,18 +45,25 @@ impl From<RumaEventInput> for RumaEvent {
|
||||
let kind = input.kind;
|
||||
let name = input.name;
|
||||
let content_name = Ident::new(&format!("{}Content", &name), Span::call_site());
|
||||
let event_type = input.event_type;
|
||||
let is_custom = is_custom_event_type(&event_type);
|
||||
|
||||
let mut fields = match kind {
|
||||
EventKind::Event => {
|
||||
populate_event_fields(content_name.clone(), input.fields.unwrap_or_else(Vec::new))
|
||||
}
|
||||
EventKind::RoomEvent => populate_room_event_fields(
|
||||
EventKind::Event => populate_event_fields(
|
||||
is_custom,
|
||||
content_name.clone(),
|
||||
input.fields.unwrap_or_else(Vec::new),
|
||||
),
|
||||
EventKind::RoomEvent => populate_room_event_fields(
|
||||
is_custom,
|
||||
content_name.clone(),
|
||||
input.fields.unwrap_or_else(Vec::new),
|
||||
),
|
||||
EventKind::StateEvent => populate_state_fields(
|
||||
is_custom,
|
||||
content_name.clone(),
|
||||
input.fields.unwrap_or_else(Vec::new),
|
||||
),
|
||||
EventKind::StateEvent => {
|
||||
populate_state_fields(content_name.clone(), input.fields.unwrap_or_else(Vec::new))
|
||||
}
|
||||
};
|
||||
|
||||
fields.sort_unstable_by_key(|field| field.ident.clone().unwrap());
|
||||
@ -62,8 +72,9 @@ impl From<RumaEventInput> for RumaEvent {
|
||||
attrs: input.attrs,
|
||||
content: input.content,
|
||||
content_name,
|
||||
event_type: input.event_type,
|
||||
event_type,
|
||||
fields,
|
||||
is_custom,
|
||||
kind,
|
||||
name,
|
||||
}
|
||||
@ -78,7 +89,19 @@ impl ToTokens for RumaEvent {
|
||||
let attrs = &self.attrs;
|
||||
let content_name = &self.content_name;
|
||||
let event_fields = &self.fields;
|
||||
let event_type = &self.event_type;
|
||||
|
||||
let event_type = if self.is_custom {
|
||||
quote! {
|
||||
crate::EventType::Custom(self.event_type.clone())
|
||||
}
|
||||
} else {
|
||||
let event_type = &self.event_type;
|
||||
|
||||
quote! {
|
||||
#event_type
|
||||
}
|
||||
};
|
||||
|
||||
let name = &self.name;
|
||||
let name_str = format!("{}", name);
|
||||
let content_docstring = format!("The payload for `{}`.", name);
|
||||
@ -87,7 +110,7 @@ impl ToTokens for RumaEvent {
|
||||
Content::Struct(fields) => {
|
||||
quote! {
|
||||
#[doc = #content_docstring]
|
||||
#[derive(Clone, Debug, serde::Serialize)]
|
||||
#[derive(Clone, Debug, PartialEq, serde::Serialize)]
|
||||
pub struct #content_name {
|
||||
#(#fields),*
|
||||
}
|
||||
@ -108,7 +131,7 @@ impl ToTokens for RumaEvent {
|
||||
Content::Struct(fields) => {
|
||||
quote! {
|
||||
#[doc = #content_docstring]
|
||||
#[derive(Clone, Debug, serde::Deserialize)]
|
||||
#[derive(Clone, Debug, PartialEq, serde::Deserialize)]
|
||||
pub struct #content_name {
|
||||
#(#fields),*
|
||||
}
|
||||
@ -117,14 +140,23 @@ impl ToTokens for RumaEvent {
|
||||
Content::Typedef(_) => TokenStream::new(),
|
||||
};
|
||||
|
||||
let field_count = event_fields.len() + 1; // + 1 because of manually adding `event_type`
|
||||
// Custom events will already have an event_type field. All other events need to account
|
||||
// for this field being manually inserted in `Serialize` impls.
|
||||
let event_type_field_count = if self.is_custom { 0 } else { 1 };
|
||||
let field_count = event_fields.len() + event_type_field_count;
|
||||
|
||||
let mut from_str_field_values: Vec<TokenStream> = Vec::with_capacity(event_fields.len());
|
||||
let mut serialize_field_calls: Vec<TokenStream> = Vec::with_capacity(event_fields.len());
|
||||
|
||||
for field in event_fields {
|
||||
let ident = field.ident.clone().unwrap();
|
||||
let ident_str = format!("{}", ident);
|
||||
|
||||
let ident_str = if ident == "event_type" {
|
||||
"type".to_string()
|
||||
} else {
|
||||
format!("{}", ident)
|
||||
};
|
||||
|
||||
let span = field.span();
|
||||
|
||||
let from_str_field_value = if ident == "content" {
|
||||
@ -202,6 +234,23 @@ impl ToTokens for RumaEvent {
|
||||
serialize_field_calls.push(serialize_field_call);
|
||||
}
|
||||
|
||||
let (manually_serialize_type_field, import_event_in_serialize_impl) = if self.is_custom {
|
||||
(TokenStream::new(), TokenStream::new())
|
||||
} else {
|
||||
let manually_serialize_type_field = quote! {
|
||||
state.serialize_field("type", &self.event_type())?;
|
||||
};
|
||||
|
||||
let import_event_in_serialize_impl = quote! {
|
||||
use crate::Event as _;
|
||||
};
|
||||
|
||||
(
|
||||
manually_serialize_type_field,
|
||||
import_event_in_serialize_impl,
|
||||
)
|
||||
};
|
||||
|
||||
let impl_room_event = match self.kind {
|
||||
EventKind::RoomEvent | EventKind::StateEvent => {
|
||||
quote! {
|
||||
@ -260,7 +309,7 @@ impl ToTokens for RumaEvent {
|
||||
|
||||
let output = quote!(
|
||||
#(#attrs)*
|
||||
#[derive(Clone, Debug)]
|
||||
#[derive(Clone, PartialEq, Debug)]
|
||||
pub struct #name {
|
||||
#(#event_fields),*
|
||||
}
|
||||
@ -285,21 +334,18 @@ impl ToTokens for RumaEvent {
|
||||
where
|
||||
S: serde::Serializer
|
||||
{
|
||||
use crate::Event as _;
|
||||
#import_event_in_serialize_impl
|
||||
|
||||
let mut state = serializer.serialize_struct(#name_str, #field_count)?;
|
||||
|
||||
#(#serialize_field_calls)*
|
||||
state.serialize_field("type", &self.event_type())?;
|
||||
#manually_serialize_type_field
|
||||
|
||||
state.end()
|
||||
}
|
||||
}
|
||||
|
||||
impl crate::Event for #name {
|
||||
/// The type of the event.
|
||||
const EVENT_TYPE: crate::EventType = #event_type;
|
||||
|
||||
/// The type of this event's `content` field.
|
||||
type Content = #content_name;
|
||||
|
||||
@ -307,6 +353,11 @@ impl ToTokens for RumaEvent {
|
||||
fn content(&self) -> &Self::Content {
|
||||
&self.content
|
||||
}
|
||||
|
||||
/// The type of the event.
|
||||
fn event_type(&self) -> crate::EventType {
|
||||
#event_type
|
||||
}
|
||||
}
|
||||
|
||||
#impl_room_event
|
||||
@ -318,7 +369,7 @@ impl ToTokens for RumaEvent {
|
||||
use super::*;
|
||||
|
||||
#(#attrs)*
|
||||
#[derive(Clone, Debug, serde::Deserialize)]
|
||||
#[derive(Clone, Debug, PartialEq, serde::Deserialize)]
|
||||
pub struct #name {
|
||||
#(#event_fields),*
|
||||
}
|
||||
@ -332,10 +383,24 @@ impl ToTokens for RumaEvent {
|
||||
}
|
||||
|
||||
/// Fills in the event's struct definition with fields common to all basic events.
|
||||
fn populate_event_fields(content_name: Ident, mut fields: Vec<Field>) -> Vec<Field> {
|
||||
let punctuated_fields: Punctuated<ParsableNamedField, Token![,]> = parse_quote! {
|
||||
/// The event's content.
|
||||
pub content: #content_name,
|
||||
fn populate_event_fields(
|
||||
is_custom: bool,
|
||||
content_name: Ident,
|
||||
mut fields: Vec<Field>,
|
||||
) -> Vec<Field> {
|
||||
let punctuated_fields: Punctuated<ParsableNamedField, Token![,]> = if is_custom {
|
||||
parse_quote! {
|
||||
/// The event's content.
|
||||
pub content: #content_name,
|
||||
|
||||
/// The custom type of the event.
|
||||
pub event_type: String,
|
||||
}
|
||||
} else {
|
||||
parse_quote! {
|
||||
/// The event's content.
|
||||
pub content: #content_name,
|
||||
}
|
||||
};
|
||||
|
||||
let mut additional_fields = Vec::with_capacity(punctuated_fields.len());
|
||||
@ -350,8 +415,12 @@ fn populate_event_fields(content_name: Ident, mut fields: Vec<Field>) -> Vec<Fie
|
||||
}
|
||||
|
||||
/// Fills in the event's struct definition with fields common to all room events.
|
||||
fn populate_room_event_fields(content_name: Ident, fields: Vec<Field>) -> Vec<Field> {
|
||||
let mut fields = populate_event_fields(content_name, fields);
|
||||
fn populate_room_event_fields(
|
||||
is_custom: bool,
|
||||
content_name: Ident,
|
||||
fields: Vec<Field>,
|
||||
) -> Vec<Field> {
|
||||
let mut fields = populate_event_fields(is_custom, content_name, fields);
|
||||
|
||||
let punctuated_fields: Punctuated<ParsableNamedField, Token![,]> = parse_quote! {
|
||||
/// The unique identifier for the event.
|
||||
@ -383,8 +452,8 @@ fn populate_room_event_fields(content_name: Ident, fields: Vec<Field>) -> Vec<Fi
|
||||
}
|
||||
|
||||
/// Fills in the event's struct definition with fields common to all state events.
|
||||
fn populate_state_fields(content_name: Ident, fields: Vec<Field>) -> Vec<Field> {
|
||||
let mut fields = populate_room_event_fields(content_name.clone(), fields);
|
||||
fn populate_state_fields(is_custom: bool, content_name: Ident, fields: Vec<Field>) -> Vec<Field> {
|
||||
let mut fields = populate_room_event_fields(is_custom, content_name.clone(), fields);
|
||||
|
||||
let punctuated_fields: Punctuated<ParsableNamedField, Token![,]> = parse_quote! {
|
||||
/// The previous content for this state key, if any.
|
||||
@ -405,6 +474,11 @@ fn populate_state_fields(content_name: Ident, fields: Vec<Field>) -> Vec<Field>
|
||||
fields
|
||||
}
|
||||
|
||||
/// Checks if the given `Path` refers to `EventType::Custom`.
|
||||
fn is_custom_event_type(event_type: &Path) -> bool {
|
||||
event_type.segments.last().unwrap().value().ident == "Custom"
|
||||
}
|
||||
|
||||
/// A wrapper around `syn::Field` that makes it possible to parse `Punctuated<Field, Token![,]>`
|
||||
/// from a `TokenStream`.
|
||||
///
|
||||
|
@ -3,7 +3,7 @@ use std::fmt::Debug;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
/// The type of an event.
|
||||
#[derive(Clone, Copy, Debug, Deserialize, Serialize)]
|
||||
#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)]
|
||||
pub enum EventType {
|
||||
/// m.direct
|
||||
Direct,
|
||||
@ -13,6 +13,9 @@ pub enum EventType {
|
||||
|
||||
/// m.room.redaction
|
||||
RoomRedaction,
|
||||
|
||||
/// Any event that is not part of the specification.
|
||||
Custom(String),
|
||||
}
|
||||
|
||||
/// A basic event.
|
||||
@ -20,9 +23,6 @@ pub trait Event
|
||||
where
|
||||
Self: Debug + Serialize,
|
||||
{
|
||||
/// The type of the event.
|
||||
const EVENT_TYPE: EventType;
|
||||
|
||||
/// The type of this event's `content` field.
|
||||
type Content: Debug + Serialize;
|
||||
|
||||
@ -30,9 +30,7 @@ where
|
||||
fn content(&self) -> &Self::Content;
|
||||
|
||||
/// The type of the event.
|
||||
fn event_type(&self) -> EventType {
|
||||
Self::EVENT_TYPE
|
||||
}
|
||||
fn event_type(&self) -> EventType;
|
||||
}
|
||||
|
||||
/// An event within the context of a room.
|
||||
@ -91,6 +89,23 @@ pub mod common_case {
|
||||
}
|
||||
}
|
||||
|
||||
pub mod custom_event_type {
|
||||
use ruma_events_macros::ruma_event;
|
||||
use serde_json::Value;
|
||||
|
||||
ruma_event! {
|
||||
/// A custom event.
|
||||
CustomEvent {
|
||||
kind: Event,
|
||||
event_type: Custom,
|
||||
content_type_alias: {
|
||||
/// The payload for `CustomEvent`.
|
||||
Value
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub mod extra_fields {
|
||||
use ruma_events_macros::ruma_event;
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user