events: Add public API for custom / unsupported message types

This commit is contained in:
Jonas Platte 2021-04-11 12:02:53 +02:00
parent e2728a7081
commit 2803ee3721
No known key found for this signature in database
GPG Key ID: CC154DE0E30B7C67
2 changed files with 93 additions and 8 deletions

View File

@ -1,6 +1,6 @@
//! Types for the *m.room.message* event. //! Types for the *m.room.message* event.
use std::collections::BTreeMap; use std::borrow::Cow;
use js_int::UInt; use js_int::UInt;
use ruma_events_macros::MessageEventContent; use ruma_events_macros::MessageEventContent;
@ -8,7 +8,7 @@ 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_serde::StringEnum; use ruma_serde::StringEnum;
use serde::{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")] #[cfg(feature = "unstable-pre-spec")]
@ -23,12 +23,12 @@ pub use super::relationships::InReplyTo;
mod content_serde; mod content_serde;
pub mod feedback; pub mod feedback;
use crate::MessageEvent as OuterMessageEvent; type JsonObject = serde_json::Map<String, JsonValue>;
/// This event is used when sending messages in a room. /// This event is used when sending messages in a room.
/// ///
/// Messages are not limited to be text. /// Messages are not limited to be text.
pub type MessageEvent = OuterMessageEvent<MessageEventContent>; pub type MessageEvent = crate::MessageEvent<MessageEventContent>;
/// The payload for `MessageEvent`. /// The payload for `MessageEvent`.
#[derive(Clone, Debug, Serialize, MessageEventContent)] #[derive(Clone, Debug, Serialize, MessageEventContent)]
@ -127,6 +127,80 @@ pub enum MessageType {
_Custom(CustomEventContent), _Custom(CustomEventContent),
} }
impl MessageType {
/// Creates a `MessageType` with the given `msgtype` string and data.
///
/// Prefer to use the public variants of `MessageType` where possible; this constructor is meant
/// be used for unsupported message types only and does not allow setting arbitrary data for
/// supported ones.
pub fn new(msgtype: &str, data: JsonObject) -> serde_json::Result<Self> {
fn from_json_object<T: DeserializeOwned>(obj: JsonObject) -> serde_json::Result<T> {
serde_json::from_value(JsonValue::Object(obj))
}
Ok(match msgtype {
"m.audio" => Self::Audio(from_json_object(data)?),
"m.emote" => Self::Emote(from_json_object(data)?),
"m.file" => Self::File(from_json_object(data)?),
"m.image" => Self::Image(from_json_object(data)?),
"m.location" => Self::Location(from_json_object(data)?),
"m.notice" => Self::Notice(from_json_object(data)?),
"m.server_notice" => Self::ServerNotice(from_json_object(data)?),
"m.text" => Self::Text(from_json_object(data)?),
"m.video" => Self::Video(from_json_object(data)?),
#[cfg(feature = "unstable-pre-spec")]
"m.key.verification.request" => Self::VerificationRequest(from_json_object(data)?),
_ => Self::_Custom(CustomEventContent { msgtype: msgtype.to_owned(), data }),
})
}
/// Returns a reference to the `msgtype` string.
pub fn msgtype(&self) -> &str {
match self {
Self::Audio(_) => "m.audio",
Self::Emote(_) => "m.emote",
Self::File(_) => "m.file",
Self::Image(_) => "m.image",
Self::Location(_) => "m.location",
Self::Notice(_) => "m.notice",
Self::ServerNotice(_) => "m.server_notice",
Self::Text(_) => "m.text",
Self::Video(_) => "m.video",
#[cfg(feature = "unstable-pre-spec")]
Self::VerificationRequest(_) => "m.key.verification.request",
Self::_Custom(c) => &c.msgtype,
}
}
/// Returns the associated data.
///
/// Prefer to use the public variants of `MessageType` where possible; this method is meant to
/// be used for unsupported message types only.
pub fn data(&self) -> Cow<'_, JsonObject> {
fn serialize<T: Serialize>(obj: &T) -> JsonObject {
match serde_json::to_value(obj).expect("message type serialization to succeed") {
JsonValue::Object(obj) => obj,
_ => panic!("all message types must serialize to objects"),
}
}
match self {
Self::Audio(d) => Cow::Owned(serialize(d)),
Self::Emote(d) => Cow::Owned(serialize(d)),
Self::File(d) => Cow::Owned(serialize(d)),
Self::Image(d) => Cow::Owned(serialize(d)),
Self::Location(d) => Cow::Owned(serialize(d)),
Self::Notice(d) => Cow::Owned(serialize(d)),
Self::ServerNotice(d) => Cow::Owned(serialize(d)),
Self::Text(d) => Cow::Owned(serialize(d)),
Self::Video(d) => Cow::Owned(serialize(d)),
#[cfg(feature = "unstable-pre-spec")]
Self::VerificationRequest(d) => Cow::Owned(serialize(d)),
Self::_Custom(c) => Cow::Borrowed(&c.data),
}
}
}
impl From<MessageType> for MessageEventContent { impl From<MessageType> for MessageEventContent {
fn from(msgtype: MessageType) -> Self { fn from(msgtype: MessageType) -> Self {
Self::new(msgtype) Self::new(msgtype)
@ -606,5 +680,5 @@ pub struct CustomEventContent {
/// Remaining event content /// Remaining event content
#[serde(flatten)] #[serde(flatten)]
pub data: BTreeMap<String, JsonValue>, pub data: JsonObject,
} }

View File

@ -1,7 +1,6 @@
use std::time::{Duration, UNIX_EPOCH}; use std::time::{Duration, UNIX_EPOCH};
use assign::assign; use assign::assign;
use maplit::btreemap;
use matches::assert_matches; use matches::assert_matches;
#[cfg(feature = "unstable-pre-spec")] #[cfg(feature = "unstable-pre-spec")]
use ruma_events::{ use ruma_events::{
@ -23,6 +22,18 @@ use ruma_identifiers::{event_id, mxc_uri, room_id, user_id};
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};
macro_rules! json_object {
( $($key:expr => $value:expr),* $(,)? ) => {
{
let mut _map = serde_json::Map::<String, serde_json::Value>::new();
$(
let _ = _map.insert($key, $value);
)*
_map
}
};
}
#[test] #[test]
fn serialization() { fn serialization() {
let ev = MessageEvent { let ev = MessageEvent {
@ -78,7 +89,7 @@ fn content_serialization() {
#[test] #[test]
fn custom_content_serialization() { fn custom_content_serialization() {
let json_data = btreemap! { let json_data = json_object! {
"custom_field".into() => json!("baba"), "custom_field".into() => json!("baba"),
"another_one".into() => json!("abab"), "another_one".into() => json!("abab"),
}; };
@ -105,7 +116,7 @@ fn custom_content_deserialization() {
"another_one": "abab", "another_one": "abab",
}); });
let expected_json_data = btreemap! { let expected_json_data = json_object! {
"custom_field".into() => json!("baba"), "custom_field".into() => json!("baba"),
"another_one".into() => json!("abab"), "another_one".into() => json!("abab"),
}; };