Merge branch 'dev' of github.com:async-telegram-bot/async-telegram-bot into dev

This commit is contained in:
Waffle 2019-09-07 22:17:28 +03:00
commit 1f50a87daf
3 changed files with 297 additions and 111 deletions

View file

@ -1,18 +1,60 @@
use crate::core::types::{ChatPermissions, ChatPhoto, Message}; use crate::core::types::{ChatPermissions, ChatPhoto, Message};
#[derive(Debug, Deserialize, Eq, Hash, PartialEq, Serialize, Clone)]
#[derive(Debug, Deserialize, Eq, Hash, PartialEq, Clone)]
pub struct Chat { pub struct Chat {
#[serde(rename = "chat_id")] #[serde(rename = "chat_id")]
pub id: i32, pub id: i64,
#[serde(flatten)] #[serde(flatten)]
pub type_: ChatType, pub kind: ChatKind,
#[serde(skip_serializing_if = "Option::is_none")]
pub photo: Option<ChatPhoto>, pub photo: Option<ChatPhoto>,
} }
struct PrivateChatTypeVisitor;
impl<'de> serde::de::Visitor<'de> for PrivateChatTypeVisitor { #[derive(Debug, Deserialize, Eq, Hash, PartialEq, Clone)]
#[serde(untagged)]
pub enum ChatKind {
NonPrivate {
title: Option<String>,
#[serde(flatten)]
kind: NonPrivateChatKind,
description: Option<String>,
invite_link: Option<String>,
pinned_message: Option<Box<Message>>,
},
Private {
/// Dummy field. Used to ensure that "type" field is equal to "private"
#[serde(rename = "type")]
#[serde(deserialize_with = "assert_private_field")]
type_: (),
username: Option<String>,
first_name: Option<String>,
last_name: Option<String>,
},
}
#[derive(Debug, Deserialize, Eq, Hash, PartialEq, Clone)]
#[serde(rename_all = "snake_case")]
#[serde(tag = "type")]
pub enum NonPrivateChatKind {
Channel {
username: Option<String>,
},
Group {
permissions: Option<ChatPermissions>,
},
Supergroup {
username: Option<String>,
sticker_set_name: Option<String>,
can_set_sticker_set: Option<bool>,
permissions: Option<ChatPermissions>,
},
}
struct PrivateChatKindVisitor;
impl<'de> serde::de::Visitor<'de> for PrivateChatKindVisitor {
type Value = (); type Value = ();
fn expecting(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { fn expecting(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
@ -33,94 +75,38 @@ impl<'de> serde::de::Visitor<'de> for PrivateChatTypeVisitor {
} }
} }
fn assert_private_field<'de, D: serde::Deserializer<'de>>( fn assert_private_field<'de, D>(des: D) -> Result<(), D::Error>
des: D, where
) -> Result<(), D::Error> { D: serde::Deserializer<'de>,
des.deserialize_str(PrivateChatTypeVisitor) {
} des.deserialize_str(PrivateChatKindVisitor)
fn serialize_private_field<S: serde::Serializer>(
_: &(),
ser: S,
) -> Result<S::Ok, S::Error> {
ser.serialize_str("private")
}
#[derive(Debug, Deserialize, Eq, Hash, PartialEq, Serialize, Clone)]
#[serde(rename_all = "snake_case")]
#[serde(untagged)]
pub enum ChatType {
NotPrivate {
#[serde(skip_serializing_if = "Option::is_none")]
title: Option<String>,
#[serde(flatten)]
type_: NotPrivateChatType,
#[serde(skip_serializing_if = "Option::is_none")]
description: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
invite_link: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pinned_message: Option<Box<Message>>,
},
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<String>,
#[serde(skip_serializing_if = "Option::is_none")]
first_name: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
last_name: Option<String>,
},
}
#[derive(Debug, Deserialize, Eq, Hash, PartialEq, Serialize, Clone)]
#[serde(rename_all = "snake_case")]
#[serde(tag = "type")]
pub enum NotPrivateChatType {
Channel {
username: Option<String>,
},
Group {
permissions: Option<ChatPermissions>,
},
Supergroup {
username: Option<String>,
sticker_set_name: Option<String>,
can_set_sticker_set: Option<bool>,
permissions: Option<ChatPermissions>,
},
} }
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use crate::core::types::*; use crate::core::types::*;
use serde_json::{from_str, to_string}; use serde_json::from_str;
#[test] #[test]
fn channel_de() { fn channel_de() {
assert_eq!( let expected = Chat {
Chat { id: -1,
id: -1, kind: ChatKind::NonPrivate {
type_: ChatType::NotPrivate { title: None,
title: None, kind: NonPrivateChatKind::Channel {
type_: NotPrivateChatType::Channel { username: Some("channelname".into()),
username: Some("channelname".into())
},
description: None,
invite_link: None,
pinned_message: None
}, },
photo: None, description: None,
invite_link: None,
pinned_message: None,
}, },
from_str( photo: None,
r#"{"chat_id":-1,"type":"channel","username":"channelname"}"# };
) let actual = from_str(
.unwrap() r#"{"chat_id":-1,"type":"channel","username":"channelname"}"#,
); )
.unwrap();
assert_eq!(expected, actual);
} }
#[test] #[test]
@ -128,7 +114,7 @@ mod tests {
assert_eq!( assert_eq!(
Chat { Chat {
id: 0, id: 0,
type_: ChatType::Private { kind: ChatKind::Private {
type_: (), type_: (),
username: Some("username".into()), username: Some("username".into()),
first_name: Some("Anon".into()), first_name: Some("Anon".into()),
@ -145,22 +131,4 @@ mod tests {
fn private_chat_de_wrong_type_field() { fn private_chat_de_wrong_type_field() {
assert!(from_str::<Chat>(r#"{"chat_id":0,"type":"WRONG"}"#).is_err()); assert!(from_str::<Chat>(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()
);
}
} }

View file

@ -4,17 +4,235 @@ use crate::core::types::{
SuccessfulPayment, User, Venue, Video, VideoNote, Voice, SuccessfulPayment, User, Venue, Video, VideoNote, Voice,
}; };
#[derive(Debug, Deserialize, Eq, Hash, PartialEq, Serialize, Clone)]
#[derive(Debug, Deserialize, Eq, Hash, PartialEq, Clone)]
pub struct Message { pub struct Message {
#[serde(rename = "message_id")] #[serde(rename = "message_id")]
pub id: i32, pub id: i64,
pub date: i32, pub date: i32,
pub chat: Chat, pub chat: Chat,
#[serde(flatten)] #[serde(flatten)]
pub type_: MessageType, pub message_kind: MessageKind,
} }
#[derive(Debug, Deserialize, Eq, Hash, PartialEq, Serialize, Clone)]
#[serde(rename_all = "snake_case")] #[derive(Debug, Deserialize, Eq, Hash, PartialEq, Clone)]
#[serde(untagged)] #[serde(untagged)]
pub enum MessageType {} pub enum MessageKind {
IncomingMessage {
#[serde(flatten)]
forward_kind: ForwardKind,
edit_date: Option<i32>,
#[serde(flatten)]
media_kind: MediaKind,
reply_markup: Option<InlineKeyboardMarkup>,
},
NewChatMembers {
new_chat_members: Vec<User>,
},
LeftChatMember {
left_chat_member: User,
},
NewChatTitle {
new_chat_title: String,
},
NewChatPhoto {
new_chat_photo: Vec<PhotoSize>,
},
DeleteChatPhoto {
delete_chat_photo: bool,
},
GroupChatCreated {
group_chat_created: bool,
},
SupergroupChatCreated {
supergroup_chat_created: bool,
},
ChannelChatCreated {
channel_chat_created: bool,
},
Migrate {
migrate_to_chat_id: i64,
migrate_from_chat_id: i64,
},
PinnedMessage {
pinned_message: Box<Message>,
},
Invoice {
invoice: Invoice,
},
SuccessfulPayment {
successful_payment: SuccessfulPayment,
},
ConnectedWebsite {
connected_website: String,
},
PassportData {
passport_data: PassportData,
},
}
#[derive(Debug, Deserialize, Eq, Hash, PartialEq, Clone)]
#[serde(untagged)]
pub enum ForwardKind {
ChannelForward {
#[serde(rename = "forward_date")]
date: i32,
#[serde(rename = "forward_from_chat")]
chat: Chat,
#[serde(rename = "forward_from_message_id")]
message_id: i64,
#[serde(rename = "forward_signature")]
signature: Option<String>,
},
NonChannelForward {
#[serde(rename = "forward_date")]
date: i32,
#[serde(flatten)]
from: ForwardedFrom,
},
Origin {
reply_to_message: Option<Box<Message>>,
},
}
#[derive(Debug, Deserialize, Eq, Hash, PartialEq, Clone)]
pub enum ForwardedFrom {
#[serde(rename = "forward_from")]
User(User),
#[serde(rename = "forward_sender_name")]
SenderName(String),
}
#[derive(Debug, Deserialize, Eq, Hash, PartialEq, Clone)]
#[serde(untagged)]
pub enum MediaKind {
Animation {
animation: Animation,
caption: Option<String>,
},
Audio {
audio: Audio,
caption: Option<String>,
},
Contact {
contact: Contact,
},
Document {
document: Document,
caption: Option<String>,
},
Game {
game: Game,
},
Location {
location: Location,
},
Photo {
sizes: Vec<PhotoSize>,
caption: Option<String>,
},
Poll {
poll: Poll,
},
Sticker {
sticker: Sticker,
},
Text {
text: String,
entities: Vec<MessageEntity>,
},
Video {
video: Video,
caption: Option<String>,
},
VideoNote {
video_note: VideoNote,
},
Voice {
voice: Voice,
caption: Option<String>,
},
Venue {
venue: Venue,
},
}
#[cfg(test)]
mod tests {
use crate::core::types::*;
use serde_json::from_str;
#[test]
fn origin_de() {
let expected = Message {
id: 0,
date: 0,
chat: Chat {
id: 0,
kind: ChatKind::Private {
type_: (),
username: None,
first_name: None,
last_name: None,
},
photo: None,
},
message_kind: MessageKind::IncomingMessage {
forward_kind: ForwardKind::Origin {
reply_to_message: None,
},
edit_date: None,
media_kind: MediaKind::Text {
text: "Hello".to_string(),
entities: vec![],
},
reply_markup: None,
},
};
let actual = from_str::<Message>(r#"{"message_id":0,"date":0,"chat":{"chat_id":0,"type":"private"},"text":"Hello","entities":[]}"#).unwrap();
assert_eq!(expected, actual);
}
#[test]
fn forward_de() {
let expected = Message {
id: 1,
date: 1,
chat: Chat {
id: 1,
kind: ChatKind::Private {
type_: (),
username: None,
first_name: None,
last_name: None,
},
photo: None,
},
message_kind: MessageKind::IncomingMessage {
forward_kind: ForwardKind::NonChannelForward {
date: 1,
from: ForwardedFrom::User(User {
id: 123,
is_bot: false,
first_name: "Name".to_string(),
last_name: None,
username: None,
language_code: None,
}),
},
edit_date: None,
media_kind: MediaKind::Text {
text: "Message".into(),
entities: vec![],
},
reply_markup: None,
},
};
let actual = from_str::<Message>(
r#"{"message_id":1,"date":1,"chat":{"chat_id":1,"type":"private"},"forward_date":1,"forward_from":{"id":123,"is_bot":false,"first_name":"Name"},"text":"Message","entities":[]}"#,
)
.unwrap();
assert_eq!(expected, actual);
}
}

View file

@ -3,7 +3,7 @@ pub use self::{
answer_pre_checkout_query::AnswerPreCheckoutQuery, answer_pre_checkout_query::AnswerPreCheckoutQuery,
answer_shipping_query::AnswerShippingQuery, answer_shipping_query::AnswerShippingQuery,
audio::Audio, audio::Audio,
chat::{Chat, ChatType, NotPrivateChatType}, chat::{Chat, ChatKind, NonPrivateChatKind},
chat_member::ChatMember, chat_member::ChatMember,
chat_permissions::ChatPermissions, chat_permissions::ChatPermissions,
chat_photo::ChatPhoto, chat_photo::ChatPhoto,
@ -12,7 +12,7 @@ pub use self::{
input_media::InputMedia, input_media::InputMedia,
invoice::Invoice, invoice::Invoice,
label_price::LabeledPrice, label_price::LabeledPrice,
message::Message, message::{ForwardKind, ForwardedFrom, MediaKind, Message, MessageKind},
message_entity::MessageEntity, message_entity::MessageEntity,
order_info::OrderInfo, order_info::OrderInfo,
parse_mode::ParseMode, parse_mode::ParseMode,