events: Replace Captions with MessageContent
Use a custom serde implementation
This commit is contained in:
		
							parent
							
								
									93b4114a82
								
							
						
					
					
						commit
						245bf75276
					
				| @ -8,7 +8,7 @@ use serde::{Deserialize, Serialize}; | ||||
| 
 | ||||
| use super::{ | ||||
|     file::{EncryptedContent, FileContent}, | ||||
|     message::{MessageContent, Text}, | ||||
|     message::MessageContent, | ||||
|     room::message::Relation, | ||||
| }; | ||||
| use crate::MxcUri; | ||||
| @ -35,8 +35,13 @@ pub struct ImageEventContent { | ||||
|     pub thumbnail: Vec<ThumbnailContent>, | ||||
| 
 | ||||
|     /// The captions of the message.
 | ||||
|     #[serde(rename = "m.caption", default, skip_serializing_if = "Captions::is_empty")] | ||||
|     pub caption: Captions, | ||||
|     #[serde(
 | ||||
|         rename = "m.caption", | ||||
|         with = "super::message::content_serde::as_vec", | ||||
|         default, | ||||
|         skip_serializing_if = "Option::is_none" | ||||
|     )] | ||||
|     pub caption: Option<MessageContent>, | ||||
| 
 | ||||
|     /// Information about related messages for [rich replies].
 | ||||
|     ///
 | ||||
