diff --git a/src/core/types/chat.rs b/src/core/types/chat.rs index d808668f..1382d6a8 100644 --- a/src/core/types/chat.rs +++ b/src/core/types/chat.rs @@ -1,33 +1,83 @@ use crate::core::types::{ChatPermissions, ChatPhoto, Message}; -#[derive(Debug, Deserialize, Eq, Hash, PartialEq)] +#[derive(Debug, Deserialize, Eq, Hash, PartialEq, Serialize)] pub struct Chat { + #[serde(rename = "chat_id")] pub id: i32, #[serde(flatten)] pub type_: ChatType, + #[serde(skip_serializing_if = "Option::is_none")] pub photo: Option, } -#[derive(Debug, Deserialize, Eq, Hash, PartialEq)] +struct PrivateChatTypeVisitor; + +impl<'de> serde::de::Visitor<'de> for PrivateChatTypeVisitor { + type Value = (); + + fn expecting(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + write!(f, r#"field equal to "private""#) + } + + fn visit_borrowed_str( + self, + v: &'de str, + ) -> Result { + match v { + "private" => Ok(()), + _ => Err(E::invalid_value( + serde::de::Unexpected::Str(v), + &r#""private""#, + )), + } + } +} + +fn assert_private_field<'de, D: serde::Deserializer<'de>>( + des: D, +) -> Result<(), D::Error> { + des.deserialize_str(PrivateChatTypeVisitor) +} + +fn serialize_private_field( + _: &(), + ser: S, +) -> Result { + ser.serialize_str("private") +} + +#[derive(Debug, Deserialize, Eq, Hash, PartialEq, Serialize)] #[serde(rename_all = "snake_case")] #[serde(untagged)] pub enum ChatType { NotPrivate { + #[serde(skip_serializing_if = "Option::is_none")] title: Option, #[serde(flatten)] type_: NotPrivateChatType, + #[serde(skip_serializing_if = "Option::is_none")] description: Option, + #[serde(skip_serializing_if = "Option::is_none")] invite_link: Option, + #[serde(skip_serializing_if = "Option::is_none")] pinned_message: Option>, }, Private { + /// Dummy field. Used to ensure that "type" field is equal to "private" + #[serde(rename = "type")] + #[serde(deserialize_with = "assert_private_field")] + #[serde(serialize_with = "serialize_private_field")] + type_: (), + #[serde(skip_serializing_if = "Option::is_none")] username: Option, + #[serde(skip_serializing_if = "Option::is_none")] first_name: Option, + #[serde(skip_serializing_if = "Option::is_none")] last_name: Option, }, } -#[derive(Debug, Deserialize, Eq, Hash, PartialEq)] +#[derive(Debug, Deserialize, Eq, Hash, PartialEq, Serialize)] #[serde(rename_all = "snake_case")] #[serde(tag = "type")] pub enum NotPrivateChatType { @@ -45,25 +95,72 @@ pub enum NotPrivateChatType { }, } -#[test] -fn test_chat_de() { - use serde_json::from_str; +#[cfg(test)] +mod tests { + use crate::core::types::*; + use serde_json::{from_str, to_string}; - assert_eq!( - Chat { - id: 0, - type_: ChatType::NotPrivate { - title: None, - type_: NotPrivateChatType::Channel { - username: Some("channelname".into()) + #[test] + fn channel_de() { + assert_eq!( + Chat { + id: -1, + type_: ChatType::NotPrivate { + title: None, + type_: NotPrivateChatType::Channel { + username: Some("channelname".into()) + }, + description: None, + invite_link: None, + pinned_message: None }, - description: None, - invite_link: None, - pinned_message: None, + photo: None, }, - photo: None, - }, - from_str(r#"{"id":0,"type":"channel","username":"channelname"}"#) + from_str( + r#"{"chat_id":-1,"type":"channel","username":"channelname"}"# + ) .unwrap() - ); + ); + } + + #[test] + fn private_chat_de() { + assert_eq!( + Chat { + id: 0, + type_: ChatType::Private { + type_: (), + username: Some("username".into()), + first_name: Some("Anon".into()), + last_name: None + }, + photo: None + }, + from_str( + r#"{"chat_id":0,"type":"private","username":"username","first_name":"Anon"}"# + ).unwrap()); + } + + #[test] + fn private_chat_de_wrong_type_field() { + assert!(from_str::(r#"{"chat_id":0,"type":"WRONG"}"#).is_err()); + } + + #[test] + fn private_chat_ser() { + assert_eq!( + to_string(&Chat { + id: 0, + type_: ChatType::Private { + type_: (), + username: None, + first_name: None, + last_name: None + }, + photo: None + }) + .unwrap(), + r#"{"chat_id":0,"type":"private"}"#.to_owned() + ); + } } diff --git a/src/core/types/chat_permissions.rs b/src/core/types/chat_permissions.rs index 5e59cfc4..a6cb0d88 100644 --- a/src/core/types/chat_permissions.rs +++ b/src/core/types/chat_permissions.rs @@ -1,4 +1,4 @@ -#[derive(Debug, Deserialize, Hash, PartialEq, Eq)] +#[derive(Debug, Deserialize, Hash, PartialEq, Eq, Serialize)] pub struct ChatPermissions { pub can_send_messages: Option, pub can_send_media_messages: Option, diff --git a/src/core/types/chat_photo.rs b/src/core/types/chat_photo.rs index 463284c7..edabbf33 100644 --- a/src/core/types/chat_photo.rs +++ b/src/core/types/chat_photo.rs @@ -1,4 +1,4 @@ -#[derive(Debug, Deserialize, Hash, PartialEq, Eq)] +#[derive(Debug, Deserialize, Hash, PartialEq, Eq, Serialize)] pub struct ChatPhoto { pub small_file_id: String, pub big_file_id: String, diff --git a/src/core/types/message.rs b/src/core/types/message.rs index 112883fa..636693e1 100644 --- a/src/core/types/message.rs +++ b/src/core/types/message.rs @@ -4,53 +4,17 @@ use crate::core::types::{ SuccessfulPayment, User, Venue, Video, VideoNote, Voice, }; -#[derive(Debug, Deserialize, Eq, Hash, PartialEq)] +#[derive(Debug, Deserialize, Eq, Hash, PartialEq, Serialize)] pub struct Message { - pub message_id: i64, - pub from: Option>, - pub date: i64, + #[serde(rename = "message_id")] + pub id: i32, + pub date: i32, pub chat: Chat, - pub forward_from: Option, - pub forward_from_chat: Option, - pub forward_from_message_id: Option, - pub forward_signature: Option, - pub forward_sender_name: Option, - pub forward_date: Option, - pub reply_to_message: Option>, - pub edit_date: Option, - pub media_group_id: Option, - pub author_signature: Option, - pub text: Option, - pub entities: Option>, - pub caption_entities: Option>, - pub audio: Option