events: Don't skip serializing empty content of redacted events
This commit is contained in:
parent
6d133971b0
commit
94990f60f2
@ -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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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.
|
||||||
///
|
///
|
||||||
|
@ -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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -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.
|
||||||
|
@ -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",
|
||||||
|
@ -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 {
|
||||||
|
@ -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 {}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user