Fix redacted aliases event deserialization

This commit is contained in:
Devin R 2020-07-13 17:11:31 -04:00 committed by Jonas Platte
parent 3f951e931b
commit 6f69a6fe76
No known key found for this signature in database
GPG Key ID: CC154DE0E30B7C67
6 changed files with 92 additions and 32 deletions

View File

@ -151,11 +151,18 @@ fn expand_deserialize_event(
if name == "content" {
if is_generic && ident.to_string().contains("Redacted") {
quote! {
let content = if !C::has_deserialize_fields() {
C::empty(&event_type).map_err(A::Error::custom)?
} else {
let json = content.ok_or_else(|| ::serde::de::Error::missing_field("content"))?;
C::from_parts(&event_type, json).map_err(A::Error::custom)?
let content = match C::has_deserialize_fields() {
::ruma_events::HasDeserializeFields::False => {
C::empty(&event_type).map_err(A::Error::custom)?
},
::ruma_events::HasDeserializeFields::True => {
let json = content.ok_or_else(|| ::serde::de::Error::missing_field("content"))?;
C::from_parts(&event_type, json).map_err(A::Error::custom)?
},
::ruma_events::HasDeserializeFields::Optional => {
let json = content.unwrap_or(::serde_json::value::RawValue::from_string("{}".to_string()).unwrap());
C::from_parts(&event_type, json).map_err(A::Error::custom)?
},
};
}
} else if is_generic {

View File

@ -135,7 +135,13 @@ pub fn expand_event_content(input: &DeriveInput, emit_redacted: bool) -> syn::Re
)
};
let has_fields = if kept_redacted_fields.is_empty() {
let has_deserialize_fields = if kept_redacted_fields.is_empty() {
quote! { ::ruma_events::HasDeserializeFields::False }
} else {
quote! { ::ruma_events::HasDeserializeFields::True }
};
let has_serialize_fields = if kept_redacted_fields.is_empty() {
quote! { false }
} else {
quote! { true }
@ -170,11 +176,11 @@ pub fn expand_event_content(input: &DeriveInput, emit_redacted: bool) -> syn::Re
}
fn has_serialize_fields(&self) -> bool {
#has_fields
#has_serialize_fields
}
fn has_deserialize_fields() -> bool {
#has_fields
fn has_deserialize_fields() -> ::ruma_events::HasDeserializeFields {
#has_deserialize_fields
}
}
}

View File

@ -4,9 +4,9 @@ use serde::Serialize;
use serde_json::{value::RawValue as RawJsonValue, Value as JsonValue};
use crate::{
BasicEventContent, EphemeralRoomEventContent, EventContent, MessageEventContent,
RedactedEventContent, RedactedMessageEventContent, RedactedStateEventContent, RoomEventContent,
StateEventContent,
BasicEventContent, EphemeralRoomEventContent, EventContent, HasDeserializeFields,
MessageEventContent, RedactedEventContent, RedactedMessageEventContent,
RedactedStateEventContent, RoomEventContent, StateEventContent,
};
/// A custom event's type and `content` JSON object.
@ -83,8 +83,8 @@ impl RedactedEventContent for RedactedCustomEventContent {
false
}
fn has_deserialize_fields() -> bool {
false
fn has_deserialize_fields() -> HasDeserializeFields {
HasDeserializeFields::False
}
}

View File

@ -243,20 +243,27 @@ pub trait StateEventContent: RoomEventContent {}
/// The base trait that all redacted event content types implement.
///
/// Implementing this trait allows content types to be serialized as well as deserialized.
/// This trait's associated functions and methods should not be used to build
/// redacted events, prefer the `redact` method on `AnyStateEvent` and
/// `AnyMessageEvent` and their "sync" and "stripped" counterparts. The
/// `RedactedEventContent` trait is an implementation detail, ruma makes no
/// API guarantees.
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) -> Result<Self, serde_json::Error> {
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.
fn has_deserialize_fields() -> bool;
#[doc(hidden)]
fn has_deserialize_fields() -> HasDeserializeFields;
}
/// Marker trait for the content of a redacted message event.
@ -265,6 +272,21 @@ pub trait RedactedMessageEventContent: RedactedEventContent {}
/// Marker trait for the content of a redacted state event.
pub trait RedactedStateEventContent: RedactedEventContent {}
/// `HasDeserializeFields` is used in the code generated by the `Event` derive
/// to aid in deserializing redacted events.
#[doc(hidden)]
#[derive(Debug)]
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,
}
/// Helper struct to determine if the event has been redacted.
#[doc(hidden)]
#[derive(Debug, Deserialize)]