| @ -151,71 +156,6 @@ impl ThumbnailContent { | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| /// An array of captions.
 | ||||
| #[derive(Clone, Debug, Default, Serialize, Deserialize)] | ||||
| pub struct Captions(pub(crate) Vec<Text>); | ||||
| 
 | ||||
| impl Captions { | ||||
|     /// Creates a new `Captions` with the given captions.
 | ||||
|     ///
 | ||||
|     /// The captions must be ordered by most preferred first.
 | ||||
|     pub fn new(captions: &[Text]) -> Self { | ||||
|         Self(captions.to_owned()) | ||||
|     } | ||||
| 
 | ||||
|     /// A convenience constructor to create a plain text caption.
 | ||||
|     pub fn plain(body: impl Into<String>) -> Self { | ||||
|         Self(vec![Text::plain(body)]) | ||||
|     } | ||||
| 
 | ||||
|     /// A convenience constructor to create an HTML caption.
 | ||||
|     pub fn html(body: impl Into<String>, html_body: impl Into<String>) -> Self { | ||||
|         Self(vec![Text::html(html_body), Text::plain(body)]) | ||||
|     } | ||||
| 
 | ||||
|     /// A convenience constructor to create a Markdown caption.
 | ||||
|     ///
 | ||||
|     /// Returns an HTML caption if some Markdown formatting was detected, otherwise returns a plain
 | ||||
|     /// text caption.
 | ||||
|     #[cfg(feature = "markdown")] | ||||
|     pub fn markdown(body: impl AsRef<str> + Into<String>) -> Self { | ||||
|         let mut message = Vec::with_capacity(2); | ||||
|         if let Some(html_body) = Text::markdown(&body) { | ||||
|             message.push(html_body); | ||||
|         } | ||||
|         message.push(Text::plain(body)); | ||||
|         Self(message) | ||||
|     } | ||||
| 
 | ||||
|     /// Get the captions.
 | ||||
|     ///
 | ||||
|     /// The captions are ordered by most preferred first.
 | ||||
|     pub fn captions(&self) -> &[Text] { | ||||
|         &self.0 | ||||
|     } | ||||
| 
 | ||||
|     /// Whether this is empty.
 | ||||
|     pub fn is_empty(&self) -> bool { | ||||
|         self.0.is_empty() | ||||
|     } | ||||
| 
 | ||||
|     /// Get the plain text representation of this caption.
 | ||||
|     pub fn find_plain(&self) -> Option<&str> { | ||||
|         self.captions() | ||||
|             .iter() | ||||
|             .find(|content| content.mimetype == "text/plain") | ||||
|             .map(|content| content.body.as_ref()) | ||||
|     } | ||||
| 
 | ||||
|     /// Get the HTML representation of this caption.
 | ||||
|     pub fn find_html(&self) -> Option<&str> { | ||||
|         self.captions() | ||||
|             .iter() | ||||
|             .find(|content| content.mimetype == "text/html") | ||||
|             .map(|content| content.body.as_ref()) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| /// Image content.
 | ||||
| #[derive(Default, Clone, Debug, Serialize, Deserialize)] | ||||
| #[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)] | ||||
|  | ||||
| @ -52,7 +52,7 @@ use std::ops::Deref; | ||||
| use ruma_macros::EventContent; | ||||
| use serde::{Deserialize, Serialize}; | ||||
| 
 | ||||
| mod content_serde; | ||||
| pub(crate) mod content_serde; | ||||
| 
 | ||||
| use content_serde::MessageContentSerDeHelper; | ||||
| 
 | ||||
|  | ||||
| @ -67,3 +67,34 @@ impl Serialize for MessageContent { | ||||
|         st.end() | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| pub(crate) mod as_vec { | ||||
|     use serde::{ser::SerializeSeq, Deserialize, Deserializer, Serializer}; | ||||
| 
 | ||||
|     use crate::events::message::{MessageContent, Text}; | ||||
| 
 | ||||
|     /// Serializes a `Option<MessageContent>` as a `Vec<Text>`.
 | ||||
|     pub fn serialize<S>(content: &Option<MessageContent>, serializer: S) -> Result<S::Ok, S::Error> | ||||
|     where | ||||
|         S: Serializer, | ||||
|     { | ||||
|         if let Some(content) = content { | ||||
|             let mut seq = serializer.serialize_seq(Some(content.len()))?; | ||||
|             for e in content.iter() { | ||||
|                 seq.serialize_element(e)?; | ||||
|             } | ||||
|             seq.end() | ||||
|         } else { | ||||
|             serializer.serialize_seq(Some(0))?.end() | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     /// Deserializes a `Vec<Text>` to an `Option<MessageContent>`.
 | ||||
|     pub fn deserialize<'de, D>(deserializer: D) -> Result<Option<MessageContent>, D::Error> | ||||
|     where | ||||
|         D: Deserializer<'de>, | ||||
|     { | ||||
|         Option::<Vec<Text>>::deserialize(deserializer) | ||||
|             .map(|content| content.filter(|content| !content.is_empty()).map(Into::into)) | ||||
|     } | ||||
| } | ||||
|  | ||||
| @ -9,10 +9,7 @@ use ruma_macros::EventContent; | ||||
| use serde::{Deserialize, Serialize}; | ||||
| 
 | ||||
| use super::{ | ||||
|     file::FileContent, | ||||
|     image::{Captions, ThumbnailContent}, | ||||
|     message::MessageContent, | ||||
|     room::message::Relation, | ||||
|     file::FileContent, image::ThumbnailContent, message::MessageContent, room::message::Relation, | ||||
| }; | ||||
| 
 | ||||
| /// The payload for an extensible video message.
 | ||||
| @ -37,8 +34,13 @@ pub struct VideoEventContent { | ||||
|     pub thumbnail: Vec<ThumbnailContent>, | ||||
| 
 | ||||
|     /// The captions of the message.
 | ||||
|     #[serde(rename = "m.caption", default, skip_serializing_if = "Captions::is_empty")] | ||||
|     pub caption: Captions, | ||||
|     #[serde(
 | ||||
|         rename = "m.caption", | ||||
|         with = "super::message::content_serde::as_vec", | ||||
|         default, | ||||
|         skip_serializing_if = "Option::is_none" | ||||
|     )] | ||||
|     pub caption: Option<MessageContent>, | ||||
| 
 | ||||
|     /// Information about related messages.
 | ||||
|     #[serde(flatten, skip_serializing_if = "Option::is_none")] | ||||
|  | ||||
| @ -8,7 +8,7 @@ use ruma_common::{ | ||||
|     events::{ | ||||
|         file::{EncryptedContentInit, FileContent, FileContentInfo}, | ||||
|         image::{ | ||||
|             Captions, ImageContent, ImageEventContent, ThumbnailContent, ThumbnailFileContent, | ||||
|             ImageContent, ImageEventContent, ThumbnailContent, ThumbnailFileContent, | ||||
|             ThumbnailFileContentInfo, | ||||
|         }, | ||||
|         message::MessageContent, | ||||
| @ -128,7 +128,7 @@ fn image_event_serialization() { | ||||
|                     ), | ||||
|                     None | ||||
|                 )], | ||||
|                 caption: Captions::plain("This is my house"), | ||||
|                 caption: Some(MessageContent::plain("This is my house")), | ||||
|                 relates_to: Some(Relation::Reply { | ||||
|                     in_reply_to: InReplyTo::new(event_id!("$replyevent:example.com").to_owned()), | ||||
|                 }), | ||||
| @ -207,7 +207,7 @@ fn plain_content_deserialization() { | ||||
|     assert_matches!( | ||||
|         from_json_value::<ImageEventContent>(json_data) | ||||
|             .unwrap(), | ||||
|         ImageEventContent { message, file, image, thumbnail, caption, .. } | ||||
|         ImageEventContent { message, file, image, thumbnail, caption: Some(caption), .. } | ||||
|         if message.find_plain() == Some("Upload: my_cat.png") | ||||
|             && message.find_html().is_none() | ||||
|             && file.url == "mxc://notareal.hs/abcdef" | ||||
| @ -248,7 +248,7 @@ fn encrypted_content_deserialization() { | ||||
|     assert_matches!( | ||||
|         from_json_value::<ImageEventContent>(json_data) | ||||
|             .unwrap(), | ||||
|         ImageEventContent { message, file, image, thumbnail, caption, .. } | ||||
|         ImageEventContent { message, file, image, thumbnail, caption: None, .. } | ||||
|         if message.find_plain() == Some("Upload: my_file.txt") | ||||
|             && message.find_html().is_none() | ||||
|             && file.url == "mxc://notareal.hs/abcdef" | ||||
| @ -256,7 +256,6 @@ fn encrypted_content_deserialization() { | ||||
|             && image.width.is_none() | ||||
|             && image.height.is_none() | ||||
|             && thumbnail[0].file.url == "mxc://notareal.hs/thumbnail" | ||||
|             && caption.is_empty() | ||||
|     ); | ||||
| } | ||||
| 
 | ||||
| @ -295,7 +294,7 @@ fn message_event_deserialization() { | ||||
|                 }, | ||||
|                 image, | ||||
|                 thumbnail, | ||||
|                 caption, | ||||
|                 caption: None, | ||||
|                 .. | ||||
|             }, | ||||
|             event_id, | ||||
| @ -313,7 +312,6 @@ fn message_event_deserialization() { | ||||
|             && image.width == Some(uint!(1300)) | ||||
|             && image.height == Some(uint!(837)) | ||||
|             && thumbnail.is_empty() | ||||
|             && caption.is_empty() | ||||
|             && origin_server_ts == MilliSecondsSinceUnixEpoch(uint!(134_829_848)) | ||||
|             && room_id == room_id!("!roomid:notareal.hs") | ||||
|             && sender == user_id!("@user:notareal.hs") | ||||
|  | ||||
| @ -9,7 +9,7 @@ use ruma_common::{ | ||||
|     event_id, | ||||
|     events::{ | ||||
|         file::{EncryptedContentInit, FileContent, FileContentInfo}, | ||||
|         image::{Captions, ThumbnailContent, ThumbnailFileContent, ThumbnailFileContentInfo}, | ||||
|         image::{ThumbnailContent, ThumbnailFileContent, ThumbnailFileContentInfo}, | ||||
|         message::MessageContent, | ||||
|         room::{ | ||||
|             message::{InReplyTo, Relation}, | ||||
| @ -135,7 +135,7 @@ fn event_serialization() { | ||||
|                     ), | ||||
|                     None | ||||
|                 )], | ||||
|                 caption: Captions::plain("This is my awesome vintage lava lamp"), | ||||
|                 caption: Some(MessageContent::plain("This is my awesome vintage lava lamp")), | ||||
|                 relates_to: Some(Relation::Reply { | ||||
|                     in_reply_to: InReplyTo::new(event_id!("$replyevent:example.com").to_owned()), | ||||
|                 }), | ||||
| @ -215,7 +215,7 @@ fn plain_content_deserialization() { | ||||
|     assert_matches!( | ||||
|         from_json_value::<VideoEventContent>(json_data) | ||||
|             .unwrap(), | ||||
|         VideoEventContent { message, file, video, thumbnail, caption, .. } | ||||
|         VideoEventContent { message, file, video, thumbnail, caption: Some(caption), .. } | ||||
|         if message.find_plain() == Some("Video: my_cat.mp4") | ||||
|             && message.find_html().is_none() | ||||
|             && file.url == "mxc://notareal.hs/abcdef" | ||||
| @ -257,7 +257,7 @@ fn encrypted_content_deserialization() { | ||||
|     assert_matches!( | ||||
|         from_json_value::<VideoEventContent>(json_data) | ||||
|             .unwrap(), | ||||
|         VideoEventContent { message, file, video, thumbnail, caption, .. } | ||||
|         VideoEventContent { message, file, video, thumbnail, caption: None, .. } | ||||
|         if message.find_plain() == Some("Upload: my_cat.mp4") | ||||
|             && message.find_html().is_none() | ||||
|             && file.url == "mxc://notareal.hs/abcdef" | ||||
| @ -266,7 +266,6 @@ fn encrypted_content_deserialization() { | ||||
|             && video.height.is_none() | ||||
|             && video.duration.is_none() | ||||
|             && thumbnail[0].file.url == "mxc://notareal.hs/thumbnail" | ||||
|             && caption.is_empty() | ||||
|     ); | ||||
| } | ||||
| 
 | ||||
| @ -305,7 +304,7 @@ fn message_event_deserialization() { | ||||
|                 }, | ||||
|                 video, | ||||
|                 thumbnail, | ||||
|                 caption, | ||||
|                 caption: None, | ||||
|                 .. | ||||
|             }, | ||||
|             event_id, | ||||
| @ -324,7 +323,6 @@ fn message_event_deserialization() { | ||||
|             && video.height == Some(uint!(837)) | ||||
|             && video.duration.is_none() | ||||
|             && thumbnail.is_empty() | ||||
|             && caption.is_empty() | ||||
|             && origin_server_ts == MilliSecondsSinceUnixEpoch(uint!(134_829_848)) | ||||
|             && room_id == room_id!("!roomid:notareal.hs") | ||||
|             && sender == user_id!("@user:notareal.hs") | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user