events: Don't skip serializing empty content of redacted events

This commit is contained in:
Jonas Platte 2022-11-08 19:32:53 +01:00 committed by Jonas Platte
parent 6d133971b0
commit 94990f60f2
7 changed files with 77 additions and 191 deletions

View File

@ -3,10 +3,10 @@ use serde_json::value::RawValue as RawJsonValue;
use super::{ use super::{
EphemeralRoomEventContent, EphemeralRoomEventType, EventContent, GlobalAccountDataEventContent, EphemeralRoomEventContent, EphemeralRoomEventType, EventContent, GlobalAccountDataEventContent,
GlobalAccountDataEventType, HasDeserializeFields, MessageLikeEventContent, GlobalAccountDataEventType, MessageLikeEventContent, MessageLikeEventType, RedactContent,
MessageLikeEventType, RedactContent, RedactedEventContent, RedactedMessageLikeEventContent, RedactedEventContent, RedactedMessageLikeEventContent, RedactedStateEventContent,
RedactedStateEventContent, RoomAccountDataEventContent, RoomAccountDataEventType, RoomAccountDataEventContent, RoomAccountDataEventType, StateEventContent, StateEventType,
StateEventContent, StateEventType, StateUnsigned, ToDeviceEventContent, ToDeviceEventType, StateUnsigned, ToDeviceEventContent, ToDeviceEventType,
}; };
use crate::RoomVersionId; use crate::RoomVersionId;
@ -48,19 +48,7 @@ macro_rules! custom_room_event_content {
} }
} }
impl RedactedEventContent for $i { impl RedactedEventContent for $i {}
fn empty(event_type: &str) -> serde_json::Result<Self> {
Ok(Self { event_type: event_type.into() })
}
fn has_serialize_fields(&self) -> bool {
false
}
fn has_deserialize_fields() -> HasDeserializeFields {
HasDeserializeFields::False
}
}
}; };
} }

View File

@ -45,41 +45,7 @@ where
/// `AnyMessageLikeEvent` and their "sync" and "stripped" counterparts. /// `AnyMessageLikeEvent` and their "sync" and "stripped" counterparts.
/// The `RedactedEventContent` trait is an implementation detail, ruma makes no /// The `RedactedEventContent` trait is an implementation detail, ruma makes no
/// API guarantees. /// API guarantees.
pub trait RedactedEventContent: EventContent { pub trait RedactedEventContent: EventContent {}
/// Constructs the redacted event content.
///
/// If called for anything but "empty" redacted content this will error.
#[doc(hidden)]
fn empty(_event_type: &str) -> serde_json::Result<Self> {
Err(serde::de::Error::custom("this event is not redacted"))
}
/// Determines if the redacted event content needs to serialize fields.
#[doc(hidden)]
fn has_serialize_fields(&self) -> bool;
/// Determines if the redacted event content needs to deserialize fields.
#[doc(hidden)]
fn has_deserialize_fields() -> HasDeserializeFields;
}
/// `HasDeserializeFields` is used in the code generated by the `Event` derive
/// to aid in deserializing redacted events.
#[doc(hidden)]
#[derive(Debug)]
#[allow(clippy::exhaustive_enums)]
pub enum HasDeserializeFields {
/// Deserialize the event's content, failing if invalid.
True,
/// Return the redacted version of this event's content.
False,
/// `Optional` is used for `RedactedAliasesEventContent` since it has
/// an empty version and one with content left after redaction that
/// must be supported together.
Optional,
}
/// Trait for abstracting over event content structs. /// Trait for abstracting over event content structs.
/// ///

View File

