diff --git a/crates/ruma-common/src/events/message/content_serde.rs b/crates/ruma-common/src/events/message/content_serde.rs index a5ee6d44..9a57da59 100644 --- a/crates/ruma-common/src/events/message/content_serde.rs +++ b/crates/ruma-common/src/events/message/content_serde.rs @@ -14,6 +14,14 @@ pub(crate) struct MessageContentSerDeHelper { #[serde(rename = "org.matrix.msc1767.text")] text_unstable: Option, + /// HTML short form, stable name. + #[serde(rename = "m.html")] + html_stable: Option, + + /// HTML short form, unstable name. + #[serde(rename = "org.matrix.msc1767.html")] + html_unstable: Option, + /// Long form, stable name. #[serde(rename = "m.message")] message_stable: Option>, @@ -30,16 +38,26 @@ impl TryFrom for MessageContent { let MessageContentSerDeHelper { text_stable, text_unstable, + html_stable, + html_unstable, message_stable, message_unstable, } = helper; if let Some(message) = message_stable.or(message_unstable) { Ok(Self(message)) - } else if let Some(text) = text_stable.or(text_unstable) { - Ok(Self::plain(text)) } else { - Err(TryFromExtensibleError::MissingField("m.message or m.text".to_owned())) + let message: Vec<_> = html_stable + .or(html_unstable) + .map(Text::html) + .into_iter() + .chain(text_stable.or(text_unstable).map(Text::plain)) + .collect(); + if !message.is_empty() { + Ok(Self(message)) + } else { + Err(TryFromExtensibleError::MissingField("m.message, m.text or m.html".to_owned())) + } } } } @@ -49,13 +67,30 @@ impl Serialize for MessageContent { where S: serde::Serializer, { - let mut st = serializer.serialize_struct("MessageContent", 1)?; - if self.len() == 1 && self[0].mimetype == "text/plain" { - st.serialize_field("org.matrix.msc1767.text", &self[0].body)?; + #[cfg(feature = "unstable-msc3554")] + let has_shortcut = |message: &Text| { + matches!(&*message.mimetype, "text/plain" | "text/html") && message.lang.is_none() + }; + + #[cfg(not(feature = "unstable-msc3554"))] + let has_shortcut = + |message: &Text| matches!(&*message.mimetype, "text/plain" | "text/html"); + + if self.iter().all(has_shortcut) { + let mut st = serializer.serialize_struct("MessageContent", self.len())?; + for message in self.iter() { + if message.mimetype == "text/plain" { + st.serialize_field("org.matrix.msc1767.text", &message.body)?; + } else if message.mimetype == "text/html" { + st.serialize_field("org.matrix.msc1767.html", &message.body)?; + } + } + st.end() } else { + let mut st = serializer.serialize_struct("MessageContent", 1)?; st.serialize_field("org.matrix.msc1767.message", &self.0)?; + st.end() } - st.end() } } diff --git a/crates/ruma-common/tests/events/audio.rs b/crates/ruma-common/tests/events/audio.rs index 401d5a65..24b3c035 100644 --- a/crates/ruma-common/tests/events/audio.rs +++ b/crates/ruma-common/tests/events/audio.rs @@ -172,10 +172,8 @@ fn event_serialization() { to_json_value(&event).unwrap(), json!({ "content": { - "org.matrix.msc1767.message": [ - { "body": "Upload: my_mix.mp3", "mimetype": "text/html"}, - { "body": "Upload: my_mix.mp3", "mimetype": "text/plain"}, - ], + "org.matrix.msc1767.html": "Upload: my_mix.mp3", + "org.matrix.msc1767.text": "Upload: my_mix.mp3", "m.file": { "url": "mxc://notareal.hs/abcdef", "name": "my_mix.mp3", diff --git a/crates/ruma-common/tests/events/file.rs b/crates/ruma-common/tests/events/file.rs index 0cdb3477..e55cf818 100644 --- a/crates/ruma-common/tests/events/file.rs +++ b/crates/ruma-common/tests/events/file.rs @@ -126,10 +126,8 @@ fn file_event_serialization() { to_json_value(&event).unwrap(), json!({ "content": { - "org.matrix.msc1767.message": [ - { "body": "Upload: my_file.txt", "mimetype": "text/html"}, - { "body": "Upload: my_file.txt", "mimetype": "text/plain"}, - ], + "org.matrix.msc1767.html": "Upload: my_file.txt", + "org.matrix.msc1767.text": "Upload: my_file.txt", "m.file": { "url": "mxc://notareal.hs/abcdef", "name": "my_file.txt", diff --git a/crates/ruma-common/tests/events/image.rs b/crates/ruma-common/tests/events/image.rs index 1c633b78..b5a5b01f 100644 --- a/crates/ruma-common/tests/events/image.rs +++ b/crates/ruma-common/tests/events/image.rs @@ -147,10 +147,8 @@ fn image_event_serialization() { to_json_value(&event).unwrap(), json!({ "content": { - "org.matrix.msc1767.message": [ - { "body": "Upload: my_house.jpg", "mimetype": "text/html"}, - { "body": "Upload: my_house.jpg", "mimetype": "text/plain"}, - ], + "org.matrix.msc1767.html": "Upload: my_house.jpg", + "org.matrix.msc1767.text": "Upload: my_house.jpg", "m.file": { "url": "mxc://notareal.hs/abcdef", "name": "my_house.jpg", diff --git a/crates/ruma-common/tests/events/location.rs b/crates/ruma-common/tests/events/location.rs index b64cd16c..fd6677a7 100644 --- a/crates/ruma-common/tests/events/location.rs +++ b/crates/ruma-common/tests/events/location.rs @@ -70,16 +70,8 @@ fn event_serialization() { to_json_value(&event).unwrap(), json!({ "content": { - "org.matrix.msc1767.message": [ - { - "body": "Alice was at geo:51.5008,0.1247;u=35 as of Sat Nov 13 18:50:58 2021", - "mimetype": "text/html", - }, - { - "body": "Alice was at geo:51.5008,0.1247;u=35 as of Sat Nov 13 18:50:58 2021", - "mimetype": "text/plain", - }, - ], + "org.matrix.msc1767.html": "Alice was at geo:51.5008,0.1247;u=35 as of Sat Nov 13 18:50:58 2021", + "org.matrix.msc1767.text": "Alice was at geo:51.5008,0.1247;u=35 as of Sat Nov 13 18:50:58 2021", "m.location": { "uri": "geo:51.5008,0.1247;u=35", "description": "Alice's whereabouts", diff --git a/crates/ruma-common/tests/events/message.rs b/crates/ruma-common/tests/events/message.rs index 16e2429e..c1a76160 100644 --- a/crates/ruma-common/tests/events/message.rs +++ b/crates/ruma-common/tests/events/message.rs @@ -37,10 +37,8 @@ fn html_content_serialization() { assert_eq!( to_json_value(&message_event_content).unwrap(), json!({ - "org.matrix.msc1767.message": [ - { "body": "Hello, World!", "mimetype": "text/html"}, - { "body": "Hello, World!", "mimetype": "text/plain"}, - ] + "org.matrix.msc1767.html": "Hello, World!", + "org.matrix.msc1767.text": "Hello, World!", }) ); } @@ -96,10 +94,8 @@ fn markdown_content_serialization() { assert_eq!( to_json_value(&formatted_message).unwrap(), json!({ - "org.matrix.msc1767.message": [ - { "body": "

Testing bold and italic!

\n", "mimetype": "text/html"}, - { "body": "Testing **bold** and _italic_!", "mimetype": "text/plain"}, - ] + "org.matrix.msc1767.html": "

Testing bold and italic!

\n", + "org.matrix.msc1767.text": "Testing **bold** and _italic_!", }) ); @@ -118,10 +114,8 @@ fn markdown_content_serialization() { assert_eq!( to_json_value(&plain_message_paragraphs).unwrap(), json!({ - "org.matrix.msc1767.message": [ - { "body": "

Testing

\n

Several

\n

Paragraphs.

\n", "mimetype": "text/html"}, - { "body": "Testing\n\nSeveral\n\nParagraphs.", "mimetype": "text/plain"}, - ] + "org.matrix.msc1767.html": "

Testing

\n

Several

\n

Paragraphs.

\n", + "org.matrix.msc1767.text": "Testing\n\nSeveral\n\nParagraphs.", }) ); } @@ -198,7 +192,53 @@ fn plain_text_content_stable_deserialization() { } #[test] -fn html_text_content_unstable_deserialization() { +fn html_content_unstable_deserialization() { + let json_data = json!({ + "org.matrix.msc1767.html": "Hello, New World!", + }); + + let content = from_json_value::(json_data).unwrap(); + assert_eq!(content.message.find_plain(), None); + assert_eq!(content.message.find_html(), Some("Hello, New World!")); +} + +#[test] +fn html_content_stable_deserialization() { + let json_data = json!({ + "m.html": "Hello, New World!", + }); + + let content = from_json_value::(json_data).unwrap(); + assert_eq!(content.message.find_plain(), None); + assert_eq!(content.message.find_html(), Some("Hello, New World!")); +} + +#[test] +fn html_and_text_content_unstable_deserialization() { + let json_data = json!({ + "org.matrix.msc1767.html": "Hello, New World!", + "org.matrix.msc1767.text": "Hello, New World!", + }); + + let content = from_json_value::(json_data).unwrap(); + assert_eq!(content.message.find_plain(), Some("Hello, New World!")); + assert_eq!(content.message.find_html(), Some("Hello, New World!")); +} + +#[test] +fn html_and_text_content_stable_deserialization() { + let json_data = json!({ + "m.html": "Hello, New World!", + "m.text": "Hello, New World!", + }); + + let content = from_json_value::(json_data).unwrap(); + assert_eq!(content.message.find_plain(), Some("Hello, New World!")); + assert_eq!(content.message.find_html(), Some("Hello, New World!")); +} + +#[test] +fn message_content_unstable_deserialization() { let json_data = json!({ "org.matrix.msc1767.message": [ { "body": "Hello, New World!", "mimetype": "text/html"}, @@ -212,7 +252,7 @@ fn html_text_content_unstable_deserialization() { } #[test] -fn html_text_content_stable_deserialization() { +fn message_content_stable_deserialization() { let json_data = json!({ "m.message": [ { "body": "Hello, New World!", "mimetype": "text/html"}, @@ -315,7 +355,61 @@ fn room_message_plain_text_unstable_deserialization() { } #[test] -fn room_message_html_text_stable_deserialization() { +fn room_message_html_and_text_stable_deserialization() { + let json_data = json!({ + "body": "test", + "formatted_body": "

test

", + "format": "org.matrix.custom.html", + "msgtype": "m.text", + "m.html": "

test

", + "m.text": "test", + }); + + let content = assert_matches!( + from_json_value::(json_data), + Ok(RoomMessageEventContent { + msgtype: MessageType::Text(content), + .. + }) => content + ); + assert_eq!(content.body, "test"); + let formatted = content.formatted.unwrap(); + assert_eq!(formatted.body, "

test

"); + let message = content.message.unwrap(); + assert_eq!(message.len(), 2); + assert_eq!(message[0].body, "

test

"); + assert_eq!(message[1].body, "test"); +} + +#[test] +fn room_message_html_and_text_unstable_deserialization() { + let json_data = json!({ + "body": "test", + "formatted_body": "

test

", + "format": "org.matrix.custom.html", + "msgtype": "m.text", + "org.matrix.msc1767.html": "

test

", + "org.matrix.msc1767.text": "test", + }); + + let content = assert_matches!( + from_json_value::(json_data), + Ok(RoomMessageEventContent { + msgtype: MessageType::Text(content), + .. + }) => content + ); + assert_eq!(content.body, "test"); + let formatted = content.formatted.unwrap(); + assert_eq!(formatted.body, "

test

"); + let message = content.message.unwrap(); + assert_eq!(message.len(), 2); + assert_eq!(message[0].body, "

test

"); + assert_eq!(message[1].body, "test"); +} + +#[test] +fn room_message_message_stable_deserialization() { let json_data = json!({ "body": "test", "formatted_body": "

test

", @@ -344,7 +438,7 @@ fn room_message_html_text_stable_deserialization() { } #[test] -fn room_message_html_text_unstable_deserialization() { +fn room_message_message_unstable_deserialization() { let json_data = json!({ "body": "test", "formatted_body": "

test

", @@ -537,10 +631,8 @@ fn emote_event_serialization() { to_json_value(&event).unwrap(), json!({ "content": { - "org.matrix.msc1767.message": [ - { "body": "is testing some code…", "mimetype": "text/html" }, - { "body": "is testing some code…", "mimetype": "text/plain" }, - ] + "org.matrix.msc1767.html": "is testing some code…", + "org.matrix.msc1767.text": "is testing some code…", }, "event_id": "$event:notareal.hs", "origin_server_ts": 134_829_848, diff --git a/crates/ruma-common/tests/events/room_message.rs b/crates/ruma-common/tests/events/room_message.rs index 9e5f9c6c..c1364cae 100644 --- a/crates/ruma-common/tests/events/room_message.rs +++ b/crates/ruma-common/tests/events/room_message.rs @@ -192,10 +192,8 @@ fn formatted_body_serialization() { "msgtype": "m.text", "format": "org.matrix.custom.html", "formatted_body": "Hello, World!", - "org.matrix.msc1767.message": [ - { "body": "Hello, World!", "mimetype": "text/html" }, - { "body": "Hello, World!", "mimetype": "text/plain" }, - ], + "org.matrix.msc1767.html": "Hello, World!", + "org.matrix.msc1767.text": "Hello, World!", }) ); } @@ -253,10 +251,8 @@ fn markdown_content_serialization() { "formatted_body": "

Testing bold and italic!

\n", "format": "org.matrix.custom.html", "msgtype": "m.text", - "org.matrix.msc1767.message": [ - { "body": "

Testing bold and italic!

\n", "mimetype": "text/html" }, - { "body": "Testing **bold** and _italic_!", "mimetype": "text/plain" }, - ], + "org.matrix.msc1767.html": "

Testing bold and italic!

\n", + "org.matrix.msc1767.text": "Testing **bold** and _italic_!", }) ); @@ -306,10 +302,8 @@ fn markdown_content_serialization() { "formatted_body": "

Testing

\n

Several

\n

Paragraphs.

\n", "format": "org.matrix.custom.html", "msgtype": "m.text", - "org.matrix.msc1767.message": [ - { "body": "

Testing

\n

Several

\n

Paragraphs.

\n", "mimetype": "text/html" }, - { "body": "Testing\n\nSeveral\n\nParagraphs.", "mimetype": "text/plain" }, - ], + "org.matrix.msc1767.html": "

Testing

\n

Several

\n

Paragraphs.

\n", + "org.matrix.msc1767.text": "Testing\n\nSeveral\n\nParagraphs.", }) ); } diff --git a/crates/ruma-common/tests/events/video.rs b/crates/ruma-common/tests/events/video.rs index ce7bca44..b266f9a8 100644 --- a/crates/ruma-common/tests/events/video.rs +++ b/crates/ruma-common/tests/events/video.rs @@ -154,10 +154,8 @@ fn event_serialization() { to_json_value(&event).unwrap(), json!({ "content": { - "org.matrix.msc1767.message": [ - { "body": "Upload: my_lava_lamp.webm", "mimetype": "text/html"}, - { "body": "Upload: my_lava_lamp.webm", "mimetype": "text/plain"}, - ], + "org.matrix.msc1767.html": "Upload: my_lava_lamp.webm", + "org.matrix.msc1767.text": "Upload: my_lava_lamp.webm", "m.file": { "url": "mxc://notareal.hs/abcdef", "name": "my_lava_lamp.webm",