events: Reintroduce MSC3488 fallback behavior in LocationMessageEventContent
This commit is contained in:
parent
e017e65277
commit
d0f11f0075
@ -86,6 +86,8 @@ use serde::{Deserialize, Serialize};
|
||||
|
||||
use super::room::message::Relation;
|
||||
|
||||
pub(super) mod historical_serde;
|
||||
|
||||
/// The payload for an extensible text message.
|
||||
///
|
||||
/// This is the new primary type introduced in [MSC1767] and should only be sent in rooms with a
|
||||
|
68
crates/ruma-common/src/events/message/historical_serde.rs
Normal file
68
crates/ruma-common/src/events/message/historical_serde.rs
Normal file
@ -0,0 +1,68 @@
|
||||
//! Serde for old versions of MSC1767 still used in some types ([spec]).
|
||||
//!
|
||||
//! [spec]: https://github.com/matrix-org/matrix-spec-proposals/blob/d6046d8402e7a3c7a4fcbc9da16ea9bad5968992/proposals/1767-extensible-events.md
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use super::{TextContentBlock, TextRepresentation};
|
||||
|
||||
#[derive(Default, Serialize, Deserialize)]
|
||||
pub(in crate::events) struct MessageContentBlockSerDeHelper {
|
||||
/// Plain text short form.
|
||||
#[serde(rename = "org.matrix.msc1767.text", skip_serializing_if = "Option::is_none")]
|
||||
text: Option<String>,
|
||||
|
||||
/// HTML short form.
|
||||
#[serde(rename = "org.matrix.msc1767.html", skip_serializing_if = "Option::is_none")]
|
||||
html: Option<String>,
|
||||
|
||||
/// Long form.
|
||||
#[serde(rename = "org.matrix.msc1767.message", skip_serializing_if = "Option::is_none")]
|
||||
message: Option<Vec<TextRepresentation>>,
|
||||
}
|
||||
|
||||
impl TryFrom<MessageContentBlockSerDeHelper> for TextContentBlock {
|
||||
type Error = &'static str;
|
||||
|
||||
fn try_from(value: MessageContentBlockSerDeHelper) -> Result<Self, Self::Error> {
|
||||
let MessageContentBlockSerDeHelper { text, html, message } = value;
|
||||
|
||||
if let Some(message) = message {
|
||||
Ok(Self(message))
|
||||
} else {
|
||||
let message: Vec<_> = html
|
||||
.map(TextRepresentation::html)
|
||||
.into_iter()
|
||||
.chain(text.map(TextRepresentation::plain))
|
||||
.collect();
|
||||
if !message.is_empty() {
|
||||
Ok(Self(message))
|
||||
} else {
|
||||
Err("missing at least one of fields `org.matrix.msc1767.text`, `org.matrix.msc1767.html` or `org.matrix.msc1767.message`")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<TextContentBlock> for MessageContentBlockSerDeHelper {
|
||||
fn from(value: TextContentBlock) -> Self {
|
||||
let has_shortcut =
|
||||
|message: &TextRepresentation| matches!(&*message.mimetype, "text/plain" | "text/html");
|
||||
|
||||
if value.iter().all(has_shortcut) {
|
||||
let mut helper = Self::default();
|
||||
|
||||
for message in value.0.into_iter() {
|
||||
if message.mimetype == "text/plain" {
|
||||
helper.text = Some(message.body);
|
||||
} else if message.mimetype == "text/html" {
|
||||
helper.html = Some(message.body);
|
||||
}
|
||||
}
|
||||
|
||||
helper
|
||||
} else {
|
||||
Self { message: Some(value.0), ..Default::default() }
|
||||
}
|
||||
}
|
||||
}
|
@ -49,3 +49,82 @@ impl<'de> Deserialize<'de> for MessageType {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "unstable-msc3488")]
|
||||
pub(in super::super) mod msc3488 {
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::{
|
||||
events::{
|
||||
location::{AssetContent, LocationContent},
|
||||
message::historical_serde::MessageContentBlockSerDeHelper,
|
||||
room::message::{LocationInfo, LocationMessageEventContent},
|
||||
},
|
||||
MilliSecondsSinceUnixEpoch,
|
||||
};
|
||||
|
||||
/// Deserialize helper type for `LocationMessageEventContent` with unstable fields from msc3488.
|
||||
#[derive(Serialize, Deserialize)]
|
||||
#[serde(tag = "msgtype", rename = "m.location")]
|
||||
pub(in super::super) struct LocationMessageEventContentSerDeHelper {
|
||||
pub body: String,
|
||||
|
||||
pub geo_uri: String,
|
||||
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub info: Option<Box<LocationInfo>>,
|
||||
|
||||
#[serde(flatten)]
|
||||
pub message: MessageContentBlockSerDeHelper,
|
||||
|
||||
#[serde(rename = "org.matrix.msc3488.location", skip_serializing_if = "Option::is_none")]
|
||||
pub location: Option<LocationContent>,
|
||||
|
||||
#[serde(rename = "org.matrix.msc3488.asset", skip_serializing_if = "Option::is_none")]
|
||||
pub asset: Option<AssetContent>,
|
||||
|
||||
#[serde(rename = "org.matrix.msc3488.ts", skip_serializing_if = "Option::is_none")]
|
||||
pub ts: Option<MilliSecondsSinceUnixEpoch>,
|
||||
}
|
||||
|
||||
impl From<LocationMessageEventContent> for LocationMessageEventContentSerDeHelper {
|
||||
fn from(value: LocationMessageEventContent) -> Self {
|
||||
let LocationMessageEventContent { body, geo_uri, info, message, location, asset, ts } =
|
||||
value;
|
||||
|
||||
Self {
|
||||
body,
|
||||
geo_uri,
|
||||
info,
|
||||
message: message.map(Into::into).unwrap_or_default(),
|
||||
location,
|
||||
asset,
|
||||
ts,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<LocationMessageEventContentSerDeHelper> for LocationMessageEventContent {
|
||||
fn from(value: LocationMessageEventContentSerDeHelper) -> Self {
|
||||
let LocationMessageEventContentSerDeHelper {
|
||||
body,
|
||||
geo_uri,
|
||||
info,
|
||||
message,
|
||||
location,
|
||||
asset,
|
||||
ts,
|
||||
} = value;
|
||||
|
||||
LocationMessageEventContent {
|
||||
body,
|
||||
geo_uri,
|
||||
info,
|
||||
message: message.try_into().ok(),
|
||||
location,
|
||||
asset,
|
||||
ts,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,11 +1,26 @@
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::events::room::{MediaSource, ThumbnailInfo};
|
||||
#[cfg(feature = "unstable-msc3488")]
|
||||
use crate::{
|
||||
events::{
|
||||
location::{AssetContent, AssetType, LocationContent},
|
||||
message::{TextContentBlock, TextRepresentation},
|
||||
},
|
||||
MilliSecondsSinceUnixEpoch,
|
||||
};
|
||||
|
||||
/// The payload for a location message.
|
||||
#[derive(Clone, Debug, Deserialize, Serialize)]
|
||||
#[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)]
|
||||
#[serde(tag = "msgtype", rename = "m.location")]
|
||||
#[cfg_attr(
|
||||
feature = "unstable-msc3488",
|
||||
serde(
|
||||
from = "super::content_serde::msc3488::LocationMessageEventContentSerDeHelper",
|
||||
into = "super::content_serde::msc3488::LocationMessageEventContentSerDeHelper"
|
||||
)
|
||||
)]
|
||||
pub struct LocationMessageEventContent {
|
||||
/// A description of the location e.g. "Big Ben, London, UK", or some kind of content
|
||||
/// description for accessibility, e.g. "location attachment".
|
||||
@ -17,12 +32,84 @@ pub struct LocationMessageEventContent {
|
||||
/// Info about the location being represented.
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub info: Option<Box<LocationInfo>>,
|
||||
|
||||
/// Extensible-event text representation of the message.
|
||||
///
|
||||
/// If present, this should be preferred over the `body` field.
|
||||
#[cfg(feature = "unstable-msc3488")]
|
||||
pub message: Option<TextContentBlock>,
|
||||
|
||||
/// Extensible-event location info of the message.
|
||||
///
|
||||
/// If present, this should be preferred over the `geo_uri` field.
|
||||
#[cfg(feature = "unstable-msc3488")]
|
||||
pub location: Option<LocationContent>,
|
||||
|
||||
/// Extensible-event asset this message refers to.
|
||||
#[cfg(feature = "unstable-msc3488")]
|
||||
pub asset: Option<AssetContent>,
|
||||
|
||||
/// Extensible-event timestamp this message refers to.
|
||||
#[cfg(feature = "unstable-msc3488")]
|
||||
pub ts: Option<MilliSecondsSinceUnixEpoch>,
|
||||
}
|
||||
|
||||
impl LocationMessageEventContent {
|
||||
/// Creates a new `LocationMessageEventContent` with the given body and geo URI.
|
||||
pub fn new(body: String, geo_uri: String) -> Self {
|
||||
Self { body, geo_uri, info: None }
|
||||
Self {
|
||||
#[cfg(feature = "unstable-msc3488")]
|
||||
message: Some(vec![TextRepresentation::plain(&body)].into()),
|
||||
#[cfg(feature = "unstable-msc3488")]
|
||||
location: Some(LocationContent::new(geo_uri.clone())),
|
||||
#[cfg(feature = "unstable-msc3488")]
|
||||
asset: Some(AssetContent::default()),
|
||||
#[cfg(feature = "unstable-msc3488")]
|
||||
ts: None,
|
||||
body,
|
||||
geo_uri,
|
||||
info: None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Set the asset type of this `LocationMessageEventContent`.
|
||||
#[cfg(feature = "unstable-msc3488")]
|
||||
pub fn with_asset_type(mut self, asset: AssetType) -> Self {
|
||||
self.asset = Some(AssetContent { type_: asset });
|
||||
self
|
||||
}
|
||||
|
||||
/// Set the timestamp of this `LocationMessageEventContent`.
|
||||
#[cfg(feature = "unstable-msc3488")]
|
||||
pub fn with_ts(mut self, ts: MilliSecondsSinceUnixEpoch) -> Self {
|
||||
self.ts = Some(ts);
|
||||
self
|
||||
}
|
||||
|
||||
/// Get the `geo:` URI of this `LocationMessageEventContent`.
|
||||
pub fn geo_uri(&self) -> &str {
|
||||
#[cfg(feature = "unstable-msc3488")]
|
||||
if let Some(uri) = self.location.as_ref().map(|l| &l.uri) {
|
||||
return uri;
|
||||
}
|
||||
|
||||
&self.geo_uri
|
||||
}
|
||||
|
||||
/// Get the plain text representation of this `LocationMessageEventContent`.
|
||||
pub fn plain_text_representation(&self) -> &str {
|
||||
#[cfg(feature = "unstable-msc3488")]
|
||||
if let Some(text) = self.message.as_ref().and_then(|m| m.find_plain()) {
|
||||
return text;
|
||||
}
|
||||
|
||||
&self.body
|
||||
}
|
||||
|
||||
/// Get the asset type of this `LocationMessageEventContent`.
|
||||
#[cfg(feature = "unstable-msc3488")]
|
||||
pub fn asset_type(&self) -> AssetType {
|
||||
self.asset.as_ref().map(|a| a.type_.clone()).unwrap_or_default()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -9,7 +9,9 @@ use ruma_common::{
|
||||
location::{AssetType, LocationContent, LocationEventContent, ZoomLevel, ZoomLevelError},
|
||||
message::TextContentBlock,
|
||||
relation::InReplyTo,
|
||||
room::message::Relation,
|
||||
room::message::{
|
||||
LocationMessageEventContent, MessageType, Relation, RoomMessageEventContent,
|
||||
},
|
||||
AnyMessageLikeEvent, MessageLikeEvent,
|
||||
},
|
||||
owned_event_id, room_id,
|
||||
@ -183,3 +185,54 @@ fn message_event_deserialization() {
|
||||
assert_eq!(ev.sender, user_id!("@user:notareal.hs"));
|
||||
assert!(ev.unsigned.is_empty());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn room_message_unstable_deserialization() {
|
||||
let json_data = json!({
|
||||
"body": "Alice was at geo:51.5008,0.1247;u=35",
|
||||
"geo_uri": "geo:51.5008,0.1247;u=35",
|
||||
"msgtype": "m.location",
|
||||
"org.matrix.msc1767.text": "Alice was at geo:51.5008,0.1247;u=35",
|
||||
"org.matrix.msc3488.location": {
|
||||
"uri": "geo:51.5008,0.1247;u=35",
|
||||
},
|
||||
"org.matrix.msc3488.asset": {
|
||||
"type": "m.self",
|
||||
},
|
||||
});
|
||||
|
||||
let event_content = from_json_value::<RoomMessageEventContent>(json_data).unwrap();
|
||||
assert_matches!(event_content.msgtype, MessageType::Location(content));
|
||||
|
||||
assert_eq!(content.body, "Alice was at geo:51.5008,0.1247;u=35");
|
||||
assert_eq!(content.geo_uri, "geo:51.5008,0.1247;u=35");
|
||||
let message = content.message.unwrap();
|
||||
assert_eq!(message.len(), 1);
|
||||
assert_eq!(message[0].body, "Alice was at geo:51.5008,0.1247;u=35");
|
||||
assert_eq!(content.location.unwrap().uri, "geo:51.5008,0.1247;u=35");
|
||||
assert_eq!(content.asset.unwrap().type_, AssetType::Self_);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn room_message_unstable_serialization() {
|
||||
let message_event_content =
|
||||
RoomMessageEventContent::new(MessageType::Location(LocationMessageEventContent::new(
|
||||
"Alice was at geo:51.5008,0.1247;u=35".to_owned(),
|
||||
"geo:51.5008,0.1247;u=35".to_owned(),
|
||||
)));
|
||||
assert_eq!(
|
||||
to_json_value(&message_event_content).unwrap(),
|
||||
json!({
|
||||
"body": "Alice was at geo:51.5008,0.1247;u=35",
|
||||
"geo_uri": "geo:51.5008,0.1247;u=35",
|
||||
"msgtype": "m.location",
|
||||
"org.matrix.msc1767.text": "Alice was at geo:51.5008,0.1247;u=35",
|
||||
"org.matrix.msc3488.location": {
|
||||
"uri": "geo:51.5008,0.1247;u=35",
|
||||
},
|
||||
"org.matrix.msc3488.asset": {
|
||||
"type": "m.self",
|
||||
},
|
||||
})
|
||||
);
|
||||
}
|
||||
|
@ -9,8 +9,8 @@ use ruma_common::{
|
||||
message::{
|
||||
AudioMessageEventContent, EmoteMessageEventContent, FileMessageEventContent,
|
||||
ForwardThread, ImageMessageEventContent, KeyVerificationRequestEventContent,
|
||||
LocationMessageEventContent, MessageType, OriginalRoomMessageEvent,
|
||||
RoomMessageEventContent, TextMessageEventContent, VideoMessageEventContent,
|
||||
MessageType, OriginalRoomMessageEvent, RoomMessageEventContent,
|
||||
TextMessageEventContent, VideoMessageEventContent,
|
||||
},
|
||||
EncryptedFileInit, JsonWebKeyInit, MediaSource,
|
||||
},
|
||||
@ -650,8 +650,11 @@ fn image_msgtype_deserialization() {
|
||||
assert_eq!(url, "mxc://notareal.hs/file");
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "unstable-msc3488"))]
|
||||
#[test]
|
||||
fn location_msgtype_serialization() {
|
||||
use ruma_common::events::room::message::LocationMessageEventContent;
|
||||
|
||||
let message_event_content =
|
||||
RoomMessageEventContent::new(MessageType::Location(LocationMessageEventContent::new(
|
||||
"Alice was at geo:51.5008,0.1247;u=35".to_owned(),
|
||||
|
Loading…
x
Reference in New Issue
Block a user