From 62e9e8afd445b0f062a02428744401e193083a06 Mon Sep 17 00:00:00 2001 From: Maybe Waffle Date: Wed, 29 Dec 2021 20:36:08 +0300 Subject: [PATCH] Make ChatPermissions into bitflags --- CHANGELOG.md | 2 + Cargo.toml | 4 +- src/types/chat_permissions.rs | 252 +++++++++++++++++++++++----------- 3 files changed, 178 insertions(+), 80 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d772e23f..fa1bb419 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -43,11 +43,13 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - `InputFile` and related structures now do **not** implement `PartialEq`, `Eq` and `Hash` ([#133][pr133]) - How forwarded messages are represented ([#151][pr151]) - `RequestError::InvalidJson` now has a `raw` field with raw json for easier debugability ([#150][pr150]) +- `ChatPermissions` is now bitflags ([#157][pr157]) [pr115]: https://github.com/teloxide/teloxide-core/pull/115 [pr125]: https://github.com/teloxide/teloxide-core/pull/125 [pr134]: https://github.com/teloxide/teloxide-core/pull/134 [pr150]: https://github.com/teloxide/teloxide-core/pull/150 +[pr157]: https://github.com/teloxide/teloxide-core/pull/157 ### Fixed diff --git a/Cargo.toml b/Cargo.toml index ad4bb0b8..baa83bfd 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -42,9 +42,9 @@ once_cell = "1.5.0" never = "0.1.0" chrono = { version = "0.4.19", default-features = false } either = "1.6.1" +bitflags = { version = "1.2" } vecrem = { version = "0.1", optional = true } -bitflags = { version = "1.2", optional = true } [dev-dependencies] pretty_env_logger = "0.4" @@ -66,7 +66,7 @@ nightly = [] throttle = ["vecrem"] # Trace bot adaptor -trace_adaptor = ["bitflags"] +trace_adaptor = [] # Erased bot adaptor erased = [] diff --git a/src/types/chat_permissions.rs b/src/types/chat_permissions.rs index d95b6643..fa27bffa 100644 --- a/src/types/chat_permissions.rs +++ b/src/types/chat_permissions.rs @@ -1,96 +1,192 @@ use serde::{Deserialize, Serialize}; +use std::ops::Not; -/// Describes actions that a non-administrator user is allowed to take in a -/// chat. -/// -/// [The official docs](https://core.telegram.org/bots/api#chatpermissions). -#[serde_with_macros::skip_serializing_none] -#[derive(Copy, Clone, Default, Debug, Eq, Hash, PartialEq, Serialize, Deserialize)] -pub struct ChatPermissions { - /// `true`, if the user is allowed to send text messages, contacts, - /// locations and venues. - pub can_send_messages: Option, +bitflags::bitflags! { + /// Describes actions that a non-administrator user is allowed to take in a + /// chat. + /// + /// [The official docs](https://core.telegram.org/bots/api#chatpermissions). + /// + /// ## Examples + /// + /// ``` + /// use teloxide_core::types::ChatPermissions; + /// + /// // No permissions, nothing is allowed + /// let _ = ChatPermissions::empty(); + /// + /// // All permissions, everything is allowed + /// let _ = ChatPermissions::all(); + /// + /// // One particular permission + /// let permissions_v0 = ChatPermissions::INVITE_USERS; + /// + /// // Check what is permitted + /// assert!(permissions_v0.contains(ChatPermissions::INVITE_USERS)); + /// assert!(!permissions_v0.contains(ChatPermissions::SEND_MESSAGES)); + /// + /// // Union, add permissions + /// let permissions_v1 = permissions_v0 | ChatPermissions::SEND_MEDIA_MESSAGES; + /// assert!(permissions_v1.contains(ChatPermissions::INVITE_USERS)); + /// assert!(permissions_v1.contains(ChatPermissions::SEND_MEDIA_MESSAGES)); + /// + /// // Implied by `SEND_MEDIA_MESSAGES` + /// assert!(permissions_v1.contains(ChatPermissions::SEND_MESSAGES)); + /// + /// // Difference, remove permissions + /// let permissions_v2 = permissions_v1 - ChatPermissions::SEND_MEDIA_MESSAGES; + /// assert!(!permissions_v2.contains(ChatPermissions::SEND_MEDIA_MESSAGES)); + /// + /// // Removing `SEND_MEDIA_MESSAGES` also removes `SEND_MESSAGES` and vice versa + /// // because `SEND_MESSAGES` is implied by `SEND_MEDIA_MESSAGES` + /// assert!(!permissions_v2.contains(ChatPermissions::SEND_MESSAGES)); + /// + /// let permissions_v3 = permissions_v1 - ChatPermissions::SEND_MESSAGES; + /// assert!(!permissions_v3.contains(ChatPermissions::SEND_MEDIA_MESSAGES)); + /// ``` + #[derive(Serialize, Deserialize)] + #[serde(from = "ChatPermissionsRaw", into = "ChatPermissionsRaw")] + pub struct ChatPermissions: u8 { + /// Set if the user is allowed to send text messages, contacts, + /// locations and venues. + const SEND_MESSAGES = 1; - /// `true`, if the user is allowed to send audios, documents, - /// photos, videos, video notes and voice notes, implies - /// `can_send_messages`. - pub can_send_media_messages: Option, + /// Set if the user is allowed to send audios, documents, + /// photos, videos, video notes and voice notes, implies + /// `SEND_MESSAGES`. + const SEND_MEDIA_MESSAGES = (1 << 1) | Self::SEND_MESSAGES.bits; - /// `true`, if the user is allowed to send polls, implies - /// `can_send_messages`. - pub can_send_polls: Option, + /// Set if the user is allowed to send polls, implies + /// `SEND_MESSAGES`. + const SEND_POLLS = (1 << 2) | Self::SEND_MESSAGES.bits; - /// `true`, if the user is allowed to send animations, games, stickers and - /// use inline bots, implies `can_send_media_messages`. - pub can_send_other_messages: Option, + /// Set if the user is allowed to send animations, games, stickers and + /// use inline bots, implies `SEND_MEDIA_MESSAGES`. + const SEND_OTHER_MESSAGES = (1 << 3) | Self::SEND_MEDIA_MESSAGES.bits; - /// `true`, if the user is allowed to add web page previews to - /// their messages, implies `can_send_media_messages`. - pub can_add_web_page_previews: Option, + /// Set if the user is allowed to add web page previews to + /// their messages, implies `SEND_MEDIA_MESSAGES`. + const ADD_WEB_PAGE_PREVIEWS = (1 << 4) | Self::SEND_MEDIA_MESSAGES.bits; - /// `true`, if the user is allowed to change the chat title, photo and - /// other settings. Ignored in public supergroups. - pub can_change_info: Option, + /// Set if the user is allowed to change the chat title, photo and + /// other settings. Ignored in public supergroups. + const CHANGE_INFO = (1 << 5); - /// `true`, if the user is allowed to invite new users to the chat. - pub can_invite_users: Option, + /// Set if the user is allowed to invite new users to the chat. + const INVITE_USERS = (1 << 6); - /// `true`, if the user is allowed to pin messages. Ignored in public - /// supergroups. - pub can_pin_messages: Option, + /// Set if the user is allowed to pin messages. Ignored in public + /// supergroups. + const PIN_MESSAGES = (1 << 7); + } } -impl ChatPermissions { - pub const fn new() -> Self { - Self { - can_send_messages: None, - can_send_media_messages: None, - can_send_polls: None, - can_send_other_messages: None, - can_add_web_page_previews: None, - can_change_info: None, - can_invite_users: None, - can_pin_messages: None, +/// Helper for (de)serialization +#[derive(Serialize, Deserialize)] +struct ChatPermissionsRaw { + #[serde(default, skip_serializing_if = "Not::not")] + can_send_messages: bool, + + #[serde(default, skip_serializing_if = "Not::not")] + can_send_media_messages: bool, + + #[serde(default, skip_serializing_if = "Not::not")] + can_send_polls: bool, + + #[serde(default, skip_serializing_if = "Not::not")] + can_send_other_messages: bool, + + #[serde(default, skip_serializing_if = "Not::not")] + can_add_web_page_previews: bool, + + #[serde(default, skip_serializing_if = "Not::not")] + can_change_info: bool, + + #[serde(default, skip_serializing_if = "Not::not")] + can_invite_users: bool, + + #[serde(default, skip_serializing_if = "Not::not")] + can_pin_messages: bool, +} + +impl Into for ChatPermissions { + fn into(self) -> ChatPermissionsRaw { + ChatPermissionsRaw { + can_send_messages: self.contains(ChatPermissions::SEND_MESSAGES), + can_send_media_messages: self.contains(ChatPermissions::SEND_MEDIA_MESSAGES), + can_send_polls: self.contains(ChatPermissions::SEND_POLLS), + can_send_other_messages: self.contains(ChatPermissions::SEND_OTHER_MESSAGES), + can_add_web_page_previews: self.contains(ChatPermissions::ADD_WEB_PAGE_PREVIEWS), + can_change_info: self.contains(ChatPermissions::CHANGE_INFO), + can_invite_users: self.contains(ChatPermissions::INVITE_USERS), + can_pin_messages: self.contains(ChatPermissions::PIN_MESSAGES), } } +} - pub const fn can_send_messages(mut self, val: bool) -> Self { - self.can_send_messages = Some(val); - self - } +impl From for ChatPermissions { + fn from( + ChatPermissionsRaw { + can_send_messages, + can_send_media_messages, + can_send_polls, + can_send_other_messages, + can_add_web_page_previews, + can_change_info, + can_invite_users, + can_pin_messages, + }: ChatPermissionsRaw, + ) -> Self { + let mut this = Self::empty(); - pub const fn can_send_media_messages(mut self, val: bool) -> Self { - self.can_send_media_messages = Some(val); - self - } + if can_send_messages { + this |= Self::SEND_MESSAGES; + } + if can_send_media_messages { + this |= Self::SEND_MEDIA_MESSAGES + } + if can_send_polls { + this |= Self::SEND_POLLS; + } + if can_send_other_messages { + this |= Self::SEND_OTHER_MESSAGES; + } + if can_add_web_page_previews { + this |= Self::ADD_WEB_PAGE_PREVIEWS; + } + if can_change_info { + this |= Self::CHANGE_INFO; + } + if can_invite_users { + this |= Self::INVITE_USERS; + } + if can_pin_messages { + this |= Self::PIN_MESSAGES; + } - pub const fn can_send_polls(mut self, val: bool) -> Self { - self.can_send_polls = Some(val); - self - } - - pub const fn can_send_other_messages(mut self, val: bool) -> Self { - self.can_send_other_messages = Some(val); - self - } - - pub const fn can_add_web_page_previews(mut self, val: bool) -> Self { - self.can_add_web_page_previews = Some(val); - self - } - - pub const fn can_change_info(mut self, val: bool) -> Self { - self.can_change_info = Some(val); - self - } - - pub const fn can_invite_users(mut self, val: bool) -> Self { - self.can_invite_users = Some(val); - self - } - - pub const fn can_pin_messages(mut self, val: bool) -> Self { - self.can_pin_messages = Some(val); - self + this + } +} + +#[cfg(test)] +mod tests { + use super::ChatPermissions; + + #[test] + fn serialization() { + let permissions = ChatPermissions::SEND_MEDIA_MESSAGES | ChatPermissions::PIN_MESSAGES; + let expected = + r#"{"can_send_messages":true,"can_send_media_messages":true,"can_pin_messages":true}"#; + let actual = serde_json::to_string(&permissions).unwrap(); + assert_eq!(expected, actual); + } + + #[test] + fn deserialization() { + let json = + r#"{"can_send_messages":true,"can_send_media_messages":true,"can_pin_messages":true}"#; + let expected = ChatPermissions::SEND_MEDIA_MESSAGES | ChatPermissions::PIN_MESSAGES; + let actual = serde_json::from_str(json).unwrap(); + assert_eq!(expected, actual); } }