Move new_content and relates_to fields to MessageEventContent struct
MessageEventContent used to be an enum, it now has a msgtype field where the enum (now called MessageType) sits.
This commit is contained in:
parent
798cd49e9e
commit
12c294422b
@ -5,7 +5,7 @@ use http::Uri;
|
||||
use ruma::{
|
||||
api::client::r0::{filter::FilterDefinition, sync::sync_events},
|
||||
events::{
|
||||
room::message::{MessageEventContent, TextMessageEventContent},
|
||||
room::message::{MessageEventContent, MessageType, TextMessageEventContent},
|
||||
AnySyncMessageEvent, AnySyncRoomEvent, SyncMessageEvent,
|
||||
},
|
||||
presence::PresenceState,
|
||||
@ -40,9 +40,13 @@ async fn log_messages(homeserver_url: Uri, username: &str, password: &str) -> an
|
||||
if let AnySyncRoomEvent::Message(AnySyncMessageEvent::RoomMessage(
|
||||
SyncMessageEvent {
|
||||
content:
|
||||
MessageEventContent::Text(TextMessageEventContent {
|
||||
body: msg_body, ..
|
||||
}),
|
||||
MessageEventContent {
|
||||
msgtype:
|
||||
MessageType::Text(TextMessageEventContent {
|
||||
body: msg_body, ..
|
||||
}),
|
||||
..
|
||||
},
|
||||
sender,
|
||||
..
|
||||
},
|
||||
|
@ -34,8 +34,63 @@ pub type MessageEvent = OuterMessageEvent<MessageEventContent>;
|
||||
#[derive(Clone, Debug, Serialize, MessageEventContent)]
|
||||
#[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)]
|
||||
#[ruma_event(type = "m.room.message")]
|
||||
#[serde(untagged)]
|
||||
pub enum MessageEventContent {
|
||||
pub struct MessageEventContent {
|
||||
/// A key which identifies the type of message being sent.
|
||||
///
|
||||
/// This also holds the specific content of each message.
|
||||
#[serde(flatten)]
|
||||
pub msgtype: MessageType,
|
||||
|
||||
/// Information about related messages for
|
||||
/// [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(_))`.
|
||||
#[cfg(feature = "unstable-pre-spec")]
|
||||
#[serde(rename = "m.new_content", skip_serializing_if = "Option::is_none")]
|
||||
pub new_content: Option<Box<MessageEventContent>>,
|
||||
}
|
||||
|
||||
impl MessageEventContent {
|
||||
/// Create a `MessageEventContent` with the given `MessageType`.
|
||||
pub fn new(msgtype: MessageType) -> Self {
|
||||
Self {
|
||||
msgtype,
|
||||
relates_to: None,
|
||||
#[cfg(feature = "unstable-pre-spec")]
|
||||
new_content: None,
|
||||
}
|
||||
}
|
||||
|
||||
/// A constructor to create a plain text message.
|
||||
pub fn text_plain(body: impl Into<String>) -> Self {
|
||||
Self::new(MessageType::Text(TextMessageEventContent::plain(body)))
|
||||
}
|
||||
|
||||
/// A constructor to create an html message.
|
||||
pub fn text_html(body: impl Into<String>, html_body: impl Into<String>) -> Self {
|
||||
Self::new(MessageType::Text(TextMessageEventContent::html(body, html_body)))
|
||||
}
|
||||
|
||||
/// A constructor to create a plain text notice.
|
||||
pub fn notice_plain(body: impl Into<String>) -> Self {
|
||||
Self::new(MessageType::Notice(NoticeMessageEventContent::plain(body)))
|
||||
}
|
||||
|
||||
/// A constructor to create an html notice.
|
||||
pub fn notice_html(body: impl Into<String>, html_body: impl Into<String>) -> Self {
|
||||
Self::new(MessageType::Notice(NoticeMessageEventContent::html(body, html_body)))
|
||||
}
|
||||
}
|
||||
|
||||
/// The content that is specific to each message type variant.
|
||||
#[derive(Clone, Debug, Serialize)]
|
||||
#[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)]
|
||||
#[serde(tag = "msgtype")]
|
||||
pub enum MessageType {
|
||||
/// An audio message.
|
||||
Audio(AudioMessageEventContent),
|
||||
|
||||
@ -72,6 +127,12 @@ pub enum MessageEventContent {
|
||||
_Custom(CustomEventContent),
|
||||
}
|
||||
|
||||
impl From<MessageType> for MessageEventContent {
|
||||
fn from(msgtype: MessageType) -> Self {
|
||||
Self::new(msgtype)
|
||||
}
|
||||
}
|
||||
|
||||
/// 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)]
|
||||
@ -132,28 +193,6 @@ impl From<RelatesToJsonRepr> for Relation {
|
||||
}
|
||||
}
|
||||
|
||||
impl MessageEventContent {
|
||||
/// A convenience constructor to create a plain text message.
|
||||
pub fn text_plain(body: impl Into<String>) -> Self {
|
||||
Self::Text(TextMessageEventContent::plain(body))
|
||||
}
|
||||
|
||||
/// A convenience constructor to create an html message.
|
||||
pub fn text_html(body: impl Into<String>, html_body: impl Into<String>) -> Self {
|
||||
Self::Text(TextMessageEventContent::html(body, html_body))
|
||||
}
|
||||
|
||||
/// A convenience constructor to create an plain text notice.
|
||||
pub fn notice_plain(body: impl Into<String>) -> Self {
|
||||
Self::Notice(NoticeMessageEventContent::plain(body))
|
||||
}
|
||||
|
||||
/// A convenience constructor to create an html notice.
|
||||
pub fn notice_html(body: impl Into<String>, html_body: impl Into<String>) -> Self {
|
||||
Self::Notice(NoticeMessageEventContent::html(body, html_body))
|
||||
}
|
||||
}
|
||||
|
||||
/// The payload for an audio message.
|
||||
#[derive(Clone, Debug, Deserialize, Serialize)]
|
||||
#[serde(tag = "msgtype", rename = "m.audio")]
|
||||
@ -321,30 +360,12 @@ pub struct NoticeMessageEventContent {
|
||||
/// Formatted form of the message `body`.
|
||||
#[serde(flatten)]
|
||||
pub formatted: Option<FormattedBody>,
|
||||
|
||||
/// Information about related messages for
|
||||
/// [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(_))`.
|
||||
#[cfg(feature = "unstable-pre-spec")]
|
||||
#[serde(rename = "m.new_content", skip_serializing_if = "Option::is_none")]
|
||||
pub new_content: Option<Box<MessageEventContent>>,
|
||||
}
|
||||
|
||||
impl NoticeMessageEventContent {
|
||||
/// A convenience constructor to create a plain text notice.
|
||||
pub fn plain(body: impl Into<String>) -> Self {
|
||||
Self {
|
||||
body: body.into(),
|
||||
formatted: None,
|
||||
relates_to: None,
|
||||
#[cfg(feature = "unstable-pre-spec")]
|
||||
new_content: None,
|
||||
}
|
||||
Self { body: body.into(), formatted: None }
|
||||
}
|
||||
|
||||
/// A convenience constructor to create an html notice.
|
||||
@ -453,30 +474,12 @@ pub struct TextMessageEventContent {
|
||||
/// Formatted form of the message `body`.
|
||||
#[serde(flatten)]
|
||||
pub formatted: Option<FormattedBody>,
|
||||
|
||||
/// Information about related messages for
|
||||
/// [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(_))`.
|
||||
#[cfg(feature = "unstable-pre-spec")]
|
||||
#[serde(rename = "m.new_content", skip_serializing_if = "Option::is_none")]
|
||||
pub new_content: Option<Box<MessageEventContent>>,
|
||||
}
|
||||
|
||||
impl TextMessageEventContent {
|
||||
/// A convenience constructor to create a plain text message.
|
||||
pub fn plain(body: impl Into<String>) -> Self {
|
||||
Self {
|
||||
body: body.into(),
|
||||
formatted: None,
|
||||
relates_to: None,
|
||||
#[cfg(feature = "unstable-pre-spec")]
|
||||
new_content: None,
|
||||
}
|
||||
Self { body: body.into(), formatted: None }
|
||||
}
|
||||
|
||||
/// A convenience constructor to create an html message.
|
||||
|
@ -1,15 +1,23 @@
|
||||
//! `Deserialize` implementation for MessageEventContent
|
||||
//! `Deserialize` implementation for MessageEventContent and MessageType.
|
||||
|
||||
use serde::{de, Deserialize};
|
||||
use serde_json::value::RawValue as RawJsonValue;
|
||||
|
||||
use crate::{from_raw_json_value, room::message::MessageEventContent};
|
||||
use crate::{
|
||||
from_raw_json_value,
|
||||
room::message::{MessageEventContent, MessageType, Relation},
|
||||
};
|
||||
|
||||
/// Helper struct to determine the msgtype from a `serde_json::value::RawValue`
|
||||
/// Helper struct to determine the msgtype, relates_to and new_content fields
|
||||
/// from a `serde_json::value::RawValue`
|
||||
#[derive(Debug, Deserialize)]
|
||||
struct MessageDeHelper {
|
||||
/// The message type field
|
||||
msgtype: String,
|
||||
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> de::Deserialize<'de> for MessageEventContent {
|
||||
@ -18,7 +26,31 @@ impl<'de> de::Deserialize<'de> for MessageEventContent {
|
||||
D: de::Deserializer<'de>,
|
||||
{
|
||||
let json = Box::<RawJsonValue>::deserialize(deserializer)?;
|
||||
let MessageDeHelper { msgtype } = from_raw_json_value(&json)?;
|
||||
let helper = from_raw_json_value::<MessageContentDeHelper, D::Error>(&json)?;
|
||||
|
||||
Ok(Self {
|
||||
msgtype: from_raw_json_value(&json)?,
|
||||
relates_to: helper.relates_to,
|
||||
#[cfg(feature = "unstable-pre-spec")]
|
||||
new_content: helper.new_content,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// Helper struct to determine the msgtype from a `serde_json::value::RawValue`
|
||||
#[derive(Debug, Deserialize)]
|
||||
struct MessageTypeDeHelper {
|
||||
/// The message type field
|
||||
msgtype: String,
|
||||
}
|
||||
|
||||
impl<'de> de::Deserialize<'de> for MessageType {
|
||||
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||
where
|
||||
D: de::Deserializer<'de>,
|
||||
{
|
||||
let json = Box::<RawJsonValue>::deserialize(deserializer)?;
|
||||
let MessageTypeDeHelper { msgtype } = from_raw_json_value(&json)?;
|
||||
|
||||
Ok(match msgtype.as_ref() {
|
||||
"m.audio" => Self::Audio(from_raw_json_value(&json)?),
|
||||
|
@ -5,7 +5,7 @@ use serde_json::{from_value as from_json_value, json, Value as JsonValue};
|
||||
use ruma_events::{
|
||||
room::{
|
||||
aliases::AliasesEventContent,
|
||||
message::{MessageEventContent, TextMessageEventContent},
|
||||
message::{MessageEventContent, MessageType, TextMessageEventContent},
|
||||
power_levels::PowerLevelsEventContent,
|
||||
},
|
||||
AnyEvent, AnyMessageEvent, AnyRoomEvent, AnyStateEvent, AnyStateEventContent,
|
||||
@ -137,12 +137,14 @@ fn message_event_sync_deserialization() {
|
||||
from_json_value::<AnySyncRoomEvent>(json_data),
|
||||
Ok(AnySyncRoomEvent::Message(
|
||||
AnySyncMessageEvent::RoomMessage(SyncMessageEvent {
|
||||
content: MessageEventContent::Text(TextMessageEventContent {
|
||||
body,
|
||||
formatted: Some(formatted),
|
||||
relates_to: None,
|
||||
content: MessageEventContent {
|
||||
msgtype: MessageType::Text(TextMessageEventContent {
|
||||
body,
|
||||
formatted: Some(formatted),
|
||||
..
|
||||
}),
|
||||
..
|
||||
}),
|
||||
},
|
||||
..
|
||||
})
|
||||
))
|
||||
@ -177,12 +179,14 @@ fn message_room_event_deserialization() {
|
||||
from_json_value::<AnyRoomEvent>(json_data),
|
||||
Ok(AnyRoomEvent::Message(
|
||||
AnyMessageEvent::RoomMessage(MessageEvent {
|
||||
content: MessageEventContent::Text(TextMessageEventContent {
|
||||
body,
|
||||
formatted: Some(formatted),
|
||||
relates_to: None,
|
||||
content: MessageEventContent {
|
||||
msgtype: MessageType::Text(TextMessageEventContent {
|
||||
body,
|
||||
formatted: Some(formatted),
|
||||
..
|
||||
}),
|
||||
..
|
||||
}),
|
||||
},
|
||||
..
|
||||
})
|
||||
))
|
||||
@ -217,12 +221,14 @@ fn message_event_deserialization() {
|
||||
from_json_value::<AnyEvent>(json_data),
|
||||
Ok(AnyEvent::Message(
|
||||
AnyMessageEvent::RoomMessage(MessageEvent {
|
||||
content: MessageEventContent::Text(TextMessageEventContent {
|
||||
body,
|
||||
formatted: Some(formatted),
|
||||
relates_to: None,
|
||||
content: MessageEventContent {
|
||||
msgtype: MessageType::Text(TextMessageEventContent {
|
||||
body,
|
||||
formatted: Some(formatted),
|
||||
..
|
||||
}),
|
||||
..
|
||||
}),
|
||||
},
|
||||
..
|
||||
})
|
||||
))
|
||||
|
@ -11,7 +11,7 @@ use ruma_events::{
|
||||
room::{
|
||||
message::{
|
||||
AudioMessageEventContent, CustomEventContent, MessageEvent, MessageEventContent,
|
||||
Relation, TextMessageEventContent,
|
||||
MessageType, Relation, TextMessageEventContent,
|
||||
},
|
||||
relationships::InReplyTo,
|
||||
},
|
||||
@ -26,12 +26,12 @@ use serde_json::{from_value as from_json_value, json, to_value as to_json_value}
|
||||
#[test]
|
||||
fn serialization() {
|
||||
let ev = MessageEvent {
|
||||
content: MessageEventContent::Audio(AudioMessageEventContent {
|
||||
content: MessageEventContent::new(MessageType::Audio(AudioMessageEventContent {
|
||||
body: "test".into(),
|
||||
info: None,
|
||||
url: Some("http://example.com/audio.mp3".into()),
|
||||
file: None,
|
||||
}),
|
||||
})),
|
||||
event_id: event_id!("$143273582443PhrSn:example.org"),
|
||||
origin_server_ts: UNIX_EPOCH + Duration::from_millis(10_000),
|
||||
room_id: room_id!("!testroomid:example.org"),
|
||||
@ -58,12 +58,13 @@ fn serialization() {
|
||||
|
||||
#[test]
|
||||
fn content_serialization() {
|
||||
let message_event_content = MessageEventContent::Audio(AudioMessageEventContent {
|
||||
body: "test".into(),
|
||||
info: None,
|
||||
url: Some("http://example.com/audio.mp3".into()),
|
||||
file: None,
|
||||
});
|
||||
let message_event_content =
|
||||
MessageEventContent::new(MessageType::Audio(AudioMessageEventContent {
|
||||
body: "test".into(),
|
||||
info: None,
|
||||
url: Some("http://example.com/audio.mp3".into()),
|
||||
file: None,
|
||||
}));
|
||||
|
||||
assert_eq!(
|
||||
to_json_value(&message_event_content).unwrap(),
|
||||
@ -81,7 +82,7 @@ fn custom_content_serialization() {
|
||||
"custom_field".into() => json!("baba"),
|
||||
"another_one".into() => json!("abab"),
|
||||
};
|
||||
let custom_event_content = MessageEventContent::_Custom(CustomEventContent {
|
||||
let custom_event_content = MessageType::_Custom(CustomEventContent {
|
||||
msgtype: "my_custom_msgtype".into(),
|
||||
data: json_data,
|
||||
});
|
||||
@ -110,11 +111,11 @@ fn custom_content_deserialization() {
|
||||
};
|
||||
|
||||
assert_matches!(
|
||||
from_json_value::<Raw<MessageEventContent>>(json_data)
|
||||
from_json_value::<Raw<MessageType>>(json_data)
|
||||
.unwrap()
|
||||
.deserialize()
|
||||
.unwrap(),
|
||||
MessageEventContent::_Custom(CustomEventContent {
|
||||
MessageType::_Custom(CustomEventContent {
|
||||
msgtype,
|
||||
data
|
||||
}) if msgtype == "my_custom_msgtype"
|
||||
@ -124,10 +125,8 @@ fn custom_content_deserialization() {
|
||||
|
||||
#[test]
|
||||
fn formatted_body_serialization() {
|
||||
let message_event_content = MessageEventContent::Text(TextMessageEventContent::html(
|
||||
"Hello, World!",
|
||||
"Hello, <em>World</em>!",
|
||||
));
|
||||
let message_event_content =
|
||||
MessageEventContent::text_html("Hello, World!", "Hello, <em>World</em>!");
|
||||
|
||||
assert_eq!(
|
||||
to_json_value(&message_event_content).unwrap(),
|
||||
@ -142,9 +141,8 @@ fn formatted_body_serialization() {
|
||||
|
||||
#[test]
|
||||
fn plain_text_content_serialization() {
|
||||
let message_event_content = MessageEventContent::Text(TextMessageEventContent::plain(
|
||||
"> <@test:example.com> test\n\ntest reply",
|
||||
));
|
||||
let message_event_content =
|
||||
MessageEventContent::text_plain("> <@test:example.com> test\n\ntest reply");
|
||||
|
||||
assert_eq!(
|
||||
to_json_value(&message_event_content).unwrap(),
|
||||
@ -157,13 +155,12 @@ fn plain_text_content_serialization() {
|
||||
|
||||
#[test]
|
||||
fn relates_to_content_serialization() {
|
||||
let message_event_content = MessageEventContent::Text(
|
||||
assign!(TextMessageEventContent::plain("> <@test:example.com> test\n\ntest reply"), {
|
||||
let message_event_content =
|
||||
assign!(MessageEventContent::text_plain("> <@test:example.com> test\n\ntest reply"), {
|
||||
relates_to: Some(Relation::Reply {
|
||||
in_reply_to: InReplyTo { event_id: event_id!("$15827405538098VGFWH:example.com") },
|
||||
}),
|
||||
}),
|
||||
);
|
||||
});
|
||||
|
||||
let json_data = json!({
|
||||
"body": "> <@test:example.com> test\n\ntest reply",
|
||||
@ -195,12 +192,15 @@ fn edit_deserialization_061() {
|
||||
|
||||
assert_matches!(
|
||||
from_json_value::<MessageEventContent>(json_data).unwrap(),
|
||||
MessageEventContent::Text(TextMessageEventContent {
|
||||
body,
|
||||
formatted: None,
|
||||
MessageEventContent {
|
||||
msgtype: MessageType::Text(TextMessageEventContent {
|
||||
body,
|
||||
formatted: None,
|
||||
..
|
||||
}),
|
||||
relates_to: Some(Relation::Custom(_)),
|
||||
..
|
||||
}) if body == "s/foo/bar"
|
||||
} if body == "s/foo/bar"
|
||||
);
|
||||
}
|
||||
|
||||
@ -225,23 +225,27 @@ fn edit_deserialization_future() {
|
||||
|
||||
assert_matches!(
|
||||
from_json_value::<MessageEventContent>(json_data).unwrap(),
|
||||
MessageEventContent::Text(TextMessageEventContent {
|
||||
body,
|
||||
formatted: None,
|
||||
MessageEventContent {
|
||||
msgtype: MessageType::Text(TextMessageEventContent {
|
||||
body,
|
||||
formatted: None,
|
||||
..
|
||||
}),
|
||||
relates_to: Some(Relation::Replacement(Replacement { event_id })),
|
||||
new_content: Some(new_content),
|
||||
..
|
||||
}) if body == "s/foo/bar"
|
||||
} if body == "s/foo/bar"
|
||||
&& event_id == ev_id
|
||||
&& matches!(
|
||||
&*new_content,
|
||||
MessageEventContent::Text(TextMessageEventContent {
|
||||
body,
|
||||
formatted: None,
|
||||
relates_to: None,
|
||||
new_content: None,
|
||||
MessageEventContent {
|
||||
msgtype: MessageType::Text(TextMessageEventContent {
|
||||
body,
|
||||
formatted: None,
|
||||
..
|
||||
}),
|
||||
..
|
||||
}) if body == "bar"
|
||||
} if body == "bar"
|
||||
)
|
||||
);
|
||||
}
|
||||
@ -266,12 +270,15 @@ fn verification_request_deserialization() {
|
||||
|
||||
assert_matches!(
|
||||
from_json_value::<MessageEventContent>(json_data).unwrap(),
|
||||
MessageEventContent::VerificationRequest(KeyVerificationRequestEventContent {
|
||||
body,
|
||||
to,
|
||||
from_device,
|
||||
methods,
|
||||
}) if body == "@example:localhost is requesting to verify your key, ..."
|
||||
MessageEventContent {
|
||||
msgtype: MessageType::VerificationRequest(KeyVerificationRequestEventContent {
|
||||
body,
|
||||
to,
|
||||
from_device,
|
||||
methods,
|
||||
}),
|
||||
..
|
||||
} if body == "@example:localhost is requesting to verify your key, ..."
|
||||
&& to == user_id
|
||||
&& from_device == device_id
|
||||
&& methods.contains(&VerificationMethod::MSasV1)
|
||||
@ -299,7 +306,7 @@ fn verification_request_serialization() {
|
||||
"methods": methods
|
||||
});
|
||||
|
||||
let content = MessageEventContent::VerificationRequest(KeyVerificationRequestEventContent {
|
||||
let content = MessageType::VerificationRequest(KeyVerificationRequestEventContent {
|
||||
to: user_id,
|
||||
from_device: device_id,
|
||||
body,
|
||||
@ -322,12 +329,15 @@ fn content_deserialization() {
|
||||
.unwrap()
|
||||
.deserialize()
|
||||
.unwrap(),
|
||||
MessageEventContent::Audio(AudioMessageEventContent {
|
||||
body,
|
||||
info: None,
|
||||
url: Some(url),
|
||||
file: None,
|
||||
}) if body == "test" && url == "http://example.com/audio.mp3"
|
||||
MessageEventContent {
|
||||
msgtype: MessageType::Audio(AudioMessageEventContent {
|
||||
body,
|
||||
info: None,
|
||||
url: Some(url),
|
||||
file: None,
|
||||
}),
|
||||
..
|
||||
} if body == "test" && url == "http://example.com/audio.mp3"
|
||||
);
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user