@ -6,8 +6,8 @@ use serde_json::value::RawValue as RawJsonValue;
use crate::{ use crate::{
events::{ events::{
EventContent, HasDeserializeFields, RedactContent, RedactedEventContent, EventContent, RedactContent, RedactedEventContent, RedactedStateEventContent,
RedactedStateEventContent, StateEventContent, StateEventType, StateUnsigned, StateEventContent, StateEventType, StateUnsigned,
}, },
OwnedRoomAliasId, OwnedServerName, RoomVersionId, OwnedRoomAliasId, OwnedServerName, RoomVersionId,
}; };
@ -57,6 +57,7 @@ pub struct RedactedRoomAliasesEventContent {
/// ///
/// According to the Matrix spec version 1 redaction rules allowed this field to be /// According to the Matrix spec version 1 redaction rules allowed this field to be
/// kept after redaction, this was changed in version 6. /// kept after redaction, this was changed in version 6.
#[serde(skip_serializing_if = "Option::is_none")]
pub aliases: Option<Vec<OwnedRoomAliasId>>, pub aliases: Option<Vec<OwnedRoomAliasId>>,
} }
@ -105,12 +106,4 @@ impl RedactedStateEventContent for RedactedRoomAliasesEventContent {}
// Since this redacted event has fields we leave the default `empty` method // Since this redacted event has fields we leave the default `empty` method
// that will error if called. // that will error if called.
impl RedactedEventContent for RedactedRoomAliasesEventContent { impl RedactedEventContent for RedactedRoomAliasesEventContent {}
fn has_serialize_fields(&self) -> bool {
self.aliases.is_some()
}
fn has_deserialize_fields() -> HasDeserializeFields {
HasDeserializeFields::Optional
}
}

View File

