diff --git a/crates/ruma-common/src/events.rs b/crates/ruma-common/src/events.rs index 063a0434..30ec011b 100644 --- a/crates/ruma-common/src/events.rs +++ b/crates/ruma-common/src/events.rs @@ -188,13 +188,10 @@ pub mod video; #[cfg(feature = "unstable-msc2675")] pub use self::relation::Relations; -#[doc(hidden)] -#[cfg(feature = "compat")] -pub use self::unsigned::{RedactedUnsignedWithPrevContent, UnsignedWithPrevContent}; pub use self::{ enums::*, event_kinds::*, - unsigned::{RedactedUnsigned, Unsigned}, + unsigned::{MessageLikeUnsigned, RedactedUnsigned, StateUnsigned}, }; /// The base trait that all event content types implement. diff --git a/crates/ruma-common/src/events/event_kinds.rs b/crates/ruma-common/src/events/event_kinds.rs index 61fe67ec..de7ac3a4 100644 --- a/crates/ruma-common/src/events/event_kinds.rs +++ b/crates/ruma-common/src/events/event_kinds.rs @@ -5,8 +5,8 @@ use serde::{Deserialize, Serialize}; use super::{ EphemeralRoomEventType, EventContent, GlobalAccountDataEventType, MessageLikeEventType, - RedactedEventContent, RedactedUnsigned, RoomAccountDataEventType, StateEventType, - ToDeviceEventType, Unsigned, + MessageLikeUnsigned, RedactedEventContent, RedactedUnsigned, RoomAccountDataEventType, + StateEventType, StateUnsigned, ToDeviceEventType, }; use crate::{EventId, MilliSecondsSinceUnixEpoch, RoomId, UserId}; @@ -63,7 +63,7 @@ pub struct MessageLikeEvent> { pub room_id: Box, /// Additional key-value pairs not signed by the homeserver. - pub unsigned: Unsigned, + pub unsigned: MessageLikeUnsigned, } /// A message-like event without a `room_id`. @@ -85,7 +85,7 @@ pub struct SyncMessageLikeEvent> { /// affects. pub state_key: String, - /// Optional previous content for this event. - pub prev_content: Option, - /// Additional key-value pairs not signed by the homeserver. - pub unsigned: Unsigned, + pub unsigned: StateUnsigned, } /// A state event without a `room_id`. @@ -197,11 +194,8 @@ pub struct SyncStateEvent> { /// affects. pub state_key: String, - /// Optional previous content for this event. - pub prev_content: Option, - /// Additional key-value pairs not signed by the homeserver. - pub unsigned: Unsigned, + pub unsigned: StateUnsigned, } /// A stripped-down state event, used for previews of rooms the user has been invited to. diff --git a/crates/ruma-common/src/events/policy/rule/room.rs b/crates/ruma-common/src/events/policy/rule/room.rs index 9f392b76..fb89c781 100644 --- a/crates/ruma-common/src/events/policy/rule/room.rs +++ b/crates/ruma-common/src/events/policy/rule/room.rs @@ -27,7 +27,7 @@ mod tests { event_id, events::{ policy::rule::{PolicyRuleEventContent, Recommendation}, - Unsigned, + StateUnsigned, }, room_id, serde::Raw, @@ -42,13 +42,7 @@ mod tests { origin_server_ts: MilliSecondsSinceUnixEpoch(1_432_735_824_653_u64.try_into().unwrap()), room_id: room_id!("!jEsUZKDJdhlrceRyVU:example.org").to_owned(), state_key: "rule:#*:example.org".into(), - prev_content: None, - unsigned: Unsigned { - age: Some(int!(1234)), - transaction_id: None, - #[cfg(feature = "unstable-msc2675")] - relations: None, - }, + unsigned: StateUnsigned { age: Some(int!(1234)), ..StateUnsigned::default() }, content: PolicyRuleRoomEventContent(PolicyRuleEventContent { entity: "#*:example.org".into(), reason: "undesirable content".into(), diff --git a/crates/ruma-common/src/events/room/canonical_alias.rs b/crates/ruma-common/src/events/room/canonical_alias.rs index 9fadfcf8..cf5176e8 100644 --- a/crates/ruma-common/src/events/room/canonical_alias.rs +++ b/crates/ruma-common/src/events/room/canonical_alias.rs @@ -44,7 +44,7 @@ mod tests { use serde_json::{from_value as from_json_value, json, to_value as to_json_value}; use super::RoomCanonicalAliasEventContent; - use crate::events::{StateEvent, Unsigned}; + use crate::events::{StateEvent, StateUnsigned}; #[test] fn serialization_with_optional_fields_as_none() { @@ -55,11 +55,10 @@ mod tests { }, event_id: event_id!("$h29iv0s8:example.com").to_owned(), origin_server_ts: MilliSecondsSinceUnixEpoch(uint!(1)), - prev_content: None, room_id: room_id!("!dummy:example.com").to_owned(), sender: user_id!("@carl:example.com").to_owned(), state_key: "".into(), - unsigned: Unsigned::default(), + unsigned: StateUnsigned::default(), }; let actual = to_json_value(&canonical_alias_event).unwrap(); diff --git a/crates/ruma-common/src/events/room/member.rs b/crates/ruma-common/src/events/room/member.rs index 8bad93a9..26672f49 100644 --- a/crates/ruma-common/src/events/room/member.rs +++ b/crates/ruma-common/src/events/room/member.rs @@ -387,7 +387,12 @@ impl RoomMemberEvent { /// /// [spec]: https://spec.matrix.org/v1.2/client-server-api/#mroommember pub fn membership_change(&self) -> MembershipChange { - membership_change(&self.content, self.prev_content.as_ref(), &self.sender, &self.state_key) + membership_change( + &self.content, + self.unsigned.prev_content.as_ref(), + &self.sender, + &self.state_key, + ) } } @@ -398,7 +403,12 @@ impl SyncStateEvent { /// /// [spec]: https://spec.matrix.org/v1.2/client-server-api/#mroommember pub fn membership_change(&self) -> MembershipChange { - membership_change(&self.content, self.prev_content.as_ref(), &self.sender, &self.state_key) + membership_change( + &self.content, + self.unsigned.prev_content.as_ref(), + &self.sender, + &self.state_key, + ) } } @@ -422,7 +432,7 @@ mod tests { use serde_json::{from_value as from_json_value, json}; use super::{MembershipState, RoomMemberEventContent, SignedContent, ThirdPartyInvite}; - use crate::events::StateEvent; + use crate::events::{StateEvent, StateUnsigned}; #[test] fn serde_with_no_prev_content() { @@ -455,7 +465,6 @@ mod tests { sender, state_key, unsigned, - prev_content: None, } if event_id == "$h29iv0s8:example.com" && origin_server_ts == MilliSecondsSinceUnixEpoch(uint!(1)) && room_id == "!n8f893n9:example.com" @@ -474,12 +483,14 @@ mod tests { }, "event_id": "$h29iv0s8:example.com", "origin_server_ts": 1, - "prev_content": { - "membership": "join" - }, "room_id": "!n8f893n9:example.com", "sender": "@carl:example.com", - "state_key": "example.com" + "state_key": "example.com", + "unsigned": { + "prev_content": { + "membership": "join" + }, + }, }); assert_matches!( @@ -498,21 +509,22 @@ mod tests { room_id, sender, state_key, - unsigned, - prev_content: Some(RoomMemberEventContent { - avatar_url: None, - displayname: None, - is_direct: None, - membership: MembershipState::Join, - third_party_invite: None, + unsigned: StateUnsigned { + prev_content: Some(RoomMemberEventContent { + avatar_url: None, + displayname: None, + is_direct: None, + membership: MembershipState::Join, + third_party_invite: None, + .. + }), .. - }), + }, } if event_id == "$h29iv0s8:example.com" && origin_server_ts == MilliSecondsSinceUnixEpoch(uint!(1)) && room_id == "!n8f893n9:example.com" && sender == "@carl:example.com" && state_key == "example.com" - && unsigned.is_empty() ); } @@ -565,7 +577,6 @@ mod tests { sender, state_key, unsigned, - prev_content: None, } if avatar_url == "mxc://example.org/SEsfnsuifSDFSSEF" && displayname == "Alice Margatroid" && third_party_displayname == "alice" @@ -590,31 +601,33 @@ mod tests { let json = json!({ "type": "m.room.member", "content": { - "membership": "join" + "membership": "join", }, "event_id": "$143273582443PhrSn:example.org", "origin_server_ts": 233, - "prev_content": { - "avatar_url": "mxc://example.org/SEsfnsuifSDFSSEF", - "displayname": "Alice Margatroid", - "is_direct": true, - "membership": "invite", - "third_party_invite": { - "display_name": "alice", - "signed": { - "mxid": "@alice:example.org", - "signatures": { - "magic.forest": { - "ed25519:3": "foobar" - } - }, - "token": "abc123" - } - } - }, "room_id": "!jEsUZKDJdhlrceRyVU:example.org", "sender": "@alice:example.org", - "state_key": "@alice:example.org" + "state_key": "@alice:example.org", + "unsigned": { + "prev_content": { + "avatar_url": "mxc://example.org/SEsfnsuifSDFSSEF", + "displayname": "Alice Margatroid", + "is_direct": true, + "membership": "invite", + "third_party_invite": { + "display_name": "alice", + "signed": { + "mxid": "@alice:example.org", + "signatures": { + "magic.forest": { + "ed25519:3": "foobar", + }, + }, + "token": "abc123" + }, + }, + }, + }, }); assert_matches!( @@ -633,24 +646,25 @@ mod tests { room_id, sender, state_key, - unsigned, - prev_content: Some(RoomMemberEventContent { - avatar_url: Some(avatar_url), - displayname: Some(displayname), - is_direct: Some(true), - membership: MembershipState::Invite, - third_party_invite: Some(ThirdPartyInvite { - display_name: third_party_displayname, - signed: SignedContent { mxid, signatures, token }, + unsigned: StateUnsigned { + prev_content: Some(RoomMemberEventContent { + avatar_url: Some(avatar_url), + displayname: Some(displayname), + is_direct: Some(true), + membership: MembershipState::Invite, + third_party_invite: Some(ThirdPartyInvite { + display_name: third_party_displayname, + signed: SignedContent { mxid, signatures, token }, + }), + .. }), .. - }), + }, } if event_id == "$143273582443PhrSn:example.org" && origin_server_ts == MilliSecondsSinceUnixEpoch(uint!(233)) && room_id == "!jEsUZKDJdhlrceRyVU:example.org" && sender == "@alice:example.org" && state_key == "@alice:example.org" - && unsigned.is_empty() && avatar_url == "mxc://example.org/SEsfnsuifSDFSSEF" && displayname == "Alice Margatroid" && third_party_displayname == "alice" @@ -662,80 +676,6 @@ mod tests { } && token == "abc123" ); - - #[cfg(feature = "compat")] - assert_matches!( - from_json_value::>(json!({ - "type": "m.room.member", - "content": { - "membership": "join" - }, - "event_id": "$143273582443PhrSn:example.org", - "origin_server_ts": 233, - "prev_content": { - "avatar_url": "", - "displayname": "Alice Margatroid", - "is_direct": true, - "membership": "invite", - "third_party_invite": { - "display_name": "alice", - "signed": { - "mxid": "@alice:example.org", - "signatures": { - "magic.forest": { - "ed25519:3": "foobar" - } - }, - "token": "abc123" - } - } - }, - "room_id": "!jEsUZKDJdhlrceRyVU:example.org", - "sender": "@alice:example.org", - "state_key": "@alice:example.org" - })).unwrap(), - StateEvent { - content: RoomMemberEventContent { - avatar_url: None, - displayname: None, - is_direct: None, - membership: MembershipState::Join, - third_party_invite: None, - .. - }, - event_id, - origin_server_ts, - room_id, - sender, - state_key, - unsigned, - prev_content: Some(RoomMemberEventContent { - avatar_url: None, - displayname: Some(displayname), - is_direct: Some(true), - membership: MembershipState::Invite, - third_party_invite: Some(ThirdPartyInvite { - display_name: third_party_displayname, - signed: SignedContent { mxid, signatures, token }, - }), - .. - }), - } if event_id == "$143273582443PhrSn:example.org" - && origin_server_ts == MilliSecondsSinceUnixEpoch(uint!(233)) - && room_id == "!jEsUZKDJdhlrceRyVU:example.org" - && sender == "@alice:example.org" - && state_key == "@alice:example.org" - && unsigned.is_empty() - && displayname == "Alice Margatroid" - && third_party_displayname == "alice" - && mxid == "@alice:example.org" - && signatures == btreemap! { - server_name!("magic.forest").to_owned() => btreemap! { - server_signing_key_id!("ed25519:3").to_owned() => "foobar".to_owned() - } - } - && token == "abc123" - ); } #[test] @@ -771,7 +711,6 @@ mod tests { sender, state_key, unsigned, - prev_content: None, } if event_id == "$h29iv0s8:example.com" && origin_server_ts == MilliSecondsSinceUnixEpoch(uint!(1)) && room_id == "!n8f893n9:example.com" diff --git a/crates/ruma-common/src/events/room/message/reply.rs b/crates/ruma-common/src/events/room/message/reply.rs index 2519845a..b1cb6e7d 100644 --- a/crates/ruma-common/src/events/room/message/reply.rs +++ b/crates/ruma-common/src/events/room/message/reply.rs @@ -299,7 +299,9 @@ fn formatted_or_plain_body<'a>(formatted: &'a Option, body: &'a s #[cfg(test)] mod tests { - use crate::{event_id, room_id, user_id}; + use crate::{ + event_id, events::MessageLikeUnsigned, room_id, user_id, MilliSecondsSinceUnixEpoch, + }; use super::{RoomMessageEvent, RoomMessageEventContent}; @@ -310,9 +312,9 @@ mod tests { content: RoomMessageEventContent::text_plain("multi\nline"), event_id: event_id!("$1598361704261elfgc:localhost").to_owned(), sender: user_id!("@alice:example.com").to_owned(), - origin_server_ts: crate::MilliSecondsSinceUnixEpoch::now(), + origin_server_ts: MilliSecondsSinceUnixEpoch::now(), room_id: room_id!("!n8f893n9:example.com").to_owned(), - unsigned: crate::events::Unsigned::new(), + unsigned: MessageLikeUnsigned::new(), }), "> <@alice:example.com> multi\n> line" ); diff --git a/crates/ruma-common/src/events/room/name.rs b/crates/ruma-common/src/events/room/name.rs index 3df352d0..40781c4c 100644 --- a/crates/ruma-common/src/events/room/name.rs +++ b/crates/ruma-common/src/events/room/name.rs @@ -36,7 +36,7 @@ mod tests { use serde_json::{from_value as from_json_value, json, to_value as to_json_value}; use super::RoomNameEventContent; - use crate::events::{StateEvent, Unsigned}; + use crate::events::{StateEvent, StateUnsigned}; #[test] fn serialization_with_optional_fields_as_none() { @@ -44,11 +44,10 @@ mod tests { content: RoomNameEventContent { name: "The room name".try_into().ok() }, event_id: event_id!("$h29iv0s8:example.com").to_owned(), origin_server_ts: MilliSecondsSinceUnixEpoch(uint!(1)), - prev_content: None, room_id: room_id!("!n8f893n9:example.com").to_owned(), sender: user_id!("@carl:example.com").to_owned(), state_key: "".into(), - unsigned: Unsigned::default(), + unsigned: StateUnsigned::default(), }; let actual = to_json_value(&name_event).unwrap(); @@ -73,11 +72,14 @@ mod tests { content: RoomNameEventContent { name: "The room name".try_into().ok() }, event_id: event_id!("$h29iv0s8:example.com").to_owned(), origin_server_ts: MilliSecondsSinceUnixEpoch(uint!(1)), - prev_content: Some(RoomNameEventContent { name: "The old name".try_into().ok() }), room_id: room_id!("!n8f893n9:example.com").to_owned(), sender: user_id!("@carl:example.com").to_owned(), state_key: "".into(), - unsigned: Unsigned { age: Some(int!(100)), ..Unsigned::default() }, + unsigned: StateUnsigned { + age: Some(int!(100)), + prev_content: Some(RoomNameEventContent { name: "The old name".try_into().ok() }), + ..StateUnsigned::default() + }, }; let actual = to_json_value(&name_event).unwrap(); @@ -87,13 +89,13 @@ mod tests { }, "event_id": "$h29iv0s8:example.com", "origin_server_ts": 1, - "prev_content": { "name": "The old name" }, "room_id": "!n8f893n9:example.com", "sender": "@carl:example.com", "state_key": "", "type": "m.room.name", "unsigned": { - "age": 100 + "age": 100, + "prev_content": { "name": "The old name" }, } }); diff --git a/crates/ruma-common/src/events/room/pinned_events.rs b/crates/ruma-common/src/events/room/pinned_events.rs index b0931e85..7f92a012 100644 --- a/crates/ruma-common/src/events/room/pinned_events.rs +++ b/crates/ruma-common/src/events/room/pinned_events.rs @@ -32,7 +32,7 @@ mod tests { use crate::{server_name, EventId, MilliSecondsSinceUnixEpoch, RoomId, UserId}; use super::RoomPinnedEventsEventContent; - use crate::events::{StateEvent, Unsigned}; + use crate::events::{StateEvent, StateUnsigned}; #[test] fn serialization_deserialization() { @@ -47,11 +47,10 @@ mod tests { content: content.clone(), event_id: EventId::new(server_name), origin_server_ts: MilliSecondsSinceUnixEpoch(1_432_804_485_886_u64.try_into().unwrap()), - prev_content: None, room_id: RoomId::new(server_name), sender: UserId::new(server_name), state_key: "".into(), - unsigned: Unsigned::default(), + unsigned: StateUnsigned::default(), }; let serialized_event = serde_json::to_string(&event).unwrap(); diff --git a/crates/ruma-common/src/events/room/power_levels.rs b/crates/ruma-common/src/events/room/power_levels.rs index 75b640b5..ddcb6f14 100644 --- a/crates/ruma-common/src/events/room/power_levels.rs +++ b/crates/ruma-common/src/events/room/power_levels.rs @@ -182,7 +182,7 @@ mod tests { use serde_json::{json, to_value as to_json_value}; use super::{default_power_level, NotificationPowerLevels, RoomPowerLevelsEventContent}; - use crate::events::{StateEvent, Unsigned}; + use crate::events::{StateEvent, StateUnsigned}; #[test] fn serialization_with_optional_fields_as_none() { @@ -203,9 +203,8 @@ mod tests { }, event_id: event_id!("$h29iv0s8:example.com").to_owned(), origin_server_ts: MilliSecondsSinceUnixEpoch(uint!(1)), - prev_content: None, room_id: room_id!("!n8f893n9:example.com").to_owned(), - unsigned: Unsigned::default(), + unsigned: StateUnsigned::default(), sender: user_id!("@carl:example.com").to_owned(), state_key: "".into(), }; @@ -246,25 +245,29 @@ mod tests { }, event_id: event_id!("$h29iv0s8:example.com").to_owned(), origin_server_ts: MilliSecondsSinceUnixEpoch(uint!(1)), - prev_content: Some(RoomPowerLevelsEventContent { - // Make just one field different so we at least know they're two different objects. - ban: int!(42), - events: btreemap! { - "m.dummy".into() => int!(42) - }, - events_default: int!(42), - invite: int!(42), - kick: int!(42), - redact: int!(42), - state_default: int!(42), - users: btreemap! { - user.to_owned() => int!(42) - }, - users_default: int!(42), - notifications: assign!(NotificationPowerLevels::new(), { room: int!(42) }), - }), room_id: room_id!("!n8f893n9:example.com").to_owned(), - unsigned: Unsigned { age: Some(int!(100)), ..Unsigned::default() }, + unsigned: StateUnsigned { + age: Some(int!(100)), + prev_content: Some(RoomPowerLevelsEventContent { + // Make just one field different so we at least know they're two different + // objects. + ban: int!(42), + events: btreemap! { + "m.dummy".into() => int!(42) + }, + events_default: int!(42), + invite: int!(42), + kick: int!(42), + redact: int!(42), + state_default: int!(42), + users: btreemap! { + user.to_owned() => int!(42) + }, + users_default: int!(42), + notifications: assign!(NotificationPowerLevels::new(), { room: int!(42) }), + }), + ..StateUnsigned::default() + }, sender: user.to_owned(), state_key: "".into(), }; @@ -291,30 +294,30 @@ mod tests { }, "event_id": "$h29iv0s8:example.com", "origin_server_ts": 1, - "prev_content": { - "ban": 42, - "events": { - "m.dummy": 42 - }, - "events_default": 42, - "invite": 42, - "kick": 42, - "redact": 42, - "state_default": 42, - "users": { - "@carl:example.com": 42 - }, - "users_default": 42, - "notifications": { - "room": 42 - } - }, "room_id": "!n8f893n9:example.com", "sender": "@carl:example.com", "state_key": "", "type": "m.room.power_levels", "unsigned": { - "age": 100 + "age": 100, + "prev_content": { + "ban": 42, + "events": { + "m.dummy": 42 + }, + "events_default": 42, + "invite": 42, + "kick": 42, + "redact": 42, + "state_default": 42, + "users": { + "@carl:example.com": 42 + }, + "users_default": 42, + "notifications": { + "room": 42 + }, + }, } }); diff --git a/crates/ruma-common/src/events/room/redaction.rs b/crates/ruma-common/src/events/room/redaction.rs index f8f8e18d..d625ba2f 100644 --- a/crates/ruma-common/src/events/room/redaction.rs +++ b/crates/ruma-common/src/events/room/redaction.rs @@ -6,7 +6,7 @@ use ruma_macros::{Event, EventContent}; use serde::{Deserialize, Serialize}; use crate::{ - events::{Redact, RedactContent, RedactedUnsigned, Unsigned}, + events::{MessageLikeUnsigned, Redact, RedactContent, RedactedUnsigned}, EventId, MilliSecondsSinceUnixEpoch, RoomId, UserId, }; @@ -33,7 +33,7 @@ pub struct RoomRedactionEvent { pub room_id: Box, /// Additional key-value pairs not signed by the homeserver. - pub unsigned: Unsigned, + pub unsigned: MessageLikeUnsigned, } impl Redact for RoomRedactionEvent { @@ -103,7 +103,7 @@ pub struct SyncRoomRedactionEvent { pub origin_server_ts: MilliSecondsSinceUnixEpoch, /// Additional key-value pairs not signed by the homeserver. - pub unsigned: Unsigned, + pub unsigned: MessageLikeUnsigned, } impl Redact for SyncRoomRedactionEvent { diff --git a/crates/ruma-common/src/events/unsigned.rs b/crates/ruma-common/src/events/unsigned.rs index c1837bbc..e524b8da 100644 --- a/crates/ruma-common/src/events/unsigned.rs +++ b/crates/ruma-common/src/events/unsigned.rs @@ -1,15 +1,16 @@ use js_int::Int; use serde::{Deserialize, Serialize}; +use serde_json::{from_str as from_json_str, value::RawValue as RawJsonValue}; #[cfg(feature = "unstable-msc2675")] use super::relation::Relations; -use super::room::redaction::SyncRoomRedactionEvent; -use crate::TransactionId; +use super::{room::redaction::SyncRoomRedactionEvent, EventContent, StateEventType}; +use crate::{serde::Raw, TransactionId}; -/// Extra information about an event that is not incorporated into the event's hash. +/// Extra information about a message event that is not incorporated into the event's hash. #[derive(Clone, Debug, Default, Deserialize, Serialize)] #[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)] -pub struct Unsigned { +pub struct MessageLikeUnsigned { /// The time in milliseconds that has elapsed since the event was sent. /// /// This field is generated by the local homeserver, and may be incorrect if the local time on @@ -29,7 +30,7 @@ pub struct Unsigned { pub relations: Option, } -impl Unsigned { +impl MessageLikeUnsigned { /// Create a new `Unsigned` with fields set to `None`. pub fn new() -> Self { Self::default() @@ -53,6 +54,118 @@ impl Unsigned { } } +/// Extra information about a state event that is not incorporated into the event's hash. +#[derive(Clone, Debug, Deserialize, Serialize)] +#[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)] +pub struct StateUnsigned> { + /// The time in milliseconds that has elapsed since the event was sent. + /// + /// This field is generated by the local homeserver, and may be incorrect if the local time on + /// at least one of the two servers is out of sync, which can cause the age to either be + /// negative or greater than it actually is. + #[serde(skip_serializing_if = "Option::is_none")] + pub age: Option, + + /// The client-supplied transaction ID, if the client being given the event is the same one + /// which sent it. + #[serde(skip_serializing_if = "Option::is_none")] + pub transaction_id: Option>, + + /// Optional previous content of the event. + #[serde(skip_serializing_if = "Option::is_none")] + pub prev_content: Option, + + /// Server-compiled information from other events relating to this event. + #[cfg(feature = "unstable-pre-spec")] + #[serde(rename = "m.relations", skip_serializing_if = "Option::is_none")] + pub relations: Option, +} + +impl> StateUnsigned { + /// Create a new `Unsigned` with fields set to `None`. + pub fn new() -> Self { + Self { + age: None, + transaction_id: None, + prev_content: None, + #[cfg(feature = "unstable-pre-spec")] + relations: None, + } + } + + /// Whether this unsigned data is empty (all fields are `None`). + /// + /// This method is used to determine whether to skip serializing the `unsigned` field in room + /// events. Do not use it to determine whether an incoming `unsigned` field was present - it + /// could still have been present but contained none of the known fields. + pub fn is_empty(&self) -> bool { + #[cfg(not(feature = "unstable-msc2675"))] + { + self.age.is_none() && self.transaction_id.is_none() && self.prev_content.is_none() + } + + #[cfg(feature = "unstable-msc2675")] + { + self.age.is_none() + && self.transaction_id.is_none() + && self.prev_content.is_none() + && self.relations.is_none() + } + } +} + +/// Helper functions for proc-macro code. +/// +/// Needs to be public for UI tests. +#[doc(hidden)] +impl> StateUnsigned { + pub fn _from_parts(event_type: &str, object: &RawJsonValue) -> serde_json::Result { + #[derive(Deserialize)] + #[serde(bound = "")] // Disable default C: Deserialize bound + struct WithRawPrevContent { + #[serde(skip_serializing_if = "Option::is_none")] + age: Option, + #[serde(skip_serializing_if = "Option::is_none")] + transaction_id: Option>, + prev_content: Option>, + #[cfg(feature = "unstable-pre-spec")] + #[serde(rename = "m.relations", skip_serializing_if = "Option::is_none")] + relations: Option, + } + + let raw: WithRawPrevContent = from_json_str(object.get())?; + let prev_content = + raw.prev_content.map(|r| r.deserialize_content(event_type.into())).transpose()?; + + Ok(Self { + age: raw.age, + transaction_id: raw.transaction_id, + #[cfg(feature = "unstable-pre-spec")] + relations: raw.relations, + prev_content, + }) + } + + pub fn _map_prev_unsigned(&self, f: impl FnOnce(&C) -> T) -> StateUnsigned + where + T: EventContent, + { + StateUnsigned { + age: self.age, + transaction_id: self.transaction_id.clone(), + prev_content: self.prev_content.as_ref().map(f), + #[cfg(feature = "unstable-pre-spec")] + relations: self.relations.clone(), + } + } +} + +impl> Default for StateUnsigned { + fn default() -> Self { + Self::new() + } +} + /// Extra information about a redacted event that is not incorporated into the event's hash. #[derive(Clone, Debug, Default, Deserialize, Serialize)] #[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)] @@ -82,49 +195,3 @@ impl RedactedUnsigned { self.redacted_because.is_none() } } - -#[doc(hidden)] -#[cfg(feature = "compat")] -#[derive(Deserialize)] -pub struct UnsignedWithPrevContent { - #[serde(skip_serializing_if = "Option::is_none")] - age: Option, - - #[serde(skip_serializing_if = "Option::is_none")] - transaction_id: Option>, - - #[cfg(feature = "unstable-msc2675")] - #[serde(rename = "m.relations", skip_serializing_if = "Option::is_none")] - relations: Option, - - pub prev_content: Option>, -} - -#[cfg(feature = "compat")] -impl From for Unsigned { - fn from(u: UnsignedWithPrevContent) -> Self { - Self { - age: u.age, - transaction_id: u.transaction_id, - #[cfg(feature = "unstable-msc2675")] - relations: u.relations, - } - } -} - -#[doc(hidden)] -#[cfg(feature = "compat")] -#[derive(Deserialize)] -pub struct RedactedUnsignedWithPrevContent { - #[serde(skip_serializing_if = "Option::is_none")] - redacted_because: Option>, - - pub prev_content: Option>, -} - -#[cfg(feature = "compat")] -impl From for RedactedUnsigned { - fn from(u: RedactedUnsignedWithPrevContent) -> Self { - Self { redacted_because: u.redacted_because } - } -} diff --git a/crates/ruma-common/tests/events/audio.rs b/crates/ruma-common/tests/events/audio.rs index 400d1094..5bbfe2f7 100644 --- a/crates/ruma-common/tests/events/audio.rs +++ b/crates/ruma-common/tests/events/audio.rs @@ -15,7 +15,7 @@ use ruma_common::{ message::{InReplyTo, Relation}, JsonWebKeyInit, }, - AnyMessageLikeEvent, MessageLikeEvent, Unsigned, + AnyMessageLikeEvent, MessageLikeEvent, MessageLikeUnsigned, }, mxc_uri, room_id, serde::Base64, @@ -170,7 +170,7 @@ fn event_serialization() { sender: user_id!("@user:notareal.hs").to_owned(), origin_server_ts: MilliSecondsSinceUnixEpoch(uint!(134_829_848)), room_id: room_id!("!roomid:notareal.hs").to_owned(), - unsigned: Unsigned::default(), + unsigned: MessageLikeUnsigned::default(), }; assert_eq!( diff --git a/crates/ruma-common/tests/events/compat.rs b/crates/ruma-common/tests/events/compat.rs deleted file mode 100644 index 73446120..00000000 --- a/crates/ruma-common/tests/events/compat.rs +++ /dev/null @@ -1,36 +0,0 @@ -#![cfg(feature = "compat")] - -use matches::assert_matches; -use ruma_common::events::room::topic::{RoomTopicEvent, RoomTopicEventContent}; -use serde_json::{from_value as from_json_value, json}; - -#[test] -fn deserialize_unsigned_prev_content() { - let res = from_json_value::(json!({ - "content": { - "topic": "New room topic", - }, - "event_id": "$143273582443PhrSn:example.org", - "origin_server_ts": 1_432_735_824_653_u64, - "room_id": "!jEsUZKDJdhlrceRyVU:example.org", - "sender": "@example:example.org", - "state_key": "", - "type": "m.room.topic", - "unsigned": { - "age": 1234, - "prev_content": { - "topic": "Old room topic", - }, - }, - })); - - assert_matches!( - res, - Ok(RoomTopicEvent { - content: RoomTopicEventContent { topic: new_topic, .. }, - prev_content: Some(RoomTopicEventContent { topic: old_topic, .. }), - .. - }) if new_topic == "New room topic" - && old_topic == "Old room topic" - ); -} diff --git a/crates/ruma-common/tests/events/enums.rs b/crates/ruma-common/tests/events/enums.rs index 68eaee84..1e12d9e0 100644 --- a/crates/ruma-common/tests/events/enums.rs +++ b/crates/ruma-common/tests/events/enums.rs @@ -13,8 +13,8 @@ use ruma_common::{ AnyEphemeralRoomEvent, AnyMessageLikeEvent, AnyRoomEvent, AnyStateEvent, AnyStateEventContent, AnySyncMessageLikeEvent, AnySyncRoomEvent, AnySyncStateEvent, EphemeralRoomEventType, GlobalAccountDataEventType, MessageLikeEvent, MessageLikeEventType, - RoomAccountDataEventType, StateEvent, StateEventType, SyncMessageLikeEvent, SyncStateEvent, - ToDeviceEventType, Unsigned, + MessageLikeUnsigned, RoomAccountDataEventType, StateEvent, StateEventType, + SyncMessageLikeEvent, SyncStateEvent, ToDeviceEventType, }, MilliSecondsSinceUnixEpoch, }; @@ -208,7 +208,7 @@ fn message_event_serialization() { origin_server_ts: MilliSecondsSinceUnixEpoch(uint!(0)), room_id: room_id!("!roomid:example.com").to_owned(), sender: user_id!("@test:example.com").to_owned(), - unsigned: Unsigned::default(), + unsigned: MessageLikeUnsigned::default(), }; assert_eq!( diff --git a/crates/ruma-common/tests/events/file.rs b/crates/ruma-common/tests/events/file.rs index 28796574..821a1ce9 100644 --- a/crates/ruma-common/tests/events/file.rs +++ b/crates/ruma-common/tests/events/file.rs @@ -12,7 +12,7 @@ use ruma_common::{ message::{InReplyTo, Relation}, JsonWebKeyInit, }, - AnyMessageLikeEvent, MessageLikeEvent, Unsigned, + AnyMessageLikeEvent, MessageLikeEvent, MessageLikeUnsigned, }, mxc_uri, room_id, serde::Base64, @@ -117,7 +117,7 @@ fn file_event_serialization() { sender: user_id!("@user:notareal.hs").to_owned(), origin_server_ts: MilliSecondsSinceUnixEpoch(uint!(134_829_848)), room_id: room_id!("!roomid:notareal.hs").to_owned(), - unsigned: Unsigned::default(), + unsigned: MessageLikeUnsigned::default(), }; assert_eq!( diff --git a/crates/ruma-common/tests/events/image.rs b/crates/ruma-common/tests/events/image.rs index 53aef394..41771e63 100644 --- a/crates/ruma-common/tests/events/image.rs +++ b/crates/ruma-common/tests/events/image.rs @@ -16,7 +16,7 @@ use ruma_common::{ message::{InReplyTo, Relation}, JsonWebKeyInit, }, - AnyMessageLikeEvent, MessageLikeEvent, Unsigned, + AnyMessageLikeEvent, MessageLikeEvent, MessageLikeUnsigned, }, mxc_uri, room_id, serde::Base64, @@ -138,7 +138,7 @@ fn image_event_serialization() { sender: user_id!("@user:notareal.hs").to_owned(), origin_server_ts: MilliSecondsSinceUnixEpoch(uint!(134_829_848)), room_id: room_id!("!roomid:notareal.hs").to_owned(), - unsigned: Unsigned::default(), + unsigned: MessageLikeUnsigned::default(), }; assert_eq!( diff --git a/crates/ruma-common/tests/events/location.rs b/crates/ruma-common/tests/events/location.rs index 35d3cdff..a862844f 100644 --- a/crates/ruma-common/tests/events/location.rs +++ b/crates/ruma-common/tests/events/location.rs @@ -12,7 +12,7 @@ use ruma_common::{ }, message::MessageContent, room::message::{InReplyTo, Relation}, - AnyMessageLikeEvent, MessageLikeEvent, Unsigned, + AnyMessageLikeEvent, MessageLikeEvent, MessageLikeUnsigned, }, room_id, user_id, MilliSecondsSinceUnixEpoch, }; @@ -64,7 +64,7 @@ fn event_serialization() { sender: user_id!("@user:notareal.hs").to_owned(), origin_server_ts: MilliSecondsSinceUnixEpoch(uint!(134_829_848)), room_id: room_id!("!roomid:notareal.hs").to_owned(), - unsigned: Unsigned::default(), + unsigned: MessageLikeUnsigned::default(), }; assert_eq!( diff --git a/crates/ruma-common/tests/events/message.rs b/crates/ruma-common/tests/events/message.rs index 484b8095..a49ffa44 100644 --- a/crates/ruma-common/tests/events/message.rs +++ b/crates/ruma-common/tests/events/message.rs @@ -10,7 +10,7 @@ use ruma_common::{ message::MessageEventContent, notice::NoticeEventContent, room::message::{InReplyTo, Relation}, - AnyMessageLikeEvent, MessageLikeEvent, Unsigned, + AnyMessageLikeEvent, MessageLikeEvent, MessageLikeUnsigned, }, room_id, user_id, MilliSecondsSinceUnixEpoch, }; @@ -114,7 +114,7 @@ fn message_event_serialization() { sender: user_id!("@user:notareal.hs").to_owned(), origin_server_ts: MilliSecondsSinceUnixEpoch(uint!(134_829_848)), room_id: room_id!("!roomid:notareal.hs").to_owned(), - unsigned: Unsigned::default(), + unsigned: MessageLikeUnsigned::default(), }; assert_eq!( @@ -232,7 +232,7 @@ fn notice_event_serialization() { sender: user_id!("@user:notareal.hs").to_owned(), origin_server_ts: MilliSecondsSinceUnixEpoch(uint!(134_829_848)), room_id: room_id!("!roomid:notareal.hs").to_owned(), - unsigned: Unsigned::default(), + unsigned: MessageLikeUnsigned::default(), }; assert_eq!( @@ -299,7 +299,7 @@ fn emote_event_serialization() { sender: user_id!("@user:notareal.hs").to_owned(), origin_server_ts: MilliSecondsSinceUnixEpoch(uint!(134_829_848)), room_id: room_id!("!roomid:notareal.hs").to_owned(), - unsigned: Unsigned::default(), + unsigned: MessageLikeUnsigned::default(), }; assert_eq!( diff --git a/crates/ruma-common/tests/events/message_event.rs b/crates/ruma-common/tests/events/message_event.rs index 0c06f1f5..e3f05619 100644 --- a/crates/ruma-common/tests/events/message_event.rs +++ b/crates/ruma-common/tests/events/message_event.rs @@ -8,7 +8,7 @@ use ruma_common::{ room::{ImageInfo, ThumbnailInfo}, sticker::StickerEventContent, AnyMessageLikeEvent, AnyMessageLikeEventContent, AnySyncMessageLikeEvent, MessageLikeEvent, - MessageLikeEventType, Unsigned, + MessageLikeEventType, MessageLikeUnsigned, }, mxc_uri, room_id, serde::Raw, @@ -40,7 +40,7 @@ fn message_serialize_sticker() { origin_server_ts: MilliSecondsSinceUnixEpoch(uint!(1)), room_id: room_id!("!roomid:room.com").to_owned(), sender: user_id!("@carl:example.com").to_owned(), - unsigned: Unsigned::default(), + unsigned: MessageLikeUnsigned::default(), }; let actual = to_json_value(&aliases_event).unwrap(); diff --git a/crates/ruma-common/tests/events/mod.rs b/crates/ruma-common/tests/events/mod.rs index 6e93c028..ffe8468e 100644 --- a/crates/ruma-common/tests/events/mod.rs +++ b/crates/ruma-common/tests/events/mod.rs @@ -1,7 +1,6 @@ #![cfg(feature = "events")] mod audio; -mod compat; mod enums; mod ephemeral_event; mod event; diff --git a/crates/ruma-common/tests/events/redacted.rs b/crates/ruma-common/tests/events/redacted.rs index 6fd8df22..cb0ef836 100644 --- a/crates/ruma-common/tests/events/redacted.rs +++ b/crates/ruma-common/tests/events/redacted.rs @@ -10,9 +10,9 @@ use ruma_common::{ redaction::{RoomRedactionEventContent, SyncRoomRedactionEvent}, }, AnyMessageLikeEvent, AnyRedactedMessageLikeEvent, AnyRedactedSyncMessageLikeEvent, - AnyRedactedSyncStateEvent, AnyRoomEvent, AnySyncRoomEvent, EventContent, Redact, - RedactContent, RedactedMessageLikeEvent, RedactedSyncMessageLikeEvent, - RedactedSyncStateEvent, RedactedUnsigned, Unsigned, + AnyRedactedSyncStateEvent, AnyRoomEvent, AnySyncRoomEvent, EventContent, + MessageLikeUnsigned, Redact, RedactContent, RedactedMessageLikeEvent, + RedactedSyncMessageLikeEvent, RedactedSyncStateEvent, RedactedUnsigned, }, room_id, user_id, MilliSecondsSinceUnixEpoch, RoomVersionId, }; @@ -29,7 +29,7 @@ fn unsigned() -> RedactedUnsigned { event_id: event_id!("$h29iv0s8:example.com").to_owned(), origin_server_ts: MilliSecondsSinceUnixEpoch(uint!(1)), sender: user_id!("@carl:example.com").to_owned(), - unsigned: Unsigned::default(), + unsigned: MessageLikeUnsigned::default(), })); unsigned @@ -166,7 +166,7 @@ fn redacted_deserialize_any_room_sync() { event_id: event_id!("$h29iv0s8:example.com").to_owned(), origin_server_ts: MilliSecondsSinceUnixEpoch(uint!(1)), sender: user_id!("@carl:example.com").to_owned(), - unsigned: Unsigned::default(), + unsigned: MessageLikeUnsigned::default(), })); let redacted = json!({ @@ -266,7 +266,7 @@ fn redact_method_properly_redacts() { event_id: event_id!("$h29iv0s8:example.com").to_owned(), origin_server_ts: MilliSecondsSinceUnixEpoch(uint!(1)), sender: user_id!("@carl:example.com").to_owned(), - unsigned: Unsigned::default(), + unsigned: MessageLikeUnsigned::default(), }; let event: AnyMessageLikeEvent = from_json_value(ev).unwrap(); diff --git a/crates/ruma-common/tests/events/redaction.rs b/crates/ruma-common/tests/events/redaction.rs index 19614645..574049d9 100644 --- a/crates/ruma-common/tests/events/redaction.rs +++ b/crates/ruma-common/tests/events/redaction.rs @@ -4,7 +4,7 @@ use ruma_common::{ event_id, events::{ room::redaction::{RoomRedactionEvent, RoomRedactionEventContent}, - AnyMessageLikeEvent, Unsigned, + AnyMessageLikeEvent, MessageLikeUnsigned, }, room_id, user_id, MilliSecondsSinceUnixEpoch, }; @@ -35,7 +35,7 @@ fn serialize_redaction() { origin_server_ts: MilliSecondsSinceUnixEpoch(uint!(1)), room_id: room_id!("!roomid:room.com").to_owned(), sender: user_id!("@carl:example.com").to_owned(), - unsigned: Unsigned::default(), + unsigned: MessageLikeUnsigned::default(), }; let actual = to_json_value(&aliases_event).unwrap(); diff --git a/crates/ruma-common/tests/events/room_message.rs b/crates/ruma-common/tests/events/room_message.rs index 822d7824..e31260e4 100644 --- a/crates/ruma-common/tests/events/room_message.rs +++ b/crates/ruma-common/tests/events/room_message.rs @@ -11,7 +11,7 @@ use ruma_common::{ AudioMessageEventContent, InReplyTo, KeyVerificationRequestEventContent, MessageType, Relation, RoomMessageEvent, RoomMessageEventContent, TextMessageEventContent, }, - Unsigned, + MessageLikeUnsigned, }, mxc_uri, room_id, user_id, DeviceId, MilliSecondsSinceUnixEpoch, }; @@ -41,7 +41,7 @@ fn serialization() { origin_server_ts: MilliSecondsSinceUnixEpoch(uint!(10_000)), room_id: room_id!("!testroomid:example.org").to_owned(), sender: user_id!("@user:example.org").to_owned(), - unsigned: Unsigned::default(), + unsigned: MessageLikeUnsigned::default(), }; assert_eq!( diff --git a/crates/ruma-common/tests/events/state_event.rs b/crates/ruma-common/tests/events/state_event.rs index 39b4d5ee..89f60c71 100644 --- a/crates/ruma-common/tests/events/state_event.rs +++ b/crates/ruma-common/tests/events/state_event.rs @@ -1,3 +1,4 @@ +use assign::assign; use js_int::{uint, UInt}; use matches::assert_matches; use ruma_common::{ @@ -9,7 +10,7 @@ use ruma_common::{ ThumbnailInfo, }, AnyRoomEvent, AnyStateEvent, AnyStateEventContent, AnySyncStateEvent, StateEvent, - StateEventType, SyncStateEvent, Unsigned, + StateEventType, StateUnsigned, SyncStateEvent, }, mxc_uri, room_alias_id, room_id, serde::Raw, @@ -22,17 +23,19 @@ use serde_json::{ fn aliases_event_with_prev_content() -> JsonValue { json!({ "content": { - "aliases": [ "#somewhere:localhost" ] + "aliases": ["#somewhere:localhost"], }, "event_id": "$h29iv0s8:example.com", "origin_server_ts": 1, - "prev_content": { - "aliases": [ "#inner:localhost" ] - }, "room_id": "!roomid:room.com", "sender": "@carl:example.com", "state_key": "", - "type": "m.room.aliases" + "type": "m.room.aliases", + "unsigned": { + "prev_content": { + "aliases": ["#inner:localhost"], + }, + }, }) } @@ -44,13 +47,15 @@ fn serialize_aliases_with_prev_content() { ]), event_id: event_id!("$h29iv0s8:example.com").to_owned(), origin_server_ts: MilliSecondsSinceUnixEpoch(uint!(1)), - prev_content: Some(RoomAliasesEventContent::new(vec![ - room_alias_id!("#inner:localhost").to_owned() - ])), room_id: room_id!("!roomid:room.com").to_owned(), sender: user_id!("@carl:example.com").to_owned(), state_key: "".into(), - unsigned: Unsigned::default(), + unsigned: assign!(StateUnsigned::default(), { + prev_content: Some(RoomAliasesEventContent::new(vec![room_alias_id!( + "#inner:localhost" + ) + .to_owned()])), + }), }; let actual = to_json_value(&aliases_event).unwrap(); @@ -67,11 +72,10 @@ fn serialize_aliases_without_prev_content() { ]), event_id: event_id!("$h29iv0s8:example.com").to_owned(), origin_server_ts: MilliSecondsSinceUnixEpoch(uint!(1)), - prev_content: None, room_id: room_id!("!roomid:room.com").to_owned(), sender: user_id!("@carl:example.com").to_owned(), state_key: "".into(), - unsigned: Unsigned::default(), + unsigned: StateUnsigned::default(), }; let actual = to_json_value(&aliases_event).unwrap(); @@ -116,11 +120,13 @@ fn deserialize_aliases_with_prev_content() { content, event_id, origin_server_ts, - prev_content: Some(prev_content), room_id, sender, state_key, - unsigned, + unsigned: StateUnsigned { + prev_content: Some(prev_content), + .. + }, }) if content.aliases == vec![room_alias_id!("#somewhere:localhost")] && event_id == event_id!("$h29iv0s8:example.com") && origin_server_ts == MilliSecondsSinceUnixEpoch(uint!(1)) @@ -128,7 +134,6 @@ fn deserialize_aliases_with_prev_content() { && room_id == room_id!("!roomid:room.com") && sender == user_id!("@carl:example.com") && state_key.is_empty() - && unsigned.is_empty() ); } @@ -144,17 +149,18 @@ fn deserialize_aliases_sync_with_room_id() { content, event_id, origin_server_ts, - prev_content: Some(prev_content), sender, state_key, - unsigned, + unsigned: StateUnsigned { + prev_content: Some(prev_content), + .. + }, }) if content.aliases == vec![room_alias_id!("#somewhere:localhost")] && event_id == event_id!("$h29iv0s8:example.com") && origin_server_ts == MilliSecondsSinceUnixEpoch(uint!(1)) && prev_content.aliases == vec![room_alias_id!("#inner:localhost")] && sender == user_id!("@carl:example.com") && state_key.is_empty() - && unsigned.is_empty() ); } @@ -201,11 +207,10 @@ fn deserialize_avatar_without_prev_content() { }, event_id, origin_server_ts, - prev_content: None, room_id, sender, state_key, - unsigned + unsigned, }) if event_id == event_id!("$h29iv0s8:example.com") && origin_server_ts == MilliSecondsSinceUnixEpoch(uint!(1)) && room_id == room_id!("!roomid:room.com") @@ -273,7 +278,6 @@ fn deserialize_member_event_with_top_level_membership_field() { content, event_id, origin_server_ts, - prev_content: None, sender, .. } @@ -296,16 +300,17 @@ fn deserialize_full_event_convert_to_sync() { content, event_id, origin_server_ts, - prev_content: Some(prev_content), sender, state_key, - unsigned, + unsigned: StateUnsigned { + prev_content: Some(prev_content), + .. + } }) if content.aliases == vec![room_alias_id!("#somewhere:localhost")] && event_id == "$h29iv0s8:example.com" && origin_server_ts == MilliSecondsSinceUnixEpoch(uint!(1)) && prev_content.aliases == vec![room_alias_id!("#inner:localhost")] && sender == "@carl:example.com" && state_key.is_empty() - && unsigned.is_empty() ); } diff --git a/crates/ruma-common/tests/events/ui/04-event-sanity-check.rs b/crates/ruma-common/tests/events/ui/04-event-sanity-check.rs index a180f73e..c5c63bf1 100644 --- a/crates/ruma-common/tests/events/ui/04-event-sanity-check.rs +++ b/crates/ruma-common/tests/events/ui/04-event-sanity-check.rs @@ -4,7 +4,7 @@ extern crate serde; use ruma_common::{ - events::{EventContent, StateEventType, Unsigned}, + events::{EventContent, StateEventType, StateUnsigned}, EventId, MilliSecondsSinceUnixEpoch, RoomId, UserId, }; use ruma_macros::Event; @@ -18,8 +18,7 @@ pub struct StateEvent> { pub origin_server_ts: MilliSecondsSinceUnixEpoch, pub room_id: Box, pub state_key: String, - pub prev_content: Option, - pub unsigned: Unsigned, + pub unsigned: StateUnsigned, } fn main() {} diff --git a/crates/ruma-common/tests/events/video.rs b/crates/ruma-common/tests/events/video.rs index 2f1f9526..eba6f690 100644 --- a/crates/ruma-common/tests/events/video.rs +++ b/crates/ruma-common/tests/events/video.rs @@ -18,7 +18,7 @@ use ruma_common::{ JsonWebKeyInit, }, video::{VideoContent, VideoEventContent}, - AnyMessageLikeEvent, MessageLikeEvent, Unsigned, + AnyMessageLikeEvent, MessageLikeEvent, MessageLikeUnsigned, }, mxc_uri, room_id, serde::Base64, @@ -147,7 +147,7 @@ fn event_serialization() { sender: user_id!("@user:notareal.hs").to_owned(), origin_server_ts: MilliSecondsSinceUnixEpoch(uint!(134_829_848)), room_id: room_id!("!roomid:notareal.hs").to_owned(), - unsigned: Unsigned::default(), + unsigned: MessageLikeUnsigned::default(), }; assert_eq!( diff --git a/crates/ruma-macros/src/events/event.rs b/crates/ruma-macros/src/events/event.rs index a1ddee76..fbdba084 100644 --- a/crates/ruma-macros/src/events/event.rs +++ b/crates/ruma-macros/src/events/event.rs @@ -9,7 +9,7 @@ use syn::{ use super::{ event_parse::{to_kind_variation, EventKind, EventKindVariation}, - util::is_non_stripped_room_event, + util::{has_prev_content, is_non_stripped_room_event}, }; use crate::{import_ruma_common, util::to_camel_case}; @@ -135,7 +135,7 @@ fn expand_serialize_event( fn expand_deserialize_event( input: &DeriveInput, - _kind: EventKind, + kind: EventKind, var: EventKindVariation, fields: &[Field], ruma_common: &TokenStream, @@ -168,32 +168,14 @@ fn expand_deserialize_event( .map(|field| { let name = field.ident.as_ref().unwrap(); let ty = &field.ty; - if name == "content" || name == "prev_content" { + if name == "content" || (name == "unsigned" && has_prev_content(kind, var)) { if is_generic { quote! { ::std::boxed::Box<#serde_json::value::RawValue> } } else { quote! { #content_type } } } else { - #[allow(unused_mut)] - let mut ty = quote! { #ty }; - - #[cfg(feature = "compat")] - if matches!(_kind, EventKind::State) && name == "unsigned" { - match var { - EventKindVariation::Full | EventKindVariation::Sync => { - ty = quote! { #ruma_common::events::UnsignedWithPrevContent }; - } - EventKindVariation::Redacted | EventKindVariation::RedactedSync => { - ty = quote! { #ruma_common::events::RedactedUnsignedWithPrevContent }; - } - EventKindVariation::Stripped | EventKindVariation::Initial => { - unreachable!() - } - } - } - - ty + quote! { #ty } } }) .collect(); @@ -239,45 +221,13 @@ fn expand_deserialize_event( )?; } } - } else if name == "prev_content" { - if is_generic { - #[allow(unused_mut)] - let mut res = quote! { - let prev_content = prev_content.map(|json| { - C::from_parts(&event_type, &json).map_err(A::Error::custom) - }).transpose()?; - }; - - #[cfg(feature = "compat")] - if let EventKind::State = _kind { - res = quote! { - let prev_content = prev_content - .or_else(|| unsigned.as_mut().and_then(|u| u.prev_content.take())); - #res - }; - }; - - res - } else { - TokenStream::new() + } else if name == "unsigned" && has_prev_content(kind, var) { + quote! { + let unsigned = unsigned.map(|json| { + #ruma_common::events::StateUnsigned::_from_parts(&event_type, &json) + .map_err(A::Error::custom) + }).transpose()?.unwrap_or_default(); } - } else if name == "unsigned" { - #[allow(unused_mut)] - let mut res = quote! { - let unsigned = unsigned.unwrap_or_default(); - }; - - #[cfg(feature = "compat")] - if matches!(_kind, EventKind::State) { - res = quote! { - let unsigned = unsigned.map_or_else( - ::std::default::Default::default, - ::std::convert::From::from, - ); - }; - } - - res } else { let attrs: Vec<_> = field .attrs @@ -296,7 +246,7 @@ fn expand_deserialize_event( ) }); - if has_default_attr { + if has_default_attr || name == "unsigned" { quote! { let #name = #name.unwrap_or_default(); } diff --git a/crates/ruma-macros/src/events/event_enum.rs b/crates/ruma-macros/src/events/event_enum.rs index 29f3a3ec..14cce86c 100644 --- a/crates/ruma-macros/src/events/event_enum.rs +++ b/crates/ruma-macros/src/events/event_enum.rs @@ -6,7 +6,7 @@ use syn::{Attribute, Data, DataEnum, DeriveInput, Ident, LitStr}; use super::{ event_parse::{EventEnumDecl, EventEnumEntry, EventKind, EventKindVariation}, - util::{has_prev_content_field, EVENT_FIELDS}, + util::{has_prev_content, is_non_stripped_room_event, EVENT_FIELDS}, }; use crate::util::m_prefix_name_to_type_name; @@ -418,18 +418,20 @@ fn expand_accessor_methods( let content_enum = kind.to_content_enum(); let content_variants: Vec<_> = variants.iter().map(|v| v.ctor(&content_enum)).collect(); - let prev_content = has_prev_content_field(kind, var).then(|| { + let unsigned = if has_prev_content(kind, var) { quote! { - /// Returns the previous content for this event. - pub fn prev_content(&self) -> Option<#content_enum> { + /// Returns this event's unsigned field. + pub fn unsigned(&self) -> #ruma_common::events::StateUnsigned<#content_enum> { match self { #( #self_variants(event) => { - event.prev_content.as_ref().map(|c| #content_variants(c.clone())) + event.unsigned._map_prev_unsigned(|c| { + #content_variants(c.clone()) + }) }, )* Self::_Custom(event) => { - event.prev_content.as_ref().map(|c| #content_enum::_Custom { + event.unsigned._map_prev_unsigned(|c| #content_enum::_Custom { event_type: crate::PrivOwnedStr( ::std::convert::From::from( #ruma_common::events::EventContent::event_type(c).as_str() @@ -440,7 +442,22 @@ fn expand_accessor_methods( } } } - }); + } else if is_non_stripped_room_event(kind, var) { + let field_type = field_return_type("unsigned", var, ruma_common); + let variants = variants.iter().map(|v| v.match_arm(quote! { Self })); + + quote! { + /// Returns this event's unsigned field. + pub fn unsigned(&self) -> &#field_type { + match self { + #( #variants(event) => &event.unsigned, )* + Self::_Custom(event) => &event.unsigned, + } + } + } + } else { + quote! {} + }; quote! { /// Returns the content for this event. @@ -458,7 +475,7 @@ fn expand_accessor_methods( } } - #prev_content + #unsigned } }); @@ -558,7 +575,7 @@ fn field_return_type( if var.is_redacted() { quote! { #ruma_common::events::RedactedUnsigned } } else { - quote! { #ruma_common::events::Unsigned } + quote! { #ruma_common::events::MessageLikeUnsigned } } } _ => panic!("the `ruma_macros::event_enum::EVENT_FIELD` const was changed"), diff --git a/crates/ruma-macros/src/events/util.rs b/crates/ruma-macros/src/events/util.rs index dcd1ec2a..dd92f62f 100644 --- a/crates/ruma-macros/src/events/util.rs +++ b/crates/ruma-macros/src/events/util.rs @@ -11,7 +11,7 @@ pub(crate) fn is_non_stripped_room_event(kind: EventKind, var: EventKindVariatio ) } -pub(crate) fn has_prev_content_field(kind: EventKind, var: EventKindVariation) -> bool { +pub(crate) fn has_prev_content(kind: EventKind, var: EventKindVariation) -> bool { matches!(kind, EventKind::State) && matches!(var, EventKindVariation::Full | EventKindVariation::Sync) } @@ -34,5 +34,4 @@ pub(crate) const EVENT_FIELDS: &[(&str, EventKindFn)] = &[ && var != EventKindVariation::Initial }), ("state_key", |kind, _| matches!(kind, EventKind::State)), - ("unsigned", is_non_stripped_room_event), ];