Make ChatPermissions into bitflags

This commit is contained in:
Maybe Waffle 2021-12-29 20:36:08 +03:00
parent fd3ef0bdf3
commit 62e9e8afd4
3 changed files with 178 additions and 80 deletions

View file

@ -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

View file

@ -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 = []

View file

@ -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<bool>,
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<bool>,
/// 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<bool>,
/// 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<bool>,
/// 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<bool>,
/// 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<bool>,
/// 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<bool>,
/// 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<bool>,
/// 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<ChatPermissionsRaw> 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<ChatPermissionsRaw> 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);
}
}