Add RedactionEvent and convert RedactionEventContent to struct def

PresenceEventContent and RedactionEventContent now also derive *EventContent
This commit is contained in:
Ragotzy.devin 2020-06-09 06:43:21 -04:00 committed by GitHub
parent 647130c0fb
commit 1fdf986bae
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 178 additions and 56 deletions

View File

@ -8,7 +8,7 @@ use syn::{Data, DataStruct, DeriveInput, Field, Fields, FieldsNamed, Ident};
pub fn expand_event(input: DeriveInput) -> syn::Result<TokenStream> { pub fn expand_event(input: DeriveInput) -> syn::Result<TokenStream> {
let ident = &input.ident; let ident = &input.ident;
let (impl_gen, ty_gen, where_clause) = input.generics.split_for_impl(); let (impl_gen, ty_gen, where_clause) = input.generics.split_for_impl();
let is_presence_event = ident == "PresenceEvent"; let is_generic = !input.generics.params.is_empty();
let fields = if let Data::Struct(DataStruct { fields, .. }) = input.data.clone() { let fields = if let Data::Struct(DataStruct { fields, .. }) = input.data.clone() {
if let Fields::Named(FieldsNamed { named, .. }) = fields { if let Fields::Named(FieldsNamed { named, .. }) = fields {
@ -67,14 +67,6 @@ pub fn expand_event(input: DeriveInput) -> syn::Result<TokenStream> {
}) })
.collect::<Vec<_>>(); .collect::<Vec<_>>();
let event_ty = if is_presence_event {
quote! {
"m.presence";
}
} else {
quote! { self.content.event_type(); }
};
let serialize_impl = quote! { let serialize_impl = quote! {
impl #impl_gen ::serde::ser::Serialize for #ident #ty_gen #where_clause { impl #impl_gen ::serde::ser::Serialize for #ident #ty_gen #where_clause {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
@ -83,7 +75,7 @@ pub fn expand_event(input: DeriveInput) -> syn::Result<TokenStream> {
{ {
use ::serde::ser::{SerializeStruct as _, Error as _}; use ::serde::ser::{SerializeStruct as _, Error as _};
let event_type = #event_ty; let event_type = ::ruma_events::EventContent::event_type(&self.content);
let mut state = serializer.serialize_struct(stringify!(#ident), 7)?; let mut state = serializer.serialize_struct(stringify!(#ident), 7)?;
@ -94,7 +86,7 @@ pub fn expand_event(input: DeriveInput) -> syn::Result<TokenStream> {
} }
}; };
let deserialize_impl = expand_deserialize_event(is_presence_event, input, fields)?; let deserialize_impl = expand_deserialize_event(is_generic, input, fields)?;
Ok(quote! { Ok(quote! {
#serialize_impl #serialize_impl
@ -104,7 +96,7 @@ pub fn expand_event(input: DeriveInput) -> syn::Result<TokenStream> {
} }
fn expand_deserialize_event( fn expand_deserialize_event(
is_presence_event: bool, is_generic: bool,
input: DeriveInput, input: DeriveInput,
fields: Vec<Field>, fields: Vec<Field>,
) -> syn::Result<TokenStream> { ) -> syn::Result<TokenStream> {
@ -126,10 +118,10 @@ fn expand_deserialize_event(
let name = field.ident.as_ref().unwrap(); let name = field.ident.as_ref().unwrap();
let ty = &field.ty; let ty = &field.ty;
if name == "content" || name == "prev_content" { if name == "content" || name == "prev_content" {
if is_presence_event { if is_generic {
quote! { #content_ident }
} else {
quote! { Box<::serde_json::value::RawValue> } quote! { Box<::serde_json::value::RawValue> }
} else {
quote! { #content_ident }
} }
} else if name == "origin_server_ts" { } else if name == "origin_server_ts" {
quote! { ::js_int::UInt } quote! { ::js_int::UInt }
@ -144,17 +136,18 @@ fn expand_deserialize_event(
.map(|field| { .map(|field| {
let name = field.ident.as_ref().unwrap(); let name = field.ident.as_ref().unwrap();
if name == "content" { if name == "content" {
if is_presence_event { if is_generic {
quote! {
let content = content.ok_or_else(|| ::serde::de::Error::missing_field("content"))?;
}
} else {
quote! { quote! {
let json = content.ok_or_else(|| ::serde::de::Error::missing_field("content"))?; let json = content.ok_or_else(|| ::serde::de::Error::missing_field("content"))?;
let content = C::from_parts(&event_type, json).map_err(A::Error::custom)?; let content = C::from_parts(&event_type, json).map_err(A::Error::custom)?;
} }
} else {
quote! {
let content = content.ok_or_else(|| ::serde::de::Error::missing_field("content"))?;
}
} }
} else if name == "prev_content" { } else if name == "prev_content" {
if is_generic {
quote! { quote! {
let prev_content = if let Some(json) = prev_content { let prev_content = if let Some(json) = prev_content {
Some(C::from_parts(&event_type, json).map_err(A::Error::custom)?) Some(C::from_parts(&event_type, json).map_err(A::Error::custom)?)
@ -162,6 +155,15 @@ fn expand_deserialize_event(
None None
}; };
} }
} else {
quote! {
let prev_content = if let Some(content) = prev_content {
Some(content)
} else {
None
};
}
}
} else if name == "origin_server_ts" { } else if name == "origin_server_ts" {
quote! { quote! {
let origin_server_ts = origin_server_ts let origin_server_ts = origin_server_ts
@ -185,16 +187,16 @@ fn expand_deserialize_event(
let field_names = fields.iter().flat_map(|f| &f.ident).collect::<Vec<_>>(); let field_names = fields.iter().flat_map(|f| &f.ident).collect::<Vec<_>>();
let deserialize_impl_gen = if is_presence_event { let deserialize_impl_gen = if is_generic {
quote! { <'de> }
} else {
let gen = &input.generics.params; let gen = &input.generics.params;
quote! { <'de, #gen> } quote! { <'de, #gen> }
};
let deserialize_phantom_type = if is_presence_event {
quote! {}
} else { } else {
quote! { <'de> }
};
let deserialize_phantom_type = if is_generic {
quote! { ::std::marker::PhantomData } quote! { ::std::marker::PhantomData }
} else {
quote! {}
}; };
Ok(quote! { Ok(quote! {

View File

@ -23,7 +23,8 @@ impl Parse for EventMeta {
} }
} }
fn expand_event_content(input: DeriveInput) -> syn::Result<TokenStream> { /// Create an `EventContent` implementation for a struct.
pub fn expand_event_content(input: DeriveInput) -> syn::Result<TokenStream> {
let ident = &input.ident; let ident = &input.ident;
let event_type_attr = input let event_type_attr = input
@ -89,9 +90,7 @@ pub fn expand_ephemeral_room_event_content(input: DeriveInput) -> syn::Result<To
} }
/// Create a `RoomEventContent` implementation for a struct. /// Create a `RoomEventContent` implementation for a struct.
/// pub fn expand_room_event_content(input: DeriveInput) -> syn::Result<TokenStream> {
/// This is used internally for code sharing as `RoomEventContent` is not derivable.
fn expand_room_event_content(input: DeriveInput) -> syn::Result<TokenStream> {
let ident = input.ident.clone(); let ident = input.ident.clone();
let event_content_impl = expand_event_content(input)?; let event_content_impl = expand_event_content(input)?;

View File

@ -18,8 +18,8 @@ use self::{
content_enum::{expand_content_enum, parse::ContentEnumInput}, content_enum::{expand_content_enum, parse::ContentEnumInput},
event::expand_event, event::expand_event,
event_content::{ event_content::{
expand_basic_event_content, expand_ephemeral_room_event_content, expand_basic_event_content, expand_ephemeral_room_event_content, expand_event_content,
expand_message_event_content, expand_state_event_content, expand_message_event_content, expand_room_event_content, expand_state_event_content,
}, },
gen::RumaEvent, gen::RumaEvent,
parse::RumaEventInput, parse::RumaEventInput,
@ -137,6 +137,15 @@ pub fn event_content_enum(input: TokenStream) -> TokenStream {
.into() .into()
} }
/// Generates an implementation of `ruma_events::EventContent`.
#[proc_macro_derive(EventContent, attributes(ruma_event))]
pub fn derive_event_content(input: TokenStream) -> TokenStream {
let input = parse_macro_input!(input as DeriveInput);
expand_event_content(input)
.unwrap_or_else(|err| err.to_compile_error())
.into()
}
/// Generates an implementation of `ruma_events::BasicEventContent` and it's super traits. /// Generates an implementation of `ruma_events::BasicEventContent` and it's super traits.
#[proc_macro_derive(BasicEventContent, attributes(ruma_event))] #[proc_macro_derive(BasicEventContent, attributes(ruma_event))]
pub fn derive_basic_event_content(input: TokenStream) -> TokenStream { pub fn derive_basic_event_content(input: TokenStream) -> TokenStream {
@ -146,6 +155,15 @@ pub fn derive_basic_event_content(input: TokenStream) -> TokenStream {
.into() .into()
} }
/// Generates an implementation of `ruma_events::RoomEventContent` and it's super traits.
#[proc_macro_derive(RoomEventContent, attributes(ruma_event))]
pub fn derive_room_event_content(input: TokenStream) -> TokenStream {
let input = parse_macro_input!(input as DeriveInput);
expand_room_event_content(input)
.unwrap_or_else(|err| err.to_compile_error())
.into()
}
/// Generates an implementation of `ruma_events::MessageEventContent` and it's super traits. /// Generates an implementation of `ruma_events::MessageEventContent` and it's super traits.
#[proc_macro_derive(MessageEventContent, attributes(ruma_event))] #[proc_macro_derive(MessageEventContent, attributes(ruma_event))]
pub fn derive_message_event_content(input: TokenStream) -> TokenStream { pub fn derive_message_event_content(input: TokenStream) -> TokenStream {

View File

@ -4,7 +4,7 @@
use js_int::UInt; use js_int::UInt;
pub use ruma_common::presence::PresenceState; pub use ruma_common::presence::PresenceState;
use ruma_events_macros::Event; use ruma_events_macros::{Event, EventContent};
use ruma_identifiers::UserId; use ruma_identifiers::UserId;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
@ -22,7 +22,8 @@ pub struct PresenceEvent {
/// ///
/// This is the only event content a `PresenceEvent` can contain as it's /// This is the only event content a `PresenceEvent` can contain as it's
/// `content` field. /// `content` field.
#[derive(Clone, Debug, Deserialize, Serialize)] #[derive(Clone, Debug, Deserialize, Serialize, EventContent)]
#[ruma_event(type = "m.presence")]
pub struct PresenceEventContent { pub struct PresenceEventContent {
/// The current avatar URL for this user. /// The current avatar URL for this user.
#[serde(skip_serializing_if = "Option::is_none")] #[serde(skip_serializing_if = "Option::is_none")]

View File

@ -1,21 +1,123 @@
//! Types for the *m.room.redaction* event. //! Types for the *m.room.redaction* event.
use ruma_events_macros::ruma_event; use std::time::SystemTime;
use ruma_identifiers::EventId;
use ruma_events_macros::{Event, EventContent};
use ruma_identifiers::{EventId, RoomId, UserId};
use serde::{Deserialize, Serialize};
use crate::UnsignedData;
/// Redaction event.
#[derive(Clone, Debug, Event)]
pub struct RedactionEvent {
/// Data specific to the event type.
pub content: RedactionEventContent,
ruma_event! {
/// A redaction of an event.
RedactionEvent {
kind: RoomEvent,
event_type: "m.room.redaction",
fields: {
/// The ID of the event that was redacted. /// The ID of the event that was redacted.
pub redacts: EventId, pub redacts: EventId,
},
content: { /// The globally unique event identifier for the user who sent the event.
pub event_id: EventId,
/// The fully-qualified ID of the user who sent this event.
pub sender: UserId,
/// Timestamp in milliseconds on originating homeserver when this event was sent.
pub origin_server_ts: SystemTime,
/// The ID of the room associated with this event.
pub room_id: RoomId,
/// Additional key-value pairs not signed by the homeserver.
pub unsigned: UnsignedData,
}
/// A redaction of an event.
#[derive(Clone, Debug, Deserialize, Serialize, EventContent)]
#[ruma_event(type = "m.room.redaction")]
pub struct RedactionEventContent {
/// The reason for the redaction, if any. /// The reason for the redaction, if any.
#[serde(skip_serializing_if = "Option::is_none")] #[serde(skip_serializing_if = "Option::is_none")]
pub reason: Option<String>, pub reason: Option<String>,
}
#[cfg(test)]
mod tests {
use std::{
convert::TryFrom,
time::{Duration, UNIX_EPOCH},
};
use matches::assert_matches;
use ruma_identifiers::{EventId, RoomId, UserId};
use serde_json::{from_value as from_json_value, json, to_value as to_json_value};
use super::{RedactionEvent, RedactionEventContent};
use crate::{EventJson, UnsignedData};
#[test]
fn serialization() {
let event = RedactionEvent {
content: RedactionEventContent {
reason: Some("redacted because".into()),
},
redacts: EventId::try_from("$h29iv0s8:example.com").unwrap(),
event_id: EventId::try_from("$h29iv0s8:example.com").unwrap(),
origin_server_ts: UNIX_EPOCH + Duration::from_millis(1),
room_id: RoomId::try_from("!roomid:room.com").unwrap(),
sender: UserId::try_from("@carl:example.com").unwrap(),
unsigned: UnsignedData::default(),
};
let json = json!({
"content": {
"reason": "redacted because"
},
"event_id": "$h29iv0s8:example.com",
"origin_server_ts": 1,
"redacts": "$h29iv0s8:example.com",
"room_id": "!roomid:room.com",
"sender": "@carl:example.com",
"type": "m.room.redaction",
});
assert_eq!(to_json_value(&event).unwrap(), json);
} }
#[test]
fn deserialization() {
let e_id = EventId::try_from("$h29iv0s8:example.com").unwrap();
let json = json!({
"content": {
"reason": "redacted because"
},
"event_id": "$h29iv0s8:example.com",
"origin_server_ts": 1,
"redacts": "$h29iv0s8:example.com",
"room_id": "!roomid:room.com",
"sender": "@carl:example.com",
"type": "m.room.redaction",
});
assert_matches!(
from_json_value::<EventJson<RedactionEvent>>(json)
.unwrap()
.deserialize()
.unwrap(),
RedactionEvent {
content: RedactionEventContent {
reason: Some(reason),
},
sender,
event_id, origin_server_ts, redacts, room_id, unsigned,
} if reason == "redacted because" && redacts == e_id
&& event_id == e_id
&& sender == "@carl:example.com"
&& origin_server_ts == UNIX_EPOCH + Duration::from_millis(1)
&& room_id == RoomId::try_from("!roomid:room.com").unwrap()
&& unsigned.is_empty()
);
} }
} }