View File

@ -5,7 +5,9 @@ use ruma_identifiers::RoomAliasId;
use serde::{Deserialize, Serialize};
use serde_json::value::RawValue as RawJsonValue;
use crate::{EventContent, RedactedEventContent, RedactedStateEventContent, StateEvent};
use crate::{
EventContent, HasDeserializeFields, RedactedEventContent, RedactedStateEventContent, StateEvent,
};
/// Informs the room about what room aliases it has been given.
pub type AliasesEvent = StateEvent<AliasesEventContent>;
@ -53,8 +55,8 @@ impl RedactedEventContent for RedactedAliasesEventContent {
self.aliases.is_some()
}
fn has_deserialize_fields() -> bool {
true
fn has_deserialize_fields() -> HasDeserializeFields {
HasDeserializeFields::Optional
}
}

View File

@ -19,10 +19,6 @@ use ruma_events::{
use ruma_identifiers::{EventId, RoomId, UserId};
use serde_json::{from_value as from_json_value, json, to_value as to_json_value};
fn is_zst<T>(_: &T) -> bool {
std::mem::size_of::<T>() == 0
}
fn full_unsigned() -> UnsignedData {
let mut unsigned = UnsignedData::default();
// The presence of `redacted_because` triggers the event enum to return early
@ -63,7 +59,7 @@ fn redacted_message_event_serialize() {
}
#[test]
fn redacted_aliases_event_serialize() {
fn redacted_aliases_event_serialize_no_content() {
let redacted = RedactedSyncStateEvent {
content: RedactedAliasesEventContent { aliases: None },
event_id: EventId::try_from("$h29iv0s8:example.com").unwrap(),
@ -85,6 +81,32 @@ fn redacted_aliases_event_serialize() {
assert_eq!(actual, expected);
}
#[test]
fn redacted_aliases_event_serialize_with_content() {
let redacted = RedactedSyncStateEvent {
content: RedactedAliasesEventContent { aliases: Some(vec![]) },
event_id: EventId::try_from("$h29iv0s8:example.com").unwrap(),
state_key: "".to_string(),
origin_server_ts: UNIX_EPOCH + Duration::from_millis(1),
sender: UserId::try_from("@carl:example.com").unwrap(),
unsigned: UnsignedData::default(),
};
let expected = json!({
"content": {
"aliases": []
},
"event_id": "$h29iv0s8:example.com",
"state_key": "",
"origin_server_ts": 1,
"sender": "@carl:example.com",
"type": "m.room.aliases"
});
let actual = to_json_value(&redacted).unwrap();
assert_eq!(actual, expected);
}
#[test]
fn redacted_aliases_deserialize() {
let unsigned = full_unsigned();
@ -101,14 +123,15 @@ fn redacted_aliases_deserialize() {
let actual = to_json_value(&redacted).unwrap();
assert_matches!(
from_json_value::<EventJson<AnyRoomEventStub>>(actual)
from_json_value::<EventJson<AnySyncRoomEvent>>(actual)
.unwrap()
.deserialize()
.unwrap(),
AnyRoomEventStub::RedactedState(AnyRedactedStateEventStub::RoomAliases(RedactedStateEventStub {
event_id, content, ..
AnySyncRoomEvent::RedactedState(AnyRedactedSyncStateEvent::RoomAliases(RedactedSyncStateEvent {
content: RedactedAliasesEventContent { aliases },
event_id, ..
})) if event_id == EventId::try_from("$h29iv0s8:example.com").unwrap()
&& is_zst(&content)
&& aliases.is_none()
)
}
@ -133,10 +156,10 @@ fn redacted_deserialize_any_room() {
.deserialize()
.unwrap(),
AnyRoomEvent::RedactedMessage(AnyRedactedMessageEvent::RoomMessage(RedactedMessageEvent {
event_id, room_id, content, ..
content: RedactedMessageEventContent,
event_id, room_id, ..
})) if event_id == EventId::try_from("$h29iv0s8:example.com").unwrap()
&& room_id == RoomId::try_from("!roomid:room.com").unwrap()
&& is_zst(&content)
)
}
@ -172,9 +195,9 @@ fn redacted_deserialize_any_room_sync() {
.deserialize()
.unwrap(),
AnySyncRoomEvent::RedactedMessage(AnyRedactedSyncMessageEvent::RoomMessage(RedactedSyncMessageEvent {
event_id, content, ..
content: RedactedMessageEventContent,
event_id, ..
})) if event_id == EventId::try_from("$h29iv0s8:example.com").unwrap()
&& is_zst(&content)
)
}