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.
|
/// Struct fields of the event.
|
||||||
fields: Vec<Field>,
|
fields: Vec<Field>,
|
||||||
|
|
||||||
|
/// Whether or not the event type is `EventType::Custom`.
|
||||||
|
is_custom: bool,
|
||||||
|
|
||||||
/// The kind of event.
|
/// The kind of event.
|
||||||
kind: EventKind,
|
kind: EventKind,
|
||||||
|
|
||||||
@ -42,18 +45,25 @@ impl From<RumaEventInput> for RumaEvent {
|
|||||||
let kind = input.kind;
|
let kind = input.kind;
|
||||||
let name = input.name;
|
let name = input.name;
|
||||||
let content_name = Ident::new(&format!("{}Content", &name), Span::call_site());
|
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 {
|
let mut fields = match kind {
|
||||||
EventKind::Event => {
|
EventKind::Event => populate_event_fields(
|
||||||
populate_event_fields(content_name.clone(), input.fields.unwrap_or_else(Vec::new))
|
is_custom,
|
||||||
}
|
content_name.clone(),
|
||||||
EventKind::RoomEvent => populate_room_event_fields(
|
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(),
|
content_name.clone(),
|
||||||
input.fields.unwrap_or_else(Vec::new),
|
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());
|
fields.sort_unstable_by_key(|field| field.ident.clone().unwrap());
|
||||||
@ -62,8 +72,9 @@ impl From<RumaEventInput> for RumaEvent {
|
|||||||
attrs: input.attrs,
|
attrs: input.attrs,
|
||||||
content: input.content,
|
content: input.content,
|
||||||
content_name,
|
content_name,
|
||||||
event_type: input.event_type,
|
event_type,
|
||||||
fields,
|
fields,
|
||||||
|
is_custom,
|
||||||
kind,
|
kind,
|
||||||
name,
|
name,
|
||||||
}
|
}
|
||||||
@ -78,7 +89,19 @@ impl ToTokens for RumaEvent {
|
|||||||
let attrs = &self.attrs;
|
let attrs = &self.attrs;
|
||||||
let content_name = &self.content_name;
|
let content_name = &self.content_name;
|
||||||
let event_fields = &self.fields;
|
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 = &self.name;
|
||||||
let name_str = format!("{}", name);
|
let name_str = format!("{}", name);
|
||||||
let content_docstring = format!("The payload for `{}`.", name);
|
let content_docstring = format!("The payload for `{}`.", name);
|
||||||
@ -87,7 +110,7 @@ impl ToTokens for RumaEvent {
|
|||||||
Content::Struct(fields) => {
|
Content::Struct(fields) => {
|
||||||
quote! {
|
quote! {
|
||||||
#[doc = #content_docstring]
|
#[doc = #content_docstring]
|
||||||
#[derive(Clone, Debug, serde::Serialize)]
|
#[derive(Clone, Debug, PartialEq, serde::Serialize)]
|
||||||
pub struct #content_name {
|
pub struct #content_name {
|
||||||
#(#fields),*
|
#(#fields),*
|
||||||
}
|
}
|
||||||
@ -108,7 +131,7 @@ impl ToTokens for RumaEvent {
|
|||||||
Content::Struct(fields) => {
|
Content::Struct(fields) => {
|
||||||
quote! {
|
quote! {
|
||||||
#[doc = #content_docstring]
|
#[doc = #content_docstring]
|
||||||
#[derive(Clone, Debug, serde::Deserialize)]
|
#[derive(Clone, Debug, PartialEq, serde::Deserialize)]
|
||||||
pub struct #content_name {
|
pub struct #content_name {
|
||||||
#(#fields),*
|
#(#fields),*
|
||||||
}
|
}
|
||||||
@ -117,14 +140,23 @@ impl ToTokens for RumaEvent {
|
|||||||
Content::Typedef(_) => TokenStream::new(),
|
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 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());
|
let mut serialize_field_calls: Vec<TokenStream> = Vec::with_capacity(event_fields.len());
|
||||||
|
|
||||||
for field in event_fields {
|
for field in event_fields {
|
||||||
let ident = field.ident.clone().unwrap();
|
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 span = field.span();
|
||||||
|
|
||||||
let from_str_field_value = if ident == "content" {
|
let from_str_field_value = if ident == "content" {
|
||||||
@ -202,6 +234,23 @@ impl ToTokens for RumaEvent {
|
|||||||
serialize_field_calls.push(serialize_field_call);
|
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 {
|
let impl_room_event = match self.kind {
|
||||||
EventKind::RoomEvent | EventKind::StateEvent => {
|
EventKind::RoomEvent | EventKind::StateEvent => {
|
||||||
quote! {
|
quote! {
|
||||||
@ -260,7 +309,7 @@ impl ToTokens for RumaEvent {
|
|||||||
|
|
||||||
let output = quote!(
|
let output = quote!(
|
||||||
#(#attrs)*
|
#(#attrs)*
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, PartialEq, Debug)]
|
||||||
pub struct #name {
|
pub struct #name {
|
||||||
#(#event_fields),*
|
#(#event_fields),*
|
||||||
}
|
}
|
||||||
@ -285,21 +334,18 @@ impl ToTokens for RumaEvent {
|
|||||||
where
|
where
|
||||||
S: serde::Serializer
|
S: serde::Serializer
|
||||||
{
|
{
|
||||||
use crate::Event as _;
|
#import_event_in_serialize_impl
|
||||||
|
|
||||||
let mut state = serializer.serialize_struct(#name_str, #field_count)?;
|
let mut state = serializer.serialize_struct(#name_str, #field_count)?;
|
||||||
|
|
||||||
#(#serialize_field_calls)*
|
#(#serialize_field_calls)*
|
||||||
state.serialize_field("type", &self.event_type())?;
|
#manually_serialize_type_field
|
||||||
|
|
||||||
state.end()
|
state.end()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl crate::Event for #name {
|
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.
|
/// The type of this event's `content` field.
|
||||||
type Content = #content_name;
|
type Content = #content_name;
|
||||||
|
|
||||||
@ -307,6 +353,11 @@ impl ToTokens for RumaEvent {
|
|||||||
fn content(&self) -> &Self::Content {
|
fn content(&self) -> &Self::Content {
|
||||||
&self.content
|
&self.content
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// The type of the event.
|
||||||
|
fn event_type(&self) -> crate::EventType {
|
||||||
|
#event_type
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#impl_room_event
|
#impl_room_event
|
||||||
@ -318,7 +369,7 @@ impl ToTokens for RumaEvent {
|
|||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
#(#attrs)*
|
#(#attrs)*
|
||||||
#[derive(Clone, Debug, serde::Deserialize)]
|
#[derive(Clone, Debug, PartialEq, serde::Deserialize)]
|
||||||
pub struct #name {
|
pub struct #name {
|
||||||
#(#event_fields),*
|
#(#event_fields),*
|
||||||
}
|
}
|
||||||
@ -332,10 +383,24 @@ impl ToTokens for RumaEvent {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Fills in the event's struct definition with fields common to all basic events.
|
/// 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> {
|
fn populate_event_fields(
|
||||||
let punctuated_fields: Punctuated<ParsableNamedField, Token![,]> = parse_quote! {
|
is_custom: bool,
|
||||||
/// The event's content.
|
content_name: Ident,
|
||||||
pub content: #content_name,
|
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());
|
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.
|
/// 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> {
|
fn populate_room_event_fields(
|
||||||
let mut fields = populate_event_fields(content_name, 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! {
|
let punctuated_fields: Punctuated<ParsableNamedField, Token![,]> = parse_quote! {
|
||||||
/// The unique identifier for the event.
|
/// 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.
|
/// 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> {
|
fn populate_state_fields(is_custom: bool, content_name: Ident, fields: Vec<Field>) -> Vec<Field> {
|
||||||
let mut fields = populate_room_event_fields(content_name.clone(), fields);
|
let mut fields = populate_room_event_fields(is_custom, content_name.clone(), fields);
|
||||||
|
|
||||||
let punctuated_fields: Punctuated<ParsableNamedField, Token![,]> = parse_quote! {
|
let punctuated_fields: Punctuated<ParsableNamedField, Token![,]> = parse_quote! {
|
||||||
/// The previous content for this state key, if any.
|
/// 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
|
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![,]>`
|
/// A wrapper around `syn::Field` that makes it possible to parse `Punctuated<Field, Token![,]>`
|
||||||
/// from a `TokenStream`.
|
/// from a `TokenStream`.
|
||||||
///
|
///
|
||||||
|
@ -3,7 +3,7 @@ use std::fmt::Debug;
|
|||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
/// The type of an event.
|
/// The type of an event.
|
||||||
#[derive(Clone, Copy, Debug, Deserialize, Serialize)]
|
#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)]
|
||||||
pub enum EventType {
|
pub enum EventType {
|
||||||
/// m.direct
|
/// m.direct
|
||||||
Direct,
|
Direct,
|
||||||
@ -13,6 +13,9 @@ pub enum EventType {
|
|||||||
|
|
||||||
/// m.room.redaction
|
/// m.room.redaction
|
||||||
RoomRedaction,
|
RoomRedaction,
|
||||||
|
|
||||||
|
/// Any event that is not part of the specification.
|
||||||
|
Custom(String),
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A basic event.
|
/// A basic event.
|
||||||
@ -20,9 +23,6 @@ pub trait Event
|
|||||||
where
|
where
|
||||||
Self: Debug + Serialize,
|
Self: Debug + Serialize,
|
||||||
{
|
{
|
||||||
/// The type of the event.
|
|
||||||
const EVENT_TYPE: EventType;
|
|
||||||
|
|
||||||
/// The type of this event's `content` field.
|
/// The type of this event's `content` field.
|
||||||
type Content: Debug + Serialize;
|
type Content: Debug + Serialize;
|
||||||
|
|
||||||
@ -30,9 +30,7 @@ where
|
|||||||
fn content(&self) -> &Self::Content;
|
fn content(&self) -> &Self::Content;
|
||||||
|
|
||||||
/// The type of the event.
|
/// The type of the event.
|
||||||
fn event_type(&self) -> EventType {
|
fn event_type(&self) -> EventType;
|
||||||
Self::EVENT_TYPE
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// An event within the context of a room.
|
/// 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 {
|
pub mod extra_fields {
|
||||||
use ruma_events_macros::ruma_event;
|
use ruma_events_macros::ruma_event;
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user