events: Remove shared Relation type in favor of more per-event-type ones

This commit is contained in:
Jonas Platte 2021-06-20 18:53:30 +02:00
parent 1c31d31be1
commit 84829e23dc
No known key found for this signature in database
GPG Key ID: CC154DE0E30B7C67
8 changed files with 282 additions and 135 deletions

View File

@ -11,6 +11,8 @@ Breaking changes:
* Remove `Custom` variant from `key::verification::accept::AcceptMethod` and * Remove `Custom` variant from `key::verification::accept::AcceptMethod` and
`key::verification::start::StartMethod`. `key::verification::start::StartMethod`.
* Rename `relation` field in some events to `relates_to` * Rename `relation` field in some events to `relates_to`
* All events that support relations now have their own `Relation` types (the `room::relationships`
module has been removed)
Improvements: Improvements:

View File

@ -23,7 +23,6 @@ pub mod name;
pub mod pinned_events; pub mod pinned_events;
pub mod power_levels; pub mod power_levels;
pub mod redaction; pub mod redaction;
pub mod relationships;
pub mod server_acl; pub mod server_acl;
pub mod third_party_invite; pub mod third_party_invite;
pub mod tombstone; pub mod tombstone;

View File

@ -5,12 +5,15 @@ use std::collections::BTreeMap;
use js_int::UInt; use js_int::UInt;
use ruma_events_macros::EventContent; use ruma_events_macros::EventContent;
use ruma_identifiers::DeviceIdBox; use ruma_identifiers::DeviceIdBox;
#[cfg(feature = "unstable-pre-spec")]
use ruma_identifiers::EventId;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use crate::{ #[cfg(feature = "unstable-pre-spec")]
room::relationships::{relation_serde, Relation}, use crate::room::message::Replacement;
MessageEvent, use crate::{room::message::InReplyTo, MessageEvent};
};
mod relation_serde;
/// An event that has been encrypted. /// An event that has been encrypted.
pub type EncryptedEvent = MessageEvent<EncryptedEventContent>; pub type EncryptedEvent = MessageEvent<EncryptedEventContent>;
@ -61,6 +64,73 @@ pub enum EncryptedEventScheme {
MegolmV1AesSha2(MegolmV1AesSha2Content), MegolmV1AesSha2(MegolmV1AesSha2Content),
} }
/// Relationship information about an encrypted event.
///
/// Outside of the encrypted payload to support server aggregation.
#[derive(Clone, Debug)]
#[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)]
pub enum Relation {
/// An `m.in_reply_to` relation indicating that the event is a reply to another event.
Reply {
/// Information about another message being replied to.
in_reply_to: InReplyTo,
},
/// An event that replaces another event.
#[cfg(feature = "unstable-pre-spec")]
#[cfg_attr(docsrs, doc(cfg(feature = "unstable-pre-spec")))]
Replacement(Replacement),
/// A reference to another event.
#[cfg(feature = "unstable-pre-spec")]
#[cfg_attr(docsrs, doc(cfg(feature = "unstable-pre-spec")))]
Reference(Reference),
/// An annotation to an event.
#[cfg(feature = "unstable-pre-spec")]
#[cfg_attr(docsrs, doc(cfg(feature = "unstable-pre-spec")))]
Annotation(Annotation),
}
/// A reference to another event.
#[derive(Clone, Debug, Deserialize, Serialize)]
#[cfg(feature = "unstable-pre-spec")]
#[cfg_attr(docsrs, doc(cfg(feature = "unstable-pre-spec")))]
#[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)]
pub struct Reference {
/// The event we are referencing.
pub event_id: EventId,
}
#[cfg(feature = "unstable-pre-spec")]
impl Reference {
/// Creates a new `Reference` with the given event ID.
pub fn new(event_id: EventId) -> Self {
Self { event_id }
}
}
/// An annotation for an event.
#[derive(Clone, Debug, Deserialize, Serialize)]
#[cfg(feature = "unstable-pre-spec")]
#[cfg_attr(docsrs, doc(cfg(feature = "unstable-pre-spec")))]
#[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)]
pub struct Annotation {
/// The event that is being annotated.
pub event_id: EventId,
/// The annotation.
pub key: String,
}
#[cfg(feature = "unstable-pre-spec")]
impl Annotation {
/// Creates a new `Annotation` with the given event ID and key.
pub fn new(event_id: EventId, key: String) -> Self {
Self { event_id, key }
}
}
/// The payload for `EncryptedEvent` using the *m.olm.v1.curve25519-aes-sha2* algorithm. /// The payload for `EncryptedEvent` using the *m.olm.v1.curve25519-aes-sha2* algorithm.
#[derive(Clone, Debug, Serialize, Deserialize)] #[derive(Clone, Debug, Serialize, Deserialize)]
#[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)] #[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)]
@ -154,8 +224,8 @@ mod tests {
use ruma_serde::Raw; use ruma_serde::Raw;
use serde_json::{from_value as from_json_value, json, to_value as to_json_value}; use serde_json::{from_value as from_json_value, json, to_value as to_json_value};
use super::{EncryptedEventContent, EncryptedEventScheme, MegolmV1AesSha2Content}; use super::{EncryptedEventContent, EncryptedEventScheme, MegolmV1AesSha2Content, Relation};
use crate::room::relationships::{InReplyTo, Relation}; use crate::room::message::InReplyTo;
use ruma_identifiers::event_id; use ruma_identifiers::event_id;
#[test] #[test]

View File

@ -5,22 +5,20 @@ use std::borrow::Cow;
use indoc::formatdoc; use indoc::formatdoc;
use js_int::UInt; use js_int::UInt;
use ruma_events_macros::EventContent; use ruma_events_macros::EventContent;
use ruma_identifiers::MxcUri;
#[cfg(feature = "unstable-pre-spec")] #[cfg(feature = "unstable-pre-spec")]
use ruma_identifiers::{DeviceIdBox, UserId}; use ruma_identifiers::{DeviceIdBox, UserId};
use ruma_identifiers::{EventId, MxcUri};
use ruma_serde::StringEnum; use ruma_serde::StringEnum;
use serde::{de::DeserializeOwned, Deserialize, Serialize}; use serde::{de::DeserializeOwned, Deserialize, Serialize};
use serde_json::Value as JsonValue; use serde_json::Value as JsonValue;
use super::{ use super::{EncryptedFile, ImageInfo, ThumbnailInfo};
relationships::{relation_serde, InReplyTo, Relation},
EncryptedFile, ImageInfo, ThumbnailInfo,
};
#[cfg(feature = "unstable-pre-spec")] #[cfg(feature = "unstable-pre-spec")]
use crate::key::verification::VerificationMethod; use crate::key::verification::VerificationMethod;
mod content_serde; mod content_serde;
pub mod feedback; pub mod feedback;
mod relation_serde;
type JsonObject = serde_json::Map<String, JsonValue>; type JsonObject = serde_json::Map<String, JsonValue>;
@ -262,6 +260,60 @@ impl From<MessageType> for MessageEventContent {
} }
} }
/// Message vent relationship.
///
/// Currently used for replies and editing (message replacement).
#[derive(Clone, Debug)]
#[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)]
pub enum Relation {
/// An `m.in_reply_to` relation indicating that the event is a reply to another event.
Reply {
/// Information about another message being replied to.
in_reply_to: InReplyTo,
},
/// An event that replaces another event.
#[cfg(feature = "unstable-pre-spec")]
#[cfg_attr(docsrs, doc(cfg(feature = "unstable-pre-spec")))]
Replacement(Replacement),
}
/// Information about the event a "rich reply" is replying to.
#[derive(Clone, Debug, Deserialize, Serialize)]
#[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)]
pub struct InReplyTo {
/// The event being replied to.
pub event_id: EventId,
}
impl InReplyTo {
/// Creates a new `InReplyTo` with the given event ID.
pub fn new(event_id: EventId) -> Self {
Self { event_id }
}
}
/// The event this relation belongs to replaces another event.
#[derive(Clone, Debug)]
#[cfg(feature = "unstable-pre-spec")]
#[cfg_attr(docsrs, doc(cfg(feature = "unstable-pre-spec")))]
#[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)]
pub struct Replacement {
/// The ID of the event being replacing.
pub event_id: EventId,
/// New content.
pub new_content: Box<MessageEventContent>,
}
#[cfg(feature = "unstable-pre-spec")]
impl Replacement {
/// Creates a new `Replacement` with the given event ID and new content.
pub fn new(event_id: EventId, new_content: Box<MessageEventContent>) -> Self {
Self { event_id, new_content }
}
}
/// The payload for an audio message. /// The payload for an audio message.
#[derive(Clone, Debug, Deserialize, Serialize)] #[derive(Clone, Debug, Deserialize, Serialize)]
#[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)] #[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)]
@ -1119,8 +1171,7 @@ mod tests {
use ruma_identifiers::event_id; use ruma_identifiers::event_id;
use serde_json::{from_value as from_json_value, json}; use serde_json::{from_value as from_json_value, json};
use super::{MessageEventContent, MessageType, Relation}; use super::{InReplyTo, MessageEventContent, MessageType, Relation};
use crate::room::relationships::InReplyTo;
#[test] #[test]
fn deserialize_reply() { fn deserialize_reply() {

View File

@ -0,0 +1,142 @@
#[cfg(feature = "unstable-pre-spec")]
use ruma_identifiers::EventId;
use serde::{ser::SerializeStruct as _, Deserialize, Deserializer, Serialize, Serializer};
#[cfg(feature = "unstable-pre-spec")]
use super::Replacement;
use super::{InReplyTo, Relation};
#[cfg(feature = "unstable-pre-spec")]
use crate::room::message::MessageEventContent;
pub fn deserialize<'de, D>(deserializer: D) -> Result<Option<Relation>, D::Error>
where
D: Deserializer<'de>,
{
fn convert_relation(ev: EventWithRelatesToJsonRepr) -> Option<Relation> {
if let Some(in_reply_to) = ev.relates_to.in_reply_to {
return Some(Relation::Reply { in_reply_to });
}
#[cfg(feature = "unstable-pre-spec")]
if let Some(relation) = ev.relates_to.relation {
let relation = match relation {
RelationJsonRepr::Replacement(ReplacementJsonRepr { event_id }) => {
let new_content = ev.new_content?;
Relation::Replacement(Replacement { event_id, new_content })
}
// FIXME: Maybe we should log this, though at this point we don't even have access
// to the rel_type of the unknown relation.
RelationJsonRepr::Unknown => return None,
};
return Some(relation);
}
None
}
EventWithRelatesToJsonRepr::deserialize(deserializer).map(convert_relation)
}
pub fn serialize<S>(relation: &Option<Relation>, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
let relation = match relation {
Some(rel) => rel,
// FIXME: If this crate ends up depending on tracing, emit a warning here.
// This code path should not be reachable due to the skip_serializing_if serde attribute
// that should be applied together with `with = "relation_serde"`.
None => return serializer.serialize_struct("NoRelation", 0)?.end(),
};
let json_repr = match relation {
Relation::Reply { in_reply_to } => EventWithRelatesToJsonRepr::new(RelatesToJsonRepr {
in_reply_to: Some(in_reply_to.clone()),
..Default::default()
}),
#[cfg(feature = "unstable-pre-spec")]
Relation::Replacement(Replacement { event_id, new_content }) => {
EventWithRelatesToJsonRepr {
relates_to: RelatesToJsonRepr {
relation: Some(RelationJsonRepr::Replacement(ReplacementJsonRepr {
event_id: event_id.clone(),
})),
..Default::default()
},
new_content: Some(new_content.clone()),
}
}
};
json_repr.serialize(serializer)
}
#[derive(Deserialize, Serialize)]
struct EventWithRelatesToJsonRepr {
#[serde(rename = "m.relates_to", default, skip_serializing_if = "RelatesToJsonRepr::is_empty")]
relates_to: RelatesToJsonRepr,
#[cfg(feature = "unstable-pre-spec")]
#[serde(rename = "m.new_content", skip_serializing_if = "Option::is_none")]
new_content: Option<Box<MessageEventContent>>,
}
impl EventWithRelatesToJsonRepr {
fn new(relates_to: RelatesToJsonRepr) -> Self {
Self {
relates_to,
#[cfg(feature = "unstable-pre-spec")]
new_content: None,
}
}
}
/// Enum modeling the different ways relationships can be expressed in a `m.relates_to` field of an
/// event.
#[derive(Default, Deserialize, Serialize)]
struct RelatesToJsonRepr {
#[serde(rename = "m.in_reply_to", skip_serializing_if = "Option::is_none")]
in_reply_to: Option<InReplyTo>,
#[cfg(feature = "unstable-pre-spec")]
#[serde(flatten, skip_serializing_if = "Option::is_none")]
relation: Option<RelationJsonRepr>,
}
impl RelatesToJsonRepr {
fn is_empty(&self) -> bool {
#[cfg(not(feature = "unstable-pre-spec"))]
{
self.in_reply_to.is_none()
}
#[cfg(feature = "unstable-pre-spec")]
{
self.in_reply_to.is_none() && self.relation.is_none()
}
}
}
/// A relation, which associates new information to an existing event.
#[derive(Clone, Deserialize, Serialize)]
#[cfg(feature = "unstable-pre-spec")]
#[serde(tag = "rel_type")]
enum RelationJsonRepr {
/// An event that replaces another event.
#[serde(rename = "m.replace")]
Replacement(ReplacementJsonRepr),
/// An unknown relation type.
///
/// Not available in the public API, but exists here so deserialization
/// doesn't fail with new / custom `rel_type`s.
#[serde(other)]
Unknown,
}
#[derive(Clone, Deserialize, Serialize)]
#[cfg(feature = "unstable-pre-spec")]
struct ReplacementJsonRepr {
event_id: EventId,
}

View File

@ -1,114 +0,0 @@
//! Types for event relationships.
//!
//! Events in Matrix can relate to one another in a couple of ways, this module adds types to parse
//! the relationship of an event if any exists.
//!
//! MSC for all the relates_to types except replies:
//! <https://github.com/matrix-org/matrix-doc/pull/2674>
use ruma_identifiers::EventId;
use serde::{Deserialize, Serialize};
#[cfg(feature = "unstable-pre-spec")]
use crate::room::message::MessageEventContent;
pub(crate) mod relation_serde;
/// Enum modeling the different ways relationships can be expressed in a `m.relates_to` field of an
/// `m.room.message` or `m.room.encrypted` event.
#[derive(Clone, Debug)]
#[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)]
pub enum Relation {
/// A reference to another event.
#[cfg(feature = "unstable-pre-spec")]
#[cfg_attr(docsrs, doc(cfg(feature = "unstable-pre-spec")))]
Reference(Reference),
/// An annotation to an event.
#[cfg(feature = "unstable-pre-spec")]
#[cfg_attr(docsrs, doc(cfg(feature = "unstable-pre-spec")))]
Annotation(Annotation),
/// An event that replaces another event.
#[cfg(feature = "unstable-pre-spec")]
#[cfg_attr(docsrs, doc(cfg(feature = "unstable-pre-spec")))]
Replacement(Replacement),
/// An `m.in_reply_to` relation indicating that the event is a reply to another event.
Reply {
/// Information about another message being replied to.
in_reply_to: InReplyTo,
},
}
/// The event this relation belongs to replaces another event.
#[derive(Clone, Debug)]
#[cfg(feature = "unstable-pre-spec")]
#[cfg_attr(docsrs, doc(cfg(feature = "unstable-pre-spec")))]
#[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)]
pub struct Replacement {
/// The ID of the event being replacing.
pub event_id: EventId,
/// New content.
pub new_content: Box<MessageEventContent>,
}
#[cfg(feature = "unstable-pre-spec")]
impl Replacement {
/// Creates a new `Replacement` with the given event ID and new content.
pub fn new(event_id: EventId, new_content: Box<MessageEventContent>) -> Self {
Self { event_id, new_content }
}
}
/// Information about the event a "rich reply" is replying to.
#[derive(Clone, Debug, Deserialize, Serialize)]
#[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)]
pub struct InReplyTo {
/// The event being replied to.
pub event_id: EventId,
}
impl InReplyTo {
/// Creates a new `InReplyTo` with the given event ID.
pub fn new(event_id: EventId) -> Self {
Self { event_id }
}
}
/// A reference to another event.
#[derive(Clone, Debug, Deserialize, Serialize)]
#[cfg(feature = "unstable-pre-spec")]
#[cfg_attr(docsrs, doc(cfg(feature = "unstable-pre-spec")))]
#[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)]
pub struct Reference {
/// The event we are referencing.
pub event_id: EventId,
}
#[cfg(feature = "unstable-pre-spec")]
impl Reference {
/// Creates a new `Reference` with the given event ID.
pub fn new(event_id: EventId) -> Self {
Self { event_id }
}
}
/// An annotation for an event.
#[derive(Clone, Debug, Deserialize, Serialize)]
#[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)]
pub struct Annotation {
/// The event that is being annotated.
pub event_id: EventId,
/// The annotation.
pub key: String,
}
impl Annotation {
/// Creates a new `Annotation` with the given event ID and key.
pub fn new(event_id: EventId, key: String) -> Self {
Self { event_id, key }
}
}

View File

@ -9,12 +9,9 @@ use ruma_events::{
key::verification::VerificationMethod, room::message::KeyVerificationRequestEventContent, key::verification::VerificationMethod, room::message::KeyVerificationRequestEventContent,
}; };
use ruma_events::{ use ruma_events::{
room::{ room::message::{
message::{ AudioMessageEventContent, InReplyTo, MessageEvent, MessageEventContent, MessageType,
AudioMessageEventContent, MessageEvent, MessageEventContent, MessageType, Relation, TextMessageEventContent,
TextMessageEventContent,
},
relationships::{InReplyTo, Relation},
}, },
Unsigned, Unsigned,
}; };
@ -253,7 +250,7 @@ fn edit_deserialization_061() {
#[test] #[test]
#[cfg(feature = "unstable-pre-spec")] #[cfg(feature = "unstable-pre-spec")]
fn edit_deserialization_future() { fn edit_deserialization_future() {
use ruma_events::room::relationships::Replacement; use ruma_events::room::message::Replacement;
let ev_id = event_id!("$1598361704261elfgc:localhost"); let ev_id = event_id!("$1598361704261elfgc:localhost");
let json_data = json!({ let json_data = json!({