events: Make Replacement generic over the parent type

Because the new content must be of the same type.
This commit is contained in:
Kévin Commaille 2022-10-12 19:22:40 +02:00 committed by Kévin Commaille
parent 7c802c89ca
commit 7c0f7ba5f1
15 changed files with 53 additions and 43 deletions

View File

@ -40,7 +40,7 @@ pub struct AudioEventContent {
/// Information about related messages.
#[serde(flatten, skip_serializing_if = "Option::is_none")]
pub relates_to: Option<Relation>,
pub relates_to: Option<Relation<AudioEventContentWithoutRelation>>,
}
impl AudioEventContent {

View File

@ -24,7 +24,7 @@ pub struct EmoteEventContent {
/// Information about related messages.
#[serde(flatten, skip_serializing_if = "Option::is_none")]
pub relates_to: Option<Relation>,
pub relates_to: Option<Relation<EmoteEventContentWithoutRelation>>,
}
impl EmoteEventContent {

View File

@ -35,7 +35,7 @@ pub struct FileEventContent {
/// Information about related messages.
#[serde(flatten, skip_serializing_if = "Option::is_none")]
pub relates_to: Option<Relation>,
pub relates_to: Option<Relation<FileEventContentWithoutRelation>>,
}
impl FileEventContent {

View File

@ -51,7 +51,7 @@ pub struct ImageEventContent {
/// Information about related messages.
#[serde(flatten, skip_serializing_if = "Option::is_none")]
pub relates_to: Option<Relation>,
pub relates_to: Option<Relation<ImageEventContentWithoutRelation>>,
}
impl ImageEventContent {

View File

@ -40,7 +40,7 @@ pub struct LocationEventContent {
/// Information about related messages.
#[serde(flatten, skip_serializing_if = "Option::is_none")]
pub relates_to: Option<Relation>,
pub relates_to: Option<Relation<LocationEventContentWithoutRelation>>,
}
impl LocationEventContent {

View File

@ -73,7 +73,7 @@ pub struct MessageEventContent {
/// Information about related messages.
#[serde(flatten, skip_serializing_if = "Option::is_none")]
pub relates_to: Option<Relation>,
pub relates_to: Option<Relation<MessageEventContentWithoutRelation>>,
}
impl MessageEventContent {

View File

@ -24,7 +24,7 @@ pub struct NoticeEventContent {
/// Information about related messages.
#[serde(flatten, skip_serializing_if = "Option::is_none")]
pub relates_to: Option<Relation>,
pub relates_to: Option<Relation<NoticeEventContentWithoutRelation>>,
}
impl NoticeEventContent {

View File

@ -107,8 +107,8 @@ pub enum Relation {
_Custom,
}
impl From<message::Relation> for Relation {
fn from(rel: message::Relation) -> Self {
impl<C> From<message::Relation<C>> for Relation {
fn from(rel: message::Relation<C>) -> Self {
match rel {
message::Relation::Reply { in_reply_to } => Self::Reply { in_reply_to },
message::Relation::Replacement(re) => {

View File

@ -62,7 +62,7 @@ pub struct RoomMessageEventContent {
///
/// [rich replies]: https://spec.matrix.org/v1.2/client-server-api/#rich-replies
#[serde(flatten, skip_serializing_if = "Option::is_none")]
pub relates_to: Option<Relation>,
pub relates_to: Option<Relation<MessageType>>,
}
impl RoomMessageEventContent {
@ -467,7 +467,7 @@ impl From<RoomMessageEventContent> for MessageType {
#[derive(Clone, Debug)]
#[allow(clippy::manual_non_exhaustive)]
#[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)]
pub enum Relation {
pub enum Relation<C> {
/// An `m.in_reply_to` relation indicating that the event is a reply to another event.
Reply {
/// Information about another message being replied to.
@ -475,7 +475,7 @@ pub enum Relation {
},
/// An event that replaces another event.
Replacement(Replacement),
Replacement(Replacement<C>),
/// An event that belongs to a thread.
Thread(Thread),
@ -504,17 +504,17 @@ impl InReplyTo {
/// [replaces another event]: https://spec.matrix.org/v1.4/client-server-api/#event-replacements
#[derive(Clone, Debug)]
#[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)]
pub struct Replacement {
pub struct Replacement<C> {
/// The ID of the event being replaced.
pub event_id: OwnedEventId,
/// New content.
pub new_content: Box<RoomMessageEventContent>,
pub new_content: C,
}
impl Replacement {
impl<C> Replacement<C> {
/// Creates a new `Replacement` with the given event ID and new content.
pub fn new(event_id: OwnedEventId, new_content: Box<RoomMessageEventContent>) -> Self {
pub fn new(event_id: OwnedEventId, new_content: C) -> Self {
Self { event_id, new_content }
}
}

View File

@ -47,8 +47,8 @@ impl<'de> Deserialize<'de> for RoomMessageEventContent {
{
let json = Box::<RawJsonValue>::deserialize(deserializer)?;
let mut deserializer = serde_json::Deserializer::from_str(json.get());
let relates_to =
Option::<Relation>::deserialize(&mut deserializer).map_err(de::Error::custom)?;
let relates_to = Option::<Relation<MessageType>>::deserialize(&mut deserializer)
.map_err(de::Error::custom)?;
Ok(Self { msgtype: from_raw_json_value(&json)?, relates_to })
}

View File

@ -1,9 +1,12 @@
use serde::{Deserialize, Deserializer, Serialize, Serializer};
use super::{InReplyTo, Relation, Replacement, RoomMessageEventContent, Thread};
use super::{InReplyTo, Relation, Replacement, Thread};
use crate::OwnedEventId;
impl<'de> Deserialize<'de> for Relation {
impl<'de, C> Deserialize<'de> for Relation<C>
where
C: Deserialize<'de>,
{
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
@ -47,17 +50,22 @@ impl<'de> Deserialize<'de> for Relation {
}
}
impl Serialize for Relation {
impl<C> Serialize for Relation<C>
where
C: Clone + Serialize,
{
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
#[allow(clippy::needless_update)]
let json_repr = match self {
Relation::Reply { in_reply_to } => EventWithRelatesToJsonRepr::new(RelatesToJsonRepr {
Relation::Reply { in_reply_to } => {
EventWithRelatesToJsonRepr::<C>::new(RelatesToJsonRepr {
in_reply_to: Some(in_reply_to.clone()),
..Default::default()
}),
})
}
Relation::Replacement(Replacement { event_id, new_content }) => {
EventWithRelatesToJsonRepr {
relates_to: RelatesToJsonRepr {
@ -79,28 +87,34 @@ impl Serialize for Relation {
..Default::default()
})
}
Relation::_Custom => EventWithRelatesToJsonRepr::default(),
Relation::_Custom => EventWithRelatesToJsonRepr::<C>::default(),
};
json_repr.serialize(serializer)
}
}
#[derive(Default, Deserialize, Serialize)]
struct EventWithRelatesToJsonRepr {
#[derive(Deserialize, Serialize)]
struct EventWithRelatesToJsonRepr<C> {
#[serde(rename = "m.relates_to", default, skip_serializing_if = "RelatesToJsonRepr::is_empty")]
relates_to: RelatesToJsonRepr,
#[serde(rename = "m.new_content", skip_serializing_if = "Option::is_none")]
new_content: Option<Box<RoomMessageEventContent>>,
new_content: Option<C>,
}
impl EventWithRelatesToJsonRepr {
impl<C> EventWithRelatesToJsonRepr<C> {
fn new(relates_to: RelatesToJsonRepr) -> Self {
Self { relates_to, new_content: None }
}
}
impl<C> Default for EventWithRelatesToJsonRepr<C> {
fn default() -> Self {
Self { relates_to: RelatesToJsonRepr::default(), new_content: None }
}
}
/// Struct modeling the different ways relationships can be expressed in a `m.relates_to` field of
/// an event.
#[derive(Default, Deserialize, Serialize)]

View File

@ -50,7 +50,7 @@ pub struct VideoEventContent {
/// Information about related messages.
#[serde(flatten, skip_serializing_if = "Option::is_none")]
pub relates_to: Option<Relation>,
pub relates_to: Option<Relation<VideoEventContentWithoutRelation>>,
}
impl VideoEventContent {

View File

@ -38,7 +38,7 @@ pub struct VoiceEventContent {
/// Information about related messages.
#[serde(flatten, skip_serializing_if = "Option::is_none")]
pub relates_to: Option<Relation>,
pub relates_to: Option<Relation<VoiceEventContentWithoutRelation>>,
}
impl VoiceEventContent {

View File

@ -75,7 +75,7 @@ fn replacement_serialize() {
relates_to: Some(Relation::Replacement(
Replacement::new(
event_id!("$1598361704261elfgc").to_owned(),
Box::new(RoomMessageEventContent::text_plain("This is the new content.")),
RoomMessageEventContent::text_plain("This is the new content.").into(),
)
))
}
@ -142,7 +142,7 @@ fn replacement_deserialize() {
}) => replacement
);
assert_eq!(replacement.event_id, "$1598361704261elfgc");
let text = assert_matches!(replacement.new_content.msgtype, MessageType::Text(text) => text);
let text = assert_matches!(replacement.new_content, MessageType::Text(text) => text);
assert_eq!(text.body, "Hello! My name is bar");
}

View File

@ -1,10 +1,7 @@
use assert_matches::assert_matches;
use ruma_common::{
event_id,
events::room::message::{
InReplyTo, MessageType, Relation, RoomMessageEventContent,
RoomMessageEventContentWithoutRelation,
},
events::room::message::{InReplyTo, MessageType, Relation, RoomMessageEventContent},
};
use serde_json::{from_value as from_json_value, json, to_value as to_json_value};
@ -13,7 +10,7 @@ fn serialize_room_message_content_without_relation() {
let mut content = RoomMessageEventContent::text_plain("Hello, world!");
content.relates_to =
Some(Relation::Reply { in_reply_to: InReplyTo::new(event_id!("$eventId").to_owned()) });
let without_relation = RoomMessageEventContentWithoutRelation::from(content);
let without_relation = MessageType::from(content);
#[cfg(not(feature = "unstable-msc3246"))]
assert_eq!(
@ -43,8 +40,8 @@ fn deserialize_room_message_content_without_relation() {
});
let text = assert_matches!(
from_json_value::<RoomMessageEventContentWithoutRelation>(json_data),
Ok(RoomMessageEventContentWithoutRelation::Text(text)) => text
from_json_value::<MessageType>(json_data),
Ok(MessageType::Text(text)) => text
);
assert_eq!(text.body, "Hello, world!");
}
@ -54,8 +51,7 @@ fn convert_room_message_content_without_relation_to_full() {
let mut content = RoomMessageEventContent::text_plain("Hello, world!");
content.relates_to =
Some(Relation::Reply { in_reply_to: InReplyTo::new(event_id!("$eventId").to_owned()) });
let new_content =
RoomMessageEventContent::from(RoomMessageEventContentWithoutRelation::from(content));
let new_content = RoomMessageEventContent::from(MessageType::from(content));
let (text, relates_to) = assert_matches!(
new_content,