use serde::{de::Error, Deserialize, Deserializer, Serialize, Serializer};
use serde_json::{from_value, Value};
use super::{ImageInfo, ThumbnailInfo};
room_event! {
pub struct MessageEvent(MessageEventContent) {}
}
#[derive(Clone, Copy, Debug, Deserialize, PartialEq, Serialize)]
pub enum MessageType {
#[serde(rename = "m.audio")]
Audio,
#[serde(rename = "m.emote")]
Emote,
#[serde(rename = "m.file")]
File,
#[serde(rename = "m.image")]
Image,
#[serde(rename = "m.location")]
Location,
#[serde(rename = "m.notice")]
Notice,
#[serde(rename = "m.text")]
Text,
#[serde(rename = "m.video")]
Video,
}
#[derive(Clone, Debug, PartialEq)]
pub enum MessageEventContent {
Audio(AudioMessageEventContent),
Emote(EmoteMessageEventContent),
File(FileMessageEventContent),
Image(ImageMessageEventContent),
Location(LocationMessageEventContent),
Notice(NoticeMessageEventContent),
Text(TextMessageEventContent),
Video(VideoMessageEventContent),
}
#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)]
pub struct AudioMessageEventContent {
pub body: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub info: Option<AudioInfo>,
pub msgtype: MessageType,
pub url: String,
}
#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)]
pub struct AudioInfo {
#[serde(skip_serializing_if = "Option::is_none")]
pub duration: Option<u64>,
#[serde(skip_serializing_if = "Option::is_none")]
pub mimetype: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub size: Option<u64>,
}
#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)]
pub struct EmoteMessageEventContent {
pub body: String,
pub msgtype: MessageType,
}
#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)]
pub struct FileMessageEventContent {
pub body: String,
pub filename: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub info: Option<FileInfo>,
pub msgtype: MessageType,
pub url: String,
}
#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)]
pub struct FileInfo {
pub mimetype: String,
pub size: u64,
#[serde(skip_serializing_if = "Option::is_none")]
pub thumbnail_info: Option<ThumbnailInfo>,
#[serde(skip_serializing_if = "Option::is_none")]
pub thumbnail_url: Option<String>,
}
#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)]
pub struct ImageMessageEventContent {
pub body: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub info: Option<ImageInfo>,
pub msgtype: MessageType,
pub url: String,
}
#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)]
pub struct LocationMessageEventContent {
pub body: String,
pub geo_uri: String,
pub msgtype: MessageType,
#[serde(skip_serializing_if = "Option::is_none")]
pub info: Option<LocationInfo>,
}
#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)]
pub struct LocationInfo {
#[serde(skip_serializing_if = "Option::is_none")]
pub thumbnail_info: Option<ThumbnailInfo>,
#[serde(skip_serializing_if = "Option::is_none")]
pub thumbnail_url: Option<String>,
}
#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)]
pub struct NoticeMessageEventContent {
pub body: String,
pub msgtype: MessageType,
}
#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)]
pub struct TextMessageEventContent {
pub body: String,
pub msgtype: MessageType,
}
#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)]
pub struct VideoMessageEventContent {
pub body: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub info: Option<VideoInfo>,
pub msgtype: MessageType,
pub url: String,
}
#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)]
pub struct VideoInfo {
#[serde(skip_serializing_if = "Option::is_none")]
pub duration: Option<u64>,
#[serde(rename = "h")]
#[serde(skip_serializing_if = "Option::is_none")]
pub height: Option<u64>,
#[serde(skip_serializing_if = "Option::is_none")]
pub mimetype: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub size: Option<u64>,
#[serde(skip_serializing_if = "Option::is_none")]
pub thumbnail_info: Option<ThumbnailInfo>,
#[serde(skip_serializing_if = "Option::is_none")]
pub thumbnail_url: Option<String>,
#[serde(rename = "w")]
#[serde(skip_serializing_if = "Option::is_none")]
pub width: Option<u64>,
}
impl_enum! {
MessageType {
Audio => "m.audio",
Emote => "m.emote",
File => "m.file",
Image => "m.image",
Location => "m.location",
Notice => "m.notice",
Text => "m.text",
Video => "m.video",
}
}
impl Serialize for MessageEventContent {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
match *self {
MessageEventContent::Audio(ref content) => content.serialize(serializer),
MessageEventContent::Emote(ref content) => content.serialize(serializer),
MessageEventContent::File(ref content) => content.serialize(serializer),
MessageEventContent::Image(ref content) => content.serialize(serializer),
MessageEventContent::Location(ref content) => content.serialize(serializer),
MessageEventContent::Notice(ref content) => content.serialize(serializer),
MessageEventContent::Text(ref content) => content.serialize(serializer),
MessageEventContent::Video(ref content) => content.serialize(serializer),
}
}
}
impl<'de> Deserialize<'de> for MessageEventContent {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
let value: Value = Deserialize::deserialize(deserializer)?;
let message_type_value = match value.get("msgtype") {
Some(value) => value.clone(),
None => return Err(D::Error::missing_field("msgtype")),
};
let message_type = match from_value::<MessageType>(message_type_value.clone()) {
Ok(message_type) => message_type,
Err(error) => return Err(D::Error::custom(error.to_string())),
};
match message_type {
MessageType::Audio => {
let content = match from_value::<AudioMessageEventContent>(value) {
Ok(content) => content,
Err(error) => return Err(D::Error::custom(error.to_string())),
};
Ok(MessageEventContent::Audio(content))
}
MessageType::Emote => {
let content = match from_value::<EmoteMessageEventContent>(value) {
Ok(content) => content,
Err(error) => return Err(D::Error::custom(error.to_string())),
};
Ok(MessageEventContent::Emote(content))
}
MessageType::File => {
let content = match from_value::<FileMessageEventContent>(value) {
Ok(content) => content,
Err(error) => return Err(D::Error::custom(error.to_string())),
};
Ok(MessageEventContent::File(content))
}
MessageType::Image => {
let content = match from_value::<ImageMessageEventContent>(value) {
Ok(content) => content,
Err(error) => return Err(D::Error::custom(error.to_string())),
};
Ok(MessageEventContent::Image(content))
}
MessageType::Location => {
let content = match from_value::<LocationMessageEventContent>(value) {
Ok(content) => content,
Err(error) => return Err(D::Error::custom(error.to_string())),
};
Ok(MessageEventContent::Location(content))
}
MessageType::Notice => {
let content = match from_value::<NoticeMessageEventContent>(value) {
Ok(content) => content,
Err(error) => return Err(D::Error::custom(error.to_string())),
};
Ok(MessageEventContent::Notice(content))
}
MessageType::Text => {
let content = match from_value::<TextMessageEventContent>(value) {
Ok(content) => content,
Err(error) => return Err(D::Error::custom(error.to_string())),
};
Ok(MessageEventContent::Text(content))
}
MessageType::Video => {
let content = match from_value::<VideoMessageEventContent>(value) {
Ok(content) => content,
Err(error) => return Err(D::Error::custom(error.to_string())),
};
Ok(MessageEventContent::Video(content))
}
}
}
}
#[cfg(test)]
mod tests {
use serde_json::{from_str, to_string};
use super::{AudioMessageEventContent, MessageEventContent, MessageType};
#[test]
fn serialization() {
let message_event_content = MessageEventContent::Audio(AudioMessageEventContent {
body: "test".to_string(),
info: None,
msgtype: MessageType::Audio,
url: "http://example.com/audio.mp3".to_string(),
});
assert_eq!(
to_string(&message_event_content).unwrap(),
r#"{"body":"test","msgtype":"m.audio","url":"http://example.com/audio.mp3"}"#
);
}
#[test]
fn deserialization() {
let message_event_content = MessageEventContent::Audio(AudioMessageEventContent {
body: "test".to_string(),
info: None,
msgtype: MessageType::Audio,
url: "http://example.com/audio.mp3".to_string(),
});
assert_eq!(
from_str::<MessageEventContent>(
r#"{"body":"test","msgtype":"m.audio","url":"http://example.com/audio.mp3"}"#
)
.unwrap(),
message_event_content
);
}
#[test]
fn deserialization_failure() {
assert!(from_str::<MessageEventContent>(
r#"{"body":"test","msgtype":"m.location","url":"http://example.com/audio.mp3"}"#
)
.is_err());
}
}