@ -11,9 +11,9 @@ use serde_json::{from_str as from_json_str, value::RawValue as RawJsonValue};
use crate::{ use crate::{
events::{ events::{
AnyStrippedStateEvent, EventContent, HasDeserializeFields, RedactContent, AnyStrippedStateEvent, EventContent, RedactContent, RedactedEventContent,
RedactedEventContent, RedactedStateEventContent, Relations, StateEventContent, RedactedStateEventContent, Relations, StateEventContent, StateEventType, StateUnsigned,
StateEventType, StateUnsigned, StateUnsignedFromParts, StaticEventContent, StateUnsignedFromParts, StaticEventContent,
}, },
serde::{CanBeEmpty, Raw, StringEnum}, serde::{CanBeEmpty, Raw, StringEnum},
OwnedMxcUri, OwnedServerName, OwnedServerSigningKeyId, OwnedTransactionId, OwnedUserId, OwnedMxcUri, OwnedServerName, OwnedServerSigningKeyId, OwnedTransactionId, OwnedUserId,
@ -170,7 +170,7 @@ pub struct RedactedRoomMemberEventContent {
/// ///
/// This is redacted in room versions 8 and below. It is used for validating /// This is redacted in room versions 8 and below. It is used for validating
/// joins when the join rule is restricted. /// joins when the join rule is restricted.
#[serde(rename = "join_authorised_via_users_server")] #[serde(rename = "join_authorised_via_users_server", skip_serializing_if = "Option::is_none")]
pub join_authorized_via_users_server: Option<OwnedUserId>, pub join_authorized_via_users_server: Option<OwnedUserId>,
} }
@ -218,15 +218,7 @@ impl RedactedStateEventContent for RedactedRoomMemberEventContent {}
// Since this redacted event has fields we leave the default `empty` method // Since this redacted event has fields we leave the default `empty` method
// that will error if called. // that will error if called.
impl RedactedEventContent for RedactedRoomMemberEventContent { impl RedactedEventContent for RedactedRoomMemberEventContent {}
fn has_serialize_fields(&self) -> bool {
true
}
fn has_deserialize_fields() -> HasDeserializeFields {
HasDeserializeFields::Optional
}
}
impl RoomMemberEvent { impl RoomMemberEvent {
/// Obtain the membership state, regardless of whether this event is redacted. /// Obtain the membership state, regardless of whether this event is redacted.

View File

@ -49,6 +49,7 @@ fn redacted_message_event_serialize() {
}; };
let expected = json!({ let expected = json!({
"content": {},
"event_id": "$h29iv0s8:example.com", "event_id": "$h29iv0s8:example.com",
"origin_server_ts": 1, "origin_server_ts": 1,
"sender": "@carl:example.com", "sender": "@carl:example.com",
@ -60,7 +61,7 @@ fn redacted_message_event_serialize() {
} }
#[test] #[test]
fn redacted_aliases_event_serialize_no_content() { fn redacted_aliases_event_serialize_empty_content() {
let redacted = RedactedSyncStateEvent { let redacted = RedactedSyncStateEvent {
content: RedactedRoomAliasesEventContent::default(), content: RedactedRoomAliasesEventContent::default(),
event_id: event_id!("$h29iv0s8:example.com").to_owned(), event_id: event_id!("$h29iv0s8:example.com").to_owned(),
@ -71,6 +72,7 @@ fn redacted_aliases_event_serialize_no_content() {
}; };
let expected = json!({ let expected = json!({
"content": {},
"event_id": "$h29iv0s8:example.com", "event_id": "$h29iv0s8:example.com",
"state_key": "example.com", "state_key": "example.com",
"origin_server_ts": 1, "origin_server_ts": 1,
@ -111,6 +113,7 @@ fn redacted_aliases_event_serialize_with_content() {
#[test] #[test]
fn redacted_aliases_deserialize() { fn redacted_aliases_deserialize() {
let redacted = json!({ let redacted = json!({
"content": {},
"event_id": "$h29iv0s8:example.com", "event_id": "$h29iv0s8:example.com",
"origin_server_ts": 1, "origin_server_ts": 1,
"sender": "@carl:example.com", "sender": "@carl:example.com",
@ -134,6 +137,7 @@ fn redacted_aliases_deserialize() {
#[test] #[test]
fn redacted_deserialize_any_room() { fn redacted_deserialize_any_room() {
let redacted = json!({ let redacted = json!({
"content": {},
"event_id": "$h29iv0s8:example.com", "event_id": "$h29iv0s8:example.com",
"room_id": "!roomid:room.com", "room_id": "!roomid:room.com",
"origin_server_ts": 1, "origin_server_ts": 1,
@ -171,6 +175,7 @@ fn redacted_deserialize_any_room_sync() {
}))); })));
let redacted = json!({ let redacted = json!({
"content": {},
"event_id": "$h29iv0s8:example.com", "event_id": "$h29iv0s8:example.com",
"origin_server_ts": 1, "origin_server_ts": 1,
"sender": "@carl:example.com", "sender": "@carl:example.com",
@ -217,6 +222,7 @@ fn redacted_state_event_deserialize() {
#[test] #[test]
fn redacted_custom_event_deserialize() { fn redacted_custom_event_deserialize() {
let redacted = json!({ let redacted = json!({
"content": {},
"event_id": "$h29iv0s8:example.com", "event_id": "$h29iv0s8:example.com",
"origin_server_ts": 1, "origin_server_ts": 1,
"sender": "@carl:example.com", "sender": "@carl:example.com",

View File

@ -80,10 +80,8 @@ fn expand_serialize_event(
let name = field.ident.as_ref().unwrap(); let name = field.ident.as_ref().unwrap();
if name == "content" && var.is_redacted() { if name == "content" && var.is_redacted() {
quote! { quote! {
if #ruma_common::events::RedactedEventContent::has_serialize_fields(&self.content) {
state.serialize_field("content", &self.content)?; state.serialize_field("content", &self.content)?;
} }
}
} else if name == "unsigned" { } else if name == "unsigned" {
quote! { quote! {
if !#ruma_common::serde::is_empty(&self.unsigned) { if !#ruma_common::serde::is_empty(&self.unsigned) {
@ -188,25 +186,12 @@ fn expand_deserialize_event(
Ok(if name == "content" { Ok(if name == "content" {
if is_generic && var.is_redacted() { if is_generic && var.is_redacted() {
quote! { quote! {
let content = match C::has_deserialize_fields() { let content = {
#ruma_common::events::HasDeserializeFields::False => {
C::empty(&event_type).map_err(#serde::de::Error::custom)?
},
#ruma_common::events::HasDeserializeFields::True => {
let json = content.ok_or_else( let json = content.ok_or_else(
|| #serde::de::Error::missing_field("content"), || #serde::de::Error::missing_field("content"),
)?; )?;
C::from_parts(&event_type, &json) C::from_parts(&event_type, &json)
.map_err(#serde::de::Error::custom)? .map_err(#serde::de::Error::custom)?
},
#ruma_common::events::HasDeserializeFields::Optional => {
let json = content.unwrap_or(
#serde_json::value::RawValue::from_string("{}".to_owned())
.unwrap()
);
C::from_parts(&event_type, &json)
.map_err(#serde::de::Error::custom)?
},
}; };
} }
} else if is_generic { } else if is_generic {

View File

@ -391,7 +391,6 @@ fn generate_redacted_event_content<'a>(
); );
let serde = quote! { #ruma_common::exports::serde }; let serde = quote! { #ruma_common::exports::serde };
let serde_json = quote! { #ruma_common::exports::serde_json };
let doc = format!("Redacted form of [`{ident}`]"); let doc = format!("Redacted form of [`{ident}`]");
let redacted_ident = format_ident!("Redacted{ident}"); let redacted_ident = format_ident!("Redacted{ident}");
@ -428,34 +427,13 @@ fn generate_redacted_event_content<'a>(
let redaction_struct_fields = kept_redacted_fields.iter().flat_map(|f| &f.ident); let redaction_struct_fields = kept_redacted_fields.iter().flat_map(|f| &f.ident);
let (redacted_fields, redacted_return) = if kept_redacted_fields.is_empty() {
(quote! { ; }, quote! { Ok(#redacted_ident {}) })
} else {
(
quote! {
{ #( #kept_redacted_fields, )* }
},
quote! {
Err(#serde::de::Error::custom(
format!("this redacted event has fields that cannot be constructed")
))
},
)
};
let (has_deserialize_fields, has_serialize_fields) = if kept_redacted_fields.is_empty() {
(quote! { #ruma_common::events::HasDeserializeFields::False }, quote! { false })
} else {
(quote! { #ruma_common::events::HasDeserializeFields::True }, quote! { true })
};
let constructor = kept_redacted_fields.is_empty().then(|| { let constructor = kept_redacted_fields.is_empty().then(|| {
let doc = format!("Creates an empty {redacted_ident}."); let doc = format!("Creates an empty {redacted_ident}.");
quote! { quote! {
impl #redacted_ident { impl #redacted_ident {
#[doc = #doc] #[doc = #doc]
pub fn new() -> Self { pub fn new() -> Self {
Self Self {}
} }
} }
} }
@ -479,12 +457,6 @@ fn generate_redacted_event_content<'a>(
generate_static_event_content_impl(&redacted_ident, kind, true, event_type, ruma_common) generate_static_event_content_impl(&redacted_ident, kind, true, event_type, ruma_common)
}); });
let mut event_types = aliases.to_owned();
event_types.push(event_type);
let event_types = quote! {
[#(#event_types,)*]
};
Ok(quote! { Ok(quote! {
// this is the non redacted event content's impl // this is the non redacted event content's impl
#[automatically_derived] #[automatically_derived]
@ -501,32 +473,16 @@ fn generate_redacted_event_content<'a>(
#[doc = #doc] #[doc = #doc]
#[derive(Clone, Debug, #serde::Deserialize, #serde::Serialize)] #[derive(Clone, Debug, #serde::Deserialize, #serde::Serialize)]
#[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)] #[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)]
pub struct #redacted_ident #redacted_fields pub struct #redacted_ident {
#( #kept_redacted_fields, )*
}
#constructor #constructor
#redacted_event_content #redacted_event_content
#[automatically_derived] #[automatically_derived]
impl #ruma_common::events::RedactedEventContent for #redacted_ident { impl #ruma_common::events::RedactedEventContent for #redacted_ident {}
fn empty(ev_type: &str) -> #serde_json::Result<Self> {
if !#event_types.contains(&ev_type) {
return Err(#serde::de::Error::custom(
format!("expected event type as one of `{:?}`, found `{}`", #event_types, ev_type)
));
}
#redacted_return
}
fn has_serialize_fields(&self) -> bool {
#has_serialize_fields
}
fn has_deserialize_fields() -> #ruma_common::events::HasDeserializeFields {
#has_deserialize_fields
}
}
#[automatically_derived] #[automatically_derived]
impl #ruma_common::events::#sub_trait_name for #redacted_ident {} impl #ruma_common::events::#sub_trait_name for #redacted_ident {}