events: Refactor message relations
* Discard unknown relations * Move new_content into Relation::Replacement
This commit is contained in:
parent
2c8af1e17f
commit
ec4b719546
@ -52,3 +52,31 @@ impl Relation {
|
|||||||
Self { event_id, emoji }
|
Self { event_id, emoji }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use matches::assert_matches;
|
||||||
|
use ruma_identifiers::event_id;
|
||||||
|
use serde_json::{from_value as from_json_value, json};
|
||||||
|
|
||||||
|
use super::{ReactionEventContent, Relation};
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn deserialize() {
|
||||||
|
let ev_id = event_id!("$1598361704261elfgc:localhost");
|
||||||
|
|
||||||
|
let json = json!({
|
||||||
|
"m.relates_to": {
|
||||||
|
"rel_type": "m.annotation",
|
||||||
|
"event_id": ev_id,
|
||||||
|
"key": "🦛",
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
assert_matches!(
|
||||||
|
from_json_value::<ReactionEventContent>(json).unwrap(),
|
||||||
|
ReactionEventContent { relates_to: Relation { event_id, emoji } }
|
||||||
|
if event_id == ev_id && emoji == "🦛"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -7,7 +7,10 @@ use ruma_events_macros::EventContent;
|
|||||||
use ruma_identifiers::DeviceIdBox;
|
use ruma_identifiers::DeviceIdBox;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
use crate::{room::message::Relation, MessageEvent};
|
use crate::{
|
||||||
|
room::relationships::{relation_serde, Relation},
|
||||||
|
MessageEvent,
|
||||||
|
};
|
||||||
|
|
||||||
/// An event that has been encrypted.
|
/// An event that has been encrypted.
|
||||||
pub type EncryptedEvent = MessageEvent<EncryptedEventContent>;
|
pub type EncryptedEvent = MessageEvent<EncryptedEventContent>;
|
||||||
@ -35,22 +38,23 @@ pub struct EncryptedEventContent {
|
|||||||
#[serde(flatten)]
|
#[serde(flatten)]
|
||||||
pub scheme: EncryptedEventScheme,
|
pub scheme: EncryptedEventScheme,
|
||||||
|
|
||||||
/// Information about related messages for
|
/// Information about related messages for [rich replies].
|
||||||
/// [rich replies](https://matrix.org/docs/spec/client_server/r0.6.1#rich-replies).
|
///
|
||||||
#[serde(rename = "m.relates_to", skip_serializing_if = "Option::is_none")]
|
/// [rich replies]: https://matrix.org/docs/spec/client_server/r0.6.1#rich-replies
|
||||||
pub relates_to: Option<Relation>,
|
#[serde(flatten, with = "relation_serde", skip_serializing_if = "Option::is_none")]
|
||||||
|
pub relation: Option<Relation>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl EncryptedEventContent {
|
impl EncryptedEventContent {
|
||||||
/// Creates a new `EncryptedEventContent` with the given scheme and relation.
|
/// Creates a new `EncryptedEventContent` with the given scheme and relation.
|
||||||
pub fn new(scheme: EncryptedEventScheme, relates_to: Option<Relation>) -> Self {
|
pub fn new(scheme: EncryptedEventScheme, relates_to: Option<Relation>) -> Self {
|
||||||
Self { scheme, relates_to }
|
Self { scheme, relation: relates_to }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<EncryptedEventScheme> for EncryptedEventContent {
|
impl From<EncryptedEventScheme> for EncryptedEventContent {
|
||||||
fn from(scheme: EncryptedEventScheme) -> Self {
|
fn from(scheme: EncryptedEventScheme) -> Self {
|
||||||
Self { scheme, relates_to: None }
|
Self { scheme, relation: None }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -150,7 +154,7 @@ mod tests {
|
|||||||
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};
|
||||||
use crate::room::message::{InReplyTo, Relation};
|
use crate::room::relationships::{InReplyTo, Relation};
|
||||||
use ruma_identifiers::event_id;
|
use ruma_identifiers::event_id;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -162,7 +166,7 @@ mod tests {
|
|||||||
device_id: "device_id".into(),
|
device_id: "device_id".into(),
|
||||||
session_id: "session_id".into(),
|
session_id: "session_id".into(),
|
||||||
}),
|
}),
|
||||||
relates_to: Some(Relation::Reply {
|
relation: Some(Relation::Reply {
|
||||||
in_reply_to: InReplyTo { event_id: event_id!("$h29iv0s8:example.com") },
|
in_reply_to: InReplyTo { event_id: event_id!("$h29iv0s8:example.com") },
|
||||||
}),
|
}),
|
||||||
};
|
};
|
||||||
@ -218,7 +222,7 @@ mod tests {
|
|||||||
);
|
);
|
||||||
|
|
||||||
assert_matches!(
|
assert_matches!(
|
||||||
content.relates_to,
|
content.relation,
|
||||||
Some(Relation::Reply { in_reply_to })
|
Some(Relation::Reply { in_reply_to })
|
||||||
if in_reply_to.event_id == event_id!("$h29iv0s8:example.com")
|
if in_reply_to.event_id == event_id!("$h29iv0s8:example.com")
|
||||||
);
|
);
|
||||||
|
@ -12,15 +12,13 @@ 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;
|
||||||
|
|
||||||
#[cfg(feature = "unstable-pre-spec")]
|
use super::{
|
||||||
use super::relationships::{Annotation, Reference, RelationJsonRepr, Replacement};
|
relationships::{relation_serde, InReplyTo, Relation},
|
||||||
use super::{relationships::RelatesToJsonRepr, EncryptedFile, ImageInfo, ThumbnailInfo};
|
EncryptedFile, ImageInfo, ThumbnailInfo,
|
||||||
|
};
|
||||||
#[cfg(feature = "unstable-pre-spec")]
|
#[cfg(feature = "unstable-pre-spec")]
|
||||||
use crate::key::verification::VerificationMethod;
|
use crate::key::verification::VerificationMethod;
|
||||||
|
|
||||||
// FIXME: Do we want to keep re-exporting this?
|
|
||||||
pub use super::relationships::InReplyTo;
|
|
||||||
|
|
||||||
mod content_serde;
|
mod content_serde;
|
||||||
pub mod feedback;
|
pub mod feedback;
|
||||||
|
|
||||||
@ -42,29 +40,17 @@ pub struct MessageEventContent {
|
|||||||
#[serde(flatten)]
|
#[serde(flatten)]
|
||||||
pub msgtype: MessageType,
|
pub msgtype: MessageType,
|
||||||
|
|
||||||
/// Information about related messages for
|
/// Information about related messages for [rich replies].
|
||||||
/// [rich replies](https://matrix.org/docs/spec/client_server/r0.6.1#rich-replies).
|
|
||||||
#[serde(rename = "m.relates_to", skip_serializing_if = "Option::is_none")]
|
|
||||||
pub relates_to: Option<Relation>,
|
|
||||||
|
|
||||||
/// New content of an edited message.
|
|
||||||
///
|
///
|
||||||
/// This should only be set if `relates_to` is `Some(Relation::Replacement(_))`.
|
/// [rich replies]: https://matrix.org/docs/spec/client_server/r0.6.1#rich-replies
|
||||||
#[cfg(feature = "unstable-pre-spec")]
|
#[serde(flatten, with = "relation_serde", skip_serializing_if = "Option::is_none")]
|
||||||
#[cfg_attr(docsrs, doc(cfg(feature = "unstable-pre-spec")))]
|
pub relates_to: Option<Relation>,
|
||||||
#[serde(rename = "m.new_content", skip_serializing_if = "Option::is_none")]
|
|
||||||
pub new_content: Option<Box<MessageEventContent>>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl MessageEventContent {
|
impl MessageEventContent {
|
||||||
/// Create a `MessageEventContent` with the given `MessageType`.
|
/// Create a `MessageEventContent` with the given `MessageType`.
|
||||||
pub fn new(msgtype: MessageType) -> Self {
|
pub fn new(msgtype: MessageType) -> Self {
|
||||||
Self {
|
Self { msgtype, relates_to: None }
|
||||||
msgtype,
|
|
||||||
relates_to: None,
|
|
||||||
#[cfg(feature = "unstable-pre-spec")]
|
|
||||||
new_content: None,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A constructor to create a plain text message.
|
/// A constructor to create a plain text message.
|
||||||
@ -276,70 +262,6 @@ impl From<MessageType> for MessageEventContent {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Enum modeling the different ways relationships can be expressed in a
|
|
||||||
/// `m.relates_to` field of an `m.room.message` event.
|
|
||||||
#[derive(Clone, Debug, Deserialize, Serialize)]
|
|
||||||
#[serde(from = "RelatesToJsonRepr", into = "RelatesToJsonRepr")]
|
|
||||||
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,
|
|
||||||
},
|
|
||||||
|
|
||||||
/// Custom, unsupported relation.
|
|
||||||
#[doc(hidden)]
|
|
||||||
_Custom(JsonValue),
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<Relation> for RelatesToJsonRepr {
|
|
||||||
fn from(value: Relation) -> Self {
|
|
||||||
match value {
|
|
||||||
#[cfg(feature = "unstable-pre-spec")]
|
|
||||||
Relation::Annotation(r) => RelatesToJsonRepr::Relation(RelationJsonRepr::Annotation(r)),
|
|
||||||
#[cfg(feature = "unstable-pre-spec")]
|
|
||||||
Relation::Reference(r) => RelatesToJsonRepr::Relation(RelationJsonRepr::Reference(r)),
|
|
||||||
#[cfg(feature = "unstable-pre-spec")]
|
|
||||||
Relation::Replacement(r) => {
|
|
||||||
RelatesToJsonRepr::Relation(RelationJsonRepr::Replacement(r))
|
|
||||||
}
|
|
||||||
Relation::Reply { in_reply_to } => RelatesToJsonRepr::Reply { in_reply_to },
|
|
||||||
Relation::_Custom(c) => RelatesToJsonRepr::Custom(c),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<RelatesToJsonRepr> for Relation {
|
|
||||||
fn from(value: RelatesToJsonRepr) -> Self {
|
|
||||||
match value {
|
|
||||||
#[cfg(feature = "unstable-pre-spec")]
|
|
||||||
RelatesToJsonRepr::Relation(r) => match r {
|
|
||||||
RelationJsonRepr::Annotation(a) => Self::Annotation(a),
|
|
||||||
RelationJsonRepr::Reference(r) => Self::Reference(r),
|
|
||||||
RelationJsonRepr::Replacement(r) => Self::Replacement(r),
|
|
||||||
},
|
|
||||||
RelatesToJsonRepr::Reply { in_reply_to } => Self::Reply { in_reply_to },
|
|
||||||
RelatesToJsonRepr::Custom(v) => Self::_Custom(v),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// 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)]
|
||||||
@ -1189,3 +1111,36 @@ fn formatted_or_plain_body<'a>(formatted: &'a Option<FormattedBody>, body: &'a s
|
|||||||
body
|
body
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use matches::assert_matches;
|
||||||
|
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;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn deserialize_reply() {
|
||||||
|
let ev_id = event_id!("$1598361704261elfgc:localhost");
|
||||||
|
|
||||||
|
let json = json!({
|
||||||
|
"msgtype": "m.text",
|
||||||
|
"body": "<text msg>",
|
||||||
|
"m.relates_to": {
|
||||||
|
"m.in_reply_to": {
|
||||||
|
"event_id": ev_id,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
assert_matches!(
|
||||||
|
from_json_value::<MessageEventContent>(json).unwrap(),
|
||||||
|
MessageEventContent {
|
||||||
|
msgtype: MessageType::Text(_),
|
||||||
|
relates_to: Some(Relation::Reply { in_reply_to: InReplyTo { event_id } }),
|
||||||
|
} if event_id == ev_id
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -5,35 +5,20 @@ use serde_json::value::RawValue as RawJsonValue;
|
|||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
from_raw_json_value,
|
from_raw_json_value,
|
||||||
room::message::{MessageEventContent, MessageType, Relation},
|
room::message::{MessageEventContent, MessageType},
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Helper struct to determine the msgtype, relates_to and new_content fields
|
|
||||||
/// from a `serde_json::value::RawValue`
|
|
||||||
#[derive(Debug, Deserialize)]
|
|
||||||
struct MessageContentDeHelper {
|
|
||||||
#[serde(rename = "m.relates_to")]
|
|
||||||
relates_to: Option<Relation>,
|
|
||||||
|
|
||||||
#[cfg(feature = "unstable-pre-spec")]
|
|
||||||
#[serde(rename = "m.new_content")]
|
|
||||||
new_content: Option<Box<MessageEventContent>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'de> Deserialize<'de> for MessageEventContent {
|
impl<'de> Deserialize<'de> for MessageEventContent {
|
||||||
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||||
where
|
where
|
||||||
D: de::Deserializer<'de>,
|
D: de::Deserializer<'de>,
|
||||||
{
|
{
|
||||||
let json = Box::<RawJsonValue>::deserialize(deserializer)?;
|
let json = Box::<RawJsonValue>::deserialize(deserializer)?;
|
||||||
let helper = from_raw_json_value::<MessageContentDeHelper, D::Error>(&json)?;
|
let mut deserializer = serde_json::Deserializer::from_str(json.get());
|
||||||
|
let relation =
|
||||||
|
super::relation_serde::deserialize(&mut deserializer).map_err(de::Error::custom)?;
|
||||||
|
|
||||||
Ok(Self {
|
Ok(Self { msgtype: from_raw_json_value(&json)?, relates_to: relation })
|
||||||
msgtype: from_raw_json_value(&json)?,
|
|
||||||
relates_to: helper.relates_to,
|
|
||||||
#[cfg(feature = "unstable-pre-spec")]
|
|
||||||
new_content: helper.new_content,
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -8,45 +8,57 @@
|
|||||||
|
|
||||||
use ruma_identifiers::EventId;
|
use ruma_identifiers::EventId;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use serde_json::Value as JsonValue;
|
|
||||||
|
#[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
|
/// Enum modeling the different ways relationships can be expressed in a `m.relates_to` field of an
|
||||||
/// event.
|
/// `m.room.message` or `m.room.encrypted` event.
|
||||||
#[derive(Clone, Debug, Deserialize, Serialize)]
|
#[derive(Clone, Debug)]
|
||||||
#[serde(untagged)]
|
#[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)]
|
||||||
pub(crate) enum RelatesToJsonRepr {
|
pub enum Relation {
|
||||||
/// A relation which contains subtypes indicating the type of the relationship with the
|
|
||||||
/// `rel_type` field.
|
|
||||||
#[cfg(feature = "unstable-pre-spec")]
|
|
||||||
Relation(RelationJsonRepr),
|
|
||||||
|
|
||||||
/// An `m.in_reply_to` relationship indicating that the event is a reply to another event.
|
|
||||||
Reply {
|
|
||||||
/// Information about another message being replied to.
|
|
||||||
#[serde(rename = "m.in_reply_to")]
|
|
||||||
in_reply_to: InReplyTo,
|
|
||||||
},
|
|
||||||
|
|
||||||
/// Custom, unsupported relationship.
|
|
||||||
Custom(JsonValue),
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A relation, which associates new information to an existing event.
|
|
||||||
#[derive(Clone, Debug, Deserialize, Serialize)]
|
|
||||||
#[cfg(feature = "unstable-pre-spec")]
|
|
||||||
#[serde(tag = "rel_type")]
|
|
||||||
pub(crate) enum RelationJsonRepr {
|
|
||||||
/// An annotation to an event.
|
|
||||||
#[serde(rename = "m.annotation")]
|
|
||||||
Annotation(Annotation),
|
|
||||||
|
|
||||||
/// A reference to another event.
|
/// A reference to another event.
|
||||||
#[serde(rename = "m.reference")]
|
#[cfg(feature = "unstable-pre-spec")]
|
||||||
|
#[cfg_attr(docsrs, doc(cfg(feature = "unstable-pre-spec")))]
|
||||||
Reference(Reference),
|
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.
|
/// An event that replaces another event.
|
||||||
#[serde(rename = "m.replace")]
|
#[cfg(feature = "unstable-pre-spec")]
|
||||||
|
#[cfg_attr(docsrs, doc(cfg(feature = "unstable-pre-spec")))]
|
||||||
Replacement(Replacement),
|
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")))]
|
||||||
|
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.
|
/// Information about the event a "rich reply" is replying to.
|
||||||
@ -99,99 +111,3 @@ impl Annotation {
|
|||||||
Self { event_id, key }
|
Self { event_id, key }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// An event replacing 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 Replacement {
|
|
||||||
/// The event this event is replacing.
|
|
||||||
pub event_id: EventId,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(feature = "unstable-pre-spec")]
|
|
||||||
impl Replacement {
|
|
||||||
/// Creates a new `Replacement` with the given event ID.
|
|
||||||
pub fn new(event_id: EventId) -> Self {
|
|
||||||
Self { event_id }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod tests {
|
|
||||||
use matches::assert_matches;
|
|
||||||
use ruma_identifiers::event_id;
|
|
||||||
use serde_json::{from_value as from_json_value, json};
|
|
||||||
|
|
||||||
use crate::room::message::Relation;
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn reply_deserialize() {
|
|
||||||
let event_id = event_id!("$1598361704261elfgc:localhost");
|
|
||||||
|
|
||||||
let json = json!({
|
|
||||||
"m.in_reply_to": {
|
|
||||||
"event_id": event_id,
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
assert_matches!(
|
|
||||||
from_json_value::<Relation>(json).unwrap(),
|
|
||||||
Relation::Reply { in_reply_to }
|
|
||||||
if in_reply_to.event_id == event_id
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
#[cfg(feature = "unstable-pre-spec")]
|
|
||||||
fn reference_deserialize() {
|
|
||||||
let event_id = event_id!("$1598361704261elfgc:localhost");
|
|
||||||
|
|
||||||
let json = json!({
|
|
||||||
"rel_type": "m.reference",
|
|
||||||
"event_id": event_id,
|
|
||||||
});
|
|
||||||
|
|
||||||
assert_matches!(
|
|
||||||
from_json_value::<Relation>(json).unwrap(),
|
|
||||||
Relation::Reference(reference)
|
|
||||||
if reference.event_id == event_id
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
#[cfg(feature = "unstable-pre-spec")]
|
|
||||||
fn replacement_deserialization() {
|
|
||||||
let event_id = event_id!("$1598361704261elfgc:localhost");
|
|
||||||
|
|
||||||
let json = json!({
|
|
||||||
"rel_type": "m.replace",
|
|
||||||
"event_id": event_id,
|
|
||||||
});
|
|
||||||
|
|
||||||
assert_matches!(
|
|
||||||
from_json_value::<Relation>(json).unwrap(),
|
|
||||||
Relation::Replacement(replacement)
|
|
||||||
if replacement.event_id == event_id
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
#[cfg(feature = "unstable-pre-spec")]
|
|
||||||
fn annotation_deserialize() {
|
|
||||||
let event_id = event_id!("$1598361704261elfgc:localhost");
|
|
||||||
|
|
||||||
let json = json!({
|
|
||||||
"rel_type": "m.annotation",
|
|
||||||
"event_id": event_id,
|
|
||||||
"key": "🦛",
|
|
||||||
});
|
|
||||||
|
|
||||||
assert_matches!(
|
|
||||||
from_json_value::<Relation>(json).unwrap(),
|
|
||||||
Relation::Annotation(annotation)
|
|
||||||
if annotation.event_id == event_id && annotation.key == "🦛"
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
162
crates/ruma-events/src/room/relationships/relation_serde.rs
Normal file
162
crates/ruma-events/src/room/relationships/relation_serde.rs
Normal file
@ -0,0 +1,162 @@
|
|||||||
|
#[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::{Annotation, Reference, 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::Annotation(a) => Relation::Annotation(a),
|
||||||
|
RelationJsonRepr::Reference(r) => Relation::Reference(r),
|
||||||
|
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 {
|
||||||
|
#[cfg(feature = "unstable-pre-spec")]
|
||||||
|
Relation::Annotation(r) => EventWithRelatesToJsonRepr::new(RelatesToJsonRepr {
|
||||||
|
relation: Some(RelationJsonRepr::Annotation(r.clone())),
|
||||||
|
..Default::default()
|
||||||
|
}),
|
||||||
|
#[cfg(feature = "unstable-pre-spec")]
|
||||||
|
Relation::Reference(r) => EventWithRelatesToJsonRepr::new(RelatesToJsonRepr {
|
||||||
|
relation: Some(RelationJsonRepr::Reference(r.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()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Relation::Reply { in_reply_to } => EventWithRelatesToJsonRepr::new(RelatesToJsonRepr {
|
||||||
|
in_reply_to: Some(in_reply_to.clone()),
|
||||||
|
..Default::default()
|
||||||
|
}),
|
||||||
|
};
|
||||||
|
|
||||||
|
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 annotation to an event.
|
||||||
|
#[serde(rename = "m.annotation")]
|
||||||
|
Annotation(Annotation),
|
||||||
|
|
||||||
|
/// A reference to another event.
|
||||||
|
#[serde(rename = "m.reference")]
|
||||||
|
Reference(Reference),
|
||||||
|
|
||||||
|
/// 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,
|
||||||
|
}
|
@ -11,10 +11,10 @@ use ruma_events::{
|
|||||||
use ruma_events::{
|
use ruma_events::{
|
||||||
room::{
|
room::{
|
||||||
message::{
|
message::{
|
||||||
AudioMessageEventContent, MessageEvent, MessageEventContent, MessageType, Relation,
|
AudioMessageEventContent, MessageEvent, MessageEventContent, MessageType,
|
||||||
TextMessageEventContent,
|
TextMessageEventContent,
|
||||||
},
|
},
|
||||||
relationships::InReplyTo,
|
relationships::{InReplyTo, Relation},
|
||||||
},
|
},
|
||||||
Unsigned,
|
Unsigned,
|
||||||
};
|
};
|
||||||
@ -244,7 +244,7 @@ fn edit_deserialization_061() {
|
|||||||
formatted: None,
|
formatted: None,
|
||||||
..
|
..
|
||||||
}),
|
}),
|
||||||
relates_to: Some(Relation::_Custom(_)),
|
relates_to: None,
|
||||||
..
|
..
|
||||||
} if body == "s/foo/bar"
|
} if body == "s/foo/bar"
|
||||||
);
|
);
|
||||||
@ -277,8 +277,7 @@ fn edit_deserialization_future() {
|
|||||||
formatted: None,
|
formatted: None,
|
||||||
..
|
..
|
||||||
}),
|
}),
|
||||||
relates_to: Some(Relation::Replacement(Replacement { event_id })),
|
relates_to: Some(Relation::Replacement(Replacement { event_id, new_content })),
|
||||||
new_content: Some(new_content),
|
|
||||||
..
|
..
|
||||||
} if body == "s/foo/bar"
|
} if body == "s/foo/bar"
|
||||||
&& event_id == ev_id
|
&& event_id == ev_id
|
||||||
|
Loading…
x
Reference in New Issue
Block a user