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> {
|
||||
let ident = &input.ident;
|
||||
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() {
|
||||
if let Fields::Named(FieldsNamed { named, .. }) = fields {
|
||||
@ -67,14 +67,6 @@ pub fn expand_event(input: DeriveInput) -> syn::Result<TokenStream> {
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
let event_ty = if is_presence_event {
|
||||
quote! {
|
||||
"m.presence";
|
||||
}
|
||||
} else {
|
||||
quote! { self.content.event_type(); }
|
||||
};
|
||||
|
||||
let serialize_impl = quote! {
|
||||
impl #impl_gen ::serde::ser::Serialize for #ident #ty_gen #where_clause {
|
||||
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 _};
|
||||
|
||||
let event_type = #event_ty;
|
||||
let event_type = ::ruma_events::EventContent::event_type(&self.content);
|
||||
|
||||
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! {
|
||||
#serialize_impl
|
||||
@ -104,7 +96,7 @@ pub fn expand_event(input: DeriveInput) -> syn::Result<TokenStream> {
|
||||
}
|
||||
|
||||
fn expand_deserialize_event(
|
||||
is_presence_event: bool,
|
||||
is_generic: bool,
|
||||
input: DeriveInput,
|
||||
fields: Vec<Field>,
|
||||
) -> syn::Result<TokenStream> {
|
||||
@ -126,10 +118,10 @@ fn expand_deserialize_event(
|
||||
let name = field.ident.as_ref().unwrap();
|
||||
let ty = &field.ty;
|
||||
if name == "content" || name == "prev_content" {
|
||||
if is_presence_event {
|
||||
quote! { #content_ident }
|
||||
} else {
|
||||
if is_generic {
|
||||
quote! { Box<::serde_json::value::RawValue> }
|
||||
} else {
|
||||
quote! { #content_ident }
|
||||
}
|
||||
} else if name == "origin_server_ts" {
|
||||
quote! { ::js_int::UInt }
|
||||
@ -144,23 +136,33 @@ fn expand_deserialize_event(
|
||||
.map(|field| {
|
||||
let name = field.ident.as_ref().unwrap();
|
||||
if name == "content" {
|
||||
if is_presence_event {
|
||||
quote! {
|
||||
let content = content.ok_or_else(|| ::serde::de::Error::missing_field("content"))?;
|
||||
}
|
||||
} else {
|
||||
if is_generic {
|
||||
quote! {
|
||||
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)?;
|
||||
}
|
||||
} else {
|
||||
quote! {
|
||||
let content = content.ok_or_else(|| ::serde::de::Error::missing_field("content"))?;
|
||||
}
|
||||
}
|
||||
} else if name == "prev_content" {
|
||||
quote! {
|
||||
let prev_content = if let Some(json) = prev_content {
|
||||
Some(C::from_parts(&event_type, json).map_err(A::Error::custom)?)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
if is_generic {
|
||||
quote! {
|
||||
let prev_content = if let Some(json) = prev_content {
|
||||
Some(C::from_parts(&event_type, json).map_err(A::Error::custom)?)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
}
|
||||
} else {
|
||||
quote! {
|
||||
let prev_content = if let Some(content) = prev_content {
|
||||
Some(content)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
}
|
||||
}
|
||||
} else if name == "origin_server_ts" {
|
||||
quote! {
|
||||
@ -185,16 +187,16 @@ fn expand_deserialize_event(
|
||||
|
||||
let field_names = fields.iter().flat_map(|f| &f.ident).collect::<Vec<_>>();
|
||||
|
||||
let deserialize_impl_gen = if is_presence_event {
|
||||
quote! { <'de> }
|
||||
} else {
|
||||
let deserialize_impl_gen = if is_generic {
|
||||
let gen = &input.generics.params;
|
||||
quote! { <'de, #gen> }
|
||||
};
|
||||
let deserialize_phantom_type = if is_presence_event {
|
||||
quote! {}
|
||||
} else {
|
||||
quote! { <'de> }
|
||||
};
|
||||
let deserialize_phantom_type = if is_generic {
|
||||
quote! { ::std::marker::PhantomData }
|
||||
} else {
|
||||
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 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.
|
||||
///
|
||||
/// This is used internally for code sharing as `RoomEventContent` is not derivable.
|
||||
fn expand_room_event_content(input: DeriveInput) -> syn::Result<TokenStream> {
|
||||
pub fn expand_room_event_content(input: DeriveInput) -> syn::Result<TokenStream> {
|
||||
let ident = input.ident.clone();
|
||||
let event_content_impl = expand_event_content(input)?;
|
||||
|
||||
|
@ -18,8 +18,8 @@ use self::{
|
||||
content_enum::{expand_content_enum, parse::ContentEnumInput},
|
||||
event::expand_event,
|
||||
event_content::{
|
||||
expand_basic_event_content, expand_ephemeral_room_event_content,
|
||||
expand_message_event_content, expand_state_event_content,
|
||||
expand_basic_event_content, expand_ephemeral_room_event_content, expand_event_content,
|
||||
expand_message_event_content, expand_room_event_content, expand_state_event_content,
|
||||
},
|
||||
gen::RumaEvent,
|
||||
parse::RumaEventInput,
|
||||
@ -137,6 +137,15 @@ pub fn event_content_enum(input: TokenStream) -> TokenStream {
|
||||
.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.
|
||||
#[proc_macro_derive(BasicEventContent, attributes(ruma_event))]
|
||||
pub fn derive_basic_event_content(input: TokenStream) -> TokenStream {
|
||||
@ -146,6 +155,15 @@ pub fn derive_basic_event_content(input: TokenStream) -> TokenStream {
|
||||
.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.
|
||||
#[proc_macro_derive(MessageEventContent, attributes(ruma_event))]
|
||||
pub fn derive_message_event_content(input: TokenStream) -> TokenStream {
|
||||
|
@ -4,7 +4,7 @@
|
||||
|
||||
use js_int::UInt;
|
||||
pub use ruma_common::presence::PresenceState;
|
||||
use ruma_events_macros::Event;
|
||||
use ruma_events_macros::{Event, EventContent};
|
||||
use ruma_identifiers::UserId;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
@ -22,7 +22,8 @@ pub struct PresenceEvent {
|
||||
///
|
||||
/// This is the only event content a `PresenceEvent` can contain as it's
|
||||
/// `content` field.
|
||||
#[derive(Clone, Debug, Deserialize, Serialize)]
|
||||
#[derive(Clone, Debug, Deserialize, Serialize, EventContent)]
|
||||
#[ruma_event(type = "m.presence")]
|
||||
pub struct PresenceEventContent {
|
||||
/// The current avatar URL for this user.
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
|
@ -1,21 +1,123 @@
|
||||
//! Types for the *m.room.redaction* event.
|
||||
|
||||
use ruma_events_macros::ruma_event;
|
||||
use ruma_identifiers::EventId;
|
||||
use std::time::SystemTime;
|
||||
|
||||
ruma_event! {
|
||||
/// A redaction of an event.
|
||||
RedactionEvent {
|
||||
kind: RoomEvent,
|
||||
event_type: "m.room.redaction",
|
||||
fields: {
|
||||
/// The ID of the event that was redacted.
|
||||
pub redacts: EventId,
|
||||
},
|
||||
content: {
|
||||
/// The reason for the redaction, if any.
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub reason: Option<String>,
|
||||
}
|
||||
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,
|
||||
|
||||
/// The ID of the event that was redacted.
|
||||
pub redacts: EventId,
|
||||
|
||||
/// 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.
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
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