events: Remove shared Relation type in favor of more per-event-type ones
This commit is contained in:
parent
1c31d31be1
commit
84829e23dc
@ -11,6 +11,8 @@ Breaking changes:
|
||||
* Remove `Custom` variant from `key::verification::accept::AcceptMethod` and
|
||||
`key::verification::start::StartMethod`.
|
||||
* 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:
|
||||
|
||||
|
@ -23,7 +23,6 @@ pub mod name;
|
||||
pub mod pinned_events;
|
||||
pub mod power_levels;
|
||||
pub mod redaction;
|
||||
pub mod relationships;
|
||||
pub mod server_acl;
|
||||
pub mod third_party_invite;
|
||||
pub mod tombstone;
|
||||
|
@ -5,12 +5,15 @@ use std::collections::BTreeMap;
|
||||
use js_int::UInt;
|
||||
use ruma_events_macros::EventContent;
|
||||
use ruma_identifiers::DeviceIdBox;
|
||||
#[cfg(feature = "unstable-pre-spec")]
|
||||
use ruma_identifiers::EventId;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::{
|
||||
room::relationships::{relation_serde, Relation},
|
||||
MessageEvent,
|
||||
};
|
||||
#[cfg(feature = "unstable-pre-spec")]
|
||||
use crate::room::message::Replacement;
|
||||
use crate::{room::message::InReplyTo, MessageEvent};
|
||||
|
||||
mod relation_serde;
|
||||
|
||||
/// An event that has been encrypted.
|
||||
pub type EncryptedEvent = MessageEvent<EncryptedEventContent>;
|
||||
@ -61,6 +64,73 @@ pub enum EncryptedEventScheme {
|
||||
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.
|
||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||
#[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)]
|
||||
@ -154,8 +224,8 @@ mod tests {
|
||||
use ruma_serde::Raw;
|
||||
use serde_json::{from_value as from_json_value, json, to_value as to_json_value};
|
||||
|
||||
use super::{EncryptedEventContent, EncryptedEventScheme, MegolmV1AesSha2Content};
|
||||
use crate::room::relationships::{InReplyTo, Relation};
|
||||
use super::{EncryptedEventContent, EncryptedEventScheme, MegolmV1AesSha2Content, Relation};
|
||||
use crate::room::message::InReplyTo;
|
||||
use ruma_identifiers::event_id;
|
||||
|
||||
#[test]
|
||||
|
@ -5,22 +5,20 @@ use std::borrow::Cow;
|
||||
use indoc::formatdoc;
|
||||
use js_int::UInt;
|
||||
use ruma_events_macros::EventContent;
|
||||
use ruma_identifiers::MxcUri;
|
||||
#[cfg(feature = "unstable-pre-spec")]
|
||||
use ruma_identifiers::{DeviceIdBox, UserId};
|
||||
use ruma_identifiers::{EventId, MxcUri};
|
||||
use ruma_serde::StringEnum;
|
||||
use serde::{de::DeserializeOwned, Deserialize, Serialize};
|
||||
use serde_json::Value as JsonValue;
|
||||
|
||||
use super::{
|
||||
relationships::{relation_serde, InReplyTo, Relation},
|
||||
EncryptedFile, ImageInfo, ThumbnailInfo,
|
||||
};
|
||||
use super::{EncryptedFile, ImageInfo, ThumbnailInfo};
|
||||
#[cfg(feature = "unstable-pre-spec")]
|
||||
use crate::key::verification::VerificationMethod;
|
||||
|
||||
mod content_serde;
|
||||
pub mod feedback;
|
||||
mod relation_serde;
|
||||
|
||||
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.
|
||||
#[derive(Clone, Debug, Deserialize, Serialize)]
|
||||
#[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)]
|
||||
@ -1119,8 +1171,7 @@ mod tests {
|
||||
use ruma_identifiers::event_id;
|
||||
use serde_json::{from_value as from_json_value, json};
|
||||
|
||||
use super::{MessageEventContent, MessageType, Relation};
|
||||
use crate::room::relationships::InReplyTo;
|
||||
use super::{InReplyTo, MessageEventContent, MessageType, Relation};
|
||||
|
||||
#[test]
|
||||
fn deserialize_reply() {
|
||||
|
142
crates/ruma-events/src/room/message/relation_serde.rs
Normal file
142
crates/ruma-events/src/room/message/relation_serde.rs
Normal 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,
|
||||
}
|
@ -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 }
|
||||
}
|
||||
}
|
@ -9,12 +9,9 @@ use ruma_events::{
|
||||
key::verification::VerificationMethod, room::message::KeyVerificationRequestEventContent,
|
||||
};
|
||||
use ruma_events::{
|
||||
room::{
|
||||
message::{
|
||||
AudioMessageEventContent, MessageEvent, MessageEventContent, MessageType,
|
||||
TextMessageEventContent,
|
||||
},
|
||||
relationships::{InReplyTo, Relation},
|
||||
room::message::{
|
||||
AudioMessageEventContent, InReplyTo, MessageEvent, MessageEventContent, MessageType,
|
||||
Relation, TextMessageEventContent,
|
||||
},
|
||||
Unsigned,
|
||||
};
|
||||
@ -253,7 +250,7 @@ fn edit_deserialization_061() {
|
||||
#[test]
|
||||
#[cfg(feature = "unstable-pre-spec")]
|
||||
fn edit_deserialization_future() {
|
||||
use ruma_events::room::relationships::Replacement;
|
||||
use ruma_events::room::message::Replacement;
|
||||
|
||||
let ev_id = event_id!("$1598361704261elfgc:localhost");
|
||||
let json_data = json!({
|
||||
|
Loading…
x
Reference in New Issue
Block a user