Add RedactionEvent and convert RedactionEventContent to struct def
PresenceEventContent and RedactionEventContent now also derive *EventContent
This commit is contained in:
parent
647130c0fb
commit
1fdf986bae
@ -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! {
|
||||||
|
@ -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)?;
|
||||||
|
|
||||||
|
@ -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 {
|
||||||
|
@ -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")]
|
||||||
|
@ -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()
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user