diff --git a/Cargo.toml b/Cargo.toml index afc3c639349..109de4e5a20 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -32,6 +32,7 @@ secrecy = { version = "0.8.0", features = ["serde"] } arrayvec = { version = "0.7.4", features = ["serde"] } serde_cow = { version = "0.1.0" } small-fixed-array = { git = "https://github.com/GnomedDev/small-fixed-array", features = ["serde", "log_using_tracing"] } +bool_to_bitflags = { git = "https://github.com/GnomedDev/bool-to-bitflags", version = "0.1.0" } # Optional dependencies fxhash = { version = "0.2.1", optional = true } simd-json = { version = "0.13.4", optional = true } @@ -126,7 +127,7 @@ simd_json = ["simd-json", "typesize?/simd_json"] # Enables temporary caching in functions that retrieve data via the HTTP API. temp_cache = ["cache", "mini-moka", "typesize?/mini_moka"] -typesize = ["dep:typesize", "small-fixed-array/typesize"] +typesize = ["dep:typesize", "small-fixed-array/typesize", "bool_to_bitflags/typesize"] # Removed feature (https://github.com/serenity-rs/serenity/pull/2246) absolute_ratelimits = [] diff --git a/examples/e05_command_framework/src/main.rs b/examples/e05_command_framework/src/main.rs index 23960bd1709..64723d4e11d 100644 --- a/examples/e05_command_framework/src/main.rs +++ b/examples/e05_command_framework/src/main.rs @@ -349,10 +349,10 @@ async fn say(ctx: &Context, msg: &Message, mut args: Args) -> CommandResult { if let Some(guild) = msg.guild(&ctx.cache) { // By default roles, users, and channel mentions are cleaned. let settings = ContentSafeOptions::default() - // We do not want to clean channal mentions as they do not ping users. + // We do not want to clean channel mentions as they do not ping users. .clean_channel(false); - x = content_safe(&guild, x, &settings, &msg.mentions); + x = content_safe(&guild, x, settings, &msg.mentions); } msg.channel_id.say(&ctx.http, x).await?; diff --git a/src/builder/create_command.rs b/src/builder/create_command.rs index 8f625f6c403..7de5cd81b12 100644 --- a/src/builder/create_command.rs +++ b/src/builder/create_command.rs @@ -30,8 +30,7 @@ impl CreateCommandOption { name_localizations: None, description: description.into().into(), description_localizations: None, - required: false, - autocomplete: false, + __generated_flags: CommandOptionGeneratedFlags::empty(), min_value: None, max_value: None, min_length: None, @@ -104,7 +103,7 @@ impl CreateCommandOption { /// /// **Note**: This defaults to `false`. pub fn required(mut self, required: bool) -> Self { - self.0.required = required; + self.0.set_required(required); self } @@ -203,7 +202,7 @@ impl CreateCommandOption { /// - May not be set to `true` if `choices` are set /// - Options using `autocomplete` are not confined to only use given choices pub fn set_autocomplete(mut self, value: bool) -> Self { - self.0.autocomplete = value; + self.0.set_autocomplete(value); self } diff --git a/src/builder/edit_role.rs b/src/builder/edit_role.rs index c57c6ba3439..55f9d44bdf6 100644 --- a/src/builder/edit_role.rs +++ b/src/builder/edit_role.rs @@ -77,8 +77,8 @@ impl<'a> EditRole<'a> { /// Creates a new builder with the values of the given [`Role`]. pub fn from_role(role: &Role) -> Self { EditRole { - hoist: Some(role.hoist), - mentionable: Some(role.mentionable), + hoist: Some(role.hoist()), + mentionable: Some(role.mentionable()), name: Some(role.name.clone()), permissions: Some(role.permissions.bits()), position: Some(role.position), diff --git a/src/cache/event.rs b/src/cache/event.rs index 24780d20acb..2fe5a8cb4cf 100644 --- a/src/cache/event.rs +++ b/src/cache/event.rs @@ -34,7 +34,7 @@ use crate::model::event::{ VoiceStateUpdateEvent, }; use crate::model::gateway::ShardInfo; -use crate::model::guild::{Guild, GuildMemberFlags, Member, Role}; +use crate::model::guild::{Guild, GuildMemberFlags, Member, MemberGeneratedFlags, Role}; use crate::model::id::ShardId; use crate::model::user::{CurrentUser, OnlineStatus}; use crate::model::voice::VoiceState; @@ -190,13 +190,13 @@ impl CacheUpdate for GuildMemberUpdateEvent { member.nick.clone_from(&self.nick); member.roles.clone_from(&self.roles); member.user.clone_from(&self.user); - member.pending.clone_from(&self.pending); member.premium_since.clone_from(&self.premium_since); - member.deaf.clone_from(&self.deaf); - member.mute.clone_from(&self.mute); member.avatar.clone_from(&self.avatar); member.communication_disabled_until.clone_from(&self.communication_disabled_until); member.unusual_dm_activity_until.clone_from(&self.unusual_dm_activity_until); + member.set_pending(self.pending()); + member.set_deaf(self.deaf()); + member.set_mute(self.mute()); item } else { @@ -204,22 +204,26 @@ impl CacheUpdate for GuildMemberUpdateEvent { }; if item.is_none() { - guild.members.insert(self.user.id, Member { - deaf: false, + let mut new_member = Member { + __generated_flags: MemberGeneratedFlags::empty(), guild_id: self.guild_id, joined_at: Some(self.joined_at), - mute: false, nick: self.nick.clone(), roles: self.roles.clone(), user: self.user.clone(), - pending: self.pending, premium_since: self.premium_since, permissions: None, avatar: self.avatar, communication_disabled_until: self.communication_disabled_until, flags: GuildMemberFlags::default(), unusual_dm_activity_until: self.unusual_dm_activity_until, - }); + }; + + new_member.set_pending(self.pending()); + new_member.set_deaf(self.deaf()); + new_member.set_mute(self.mute()); + + guild.members.insert(self.user.id, new_member); } item @@ -318,7 +322,7 @@ impl CacheUpdate for GuildUpdateEvent { guild.system_channel_id = self.guild.system_channel_id; guild.verification_level = self.guild.verification_level; guild.widget_channel_id = self.guild.widget_channel_id; - guild.widget_enabled = self.guild.widget_enabled; + guild.set_widget_enabled(self.guild.widget_enabled()); } None @@ -416,20 +420,18 @@ impl CacheUpdate for PresenceUpdateEvent { // Create a partial member instance out of the presence update data. if let Some(user) = self.presence.user.to_user() { guild.members.entry(self.presence.user.id).or_insert_with(|| Member { - deaf: false, guild_id, joined_at: None, - mute: false, nick: None, user, roles: FixedArray::default(), - pending: false, premium_since: None, permissions: None, avatar: None, communication_disabled_until: None, flags: GuildMemberFlags::default(), unusual_dm_activity_until: None, + __generated_flags: MemberGeneratedFlags::empty(), }); } } diff --git a/src/framework/standard/configuration.rs b/src/framework/standard/configuration.rs index 9121fb2e347..6c8921ee3ca 100644 --- a/src/framework/standard/configuration.rs +++ b/src/framework/standard/configuration.rs @@ -101,24 +101,56 @@ impl From<(bool, bool, bool)> for WithWhiteSpace { /// [`Client`]: crate::Client /// [`StandardFramework`]: super::StandardFramework /// [default implementation]: Self::default +#[bool_to_bitflags::bool_to_bitflags( + getter_prefix = "get_", + setter_prefix = "", + private_getters, + document_setters, + owning_setters +)] #[derive(Clone)] pub struct Configuration { - pub(crate) allow_dm: bool, pub(crate) with_whitespace: WithWhiteSpace, - pub(crate) by_space: bool, pub(crate) blocked_guilds: HashSet, pub(crate) blocked_users: HashSet, pub(crate) allowed_channels: HashSet, pub(crate) disabled_commands: HashSet, pub(crate) dynamic_prefixes: Vec, - pub(crate) ignore_bots: bool, - pub(crate) ignore_webhooks: bool, pub(crate) on_mention: Option, pub(crate) owners: HashSet, pub(crate) prefixes: Vec, - pub(crate) no_dm_prefix: bool, pub(crate) delimiters: Vec, - pub(crate) case_insensitive: bool, + /// If set to false, bot will ignore any private messages. + /// + /// **Note**: Defaults to `true`. + pub allow_dm: bool, + /// Whether the framework should split the message by a space first to parse the group or + /// command. If set to false, it will only test part of the message by the *length* of the + /// group's or command's names. + /// + /// **Note**: Defaults to `true` + pub by_space: bool, + /// Whether the bot should respond to other bots. + /// + /// For example, if this is set to false, then the bot will respond to any other bots including + /// itself. + /// + /// **Note**: Defaults to `true`. + pub ignore_bots: bool, + /// If set to true, bot will ignore all commands called by webhooks. + /// + /// **Note**: Defaults to `true`. + pub ignore_webhooks: bool, + /// Sets whether command execution can be done without a prefix. Works only in private + /// channels. + /// + /// **Note**: Defaults to `false`. + /// + /// # Note + /// + /// The `cache` feature is required. If disabled this does absolutely nothing. + pub no_dm_prefix: bool, + case_insensitive: bool, } impl Configuration { @@ -128,15 +160,6 @@ impl Configuration { Self::default() } - /// If set to false, bot will ignore any private messages. - /// - /// **Note**: Defaults to `true`. - #[must_use] - pub fn allow_dm(mut self, allow_dm: bool) -> Self { - self.allow_dm = allow_dm; - self - } - /// Whether to allow whitespace being optional between a prefix/group-prefix/command and a /// command. /// @@ -165,17 +188,6 @@ impl Configuration { self } - /// Whether the framework should split the message by a space first to parse the group or - /// command. If set to false, it will only test part of the message by the *length* of the - /// group's or command's names. - /// - /// **Note**: Defaults to `true` - #[must_use] - pub fn by_space(mut self, b: bool) -> Self { - self.by_space = b; - self - } - /// HashSet of channels Ids where commands will be working. /// /// **Note**: Defaults to an empty HashSet. @@ -351,27 +363,6 @@ impl Configuration { self } - /// Whether the bot should respond to other bots. - /// - /// For example, if this is set to false, then the bot will respond to any other bots including - /// itself. - /// - /// **Note**: Defaults to `true`. - #[must_use] - pub fn ignore_bots(mut self, ignore_bots: bool) -> Self { - self.ignore_bots = ignore_bots; - self - } - - /// If set to true, bot will ignore all commands called by webhooks. - /// - /// **Note**: Defaults to `true`. - #[must_use] - pub fn ignore_webhooks(mut self, ignore_webhooks: bool) -> Self { - self.ignore_webhooks = ignore_webhooks; - self - } - /// Whether or not to respond to commands initiated with `id_to_mention`. /// /// **Note**: that this can be used in conjunction with [`Self::prefix`]. @@ -485,20 +476,6 @@ impl Configuration { self } - /// Sets whether command execution can be done without a prefix. Works only in private channels. - /// - /// **Note**: Defaults to `false`. - /// - /// # Note - /// - /// The `cache` feature is required. If disabled this does absolutely nothing. - #[inline] - #[must_use] - pub fn no_dm_prefix(mut self, b: bool) -> Self { - self.no_dm_prefix = b; - self - } - /// Sets a single delimiter to be used when splitting the content after a command. /// /// **Note**: Defaults to a vector with a single element of `' '`. @@ -555,7 +532,7 @@ impl Configuration { /// **Note**: Defaults to `false`. #[must_use] pub fn case_insensitivity(mut self, cs: bool) -> Self { - self.case_insensitive = cs; + self = self.case_insensitive(cs); for prefix in &mut self.prefixes { *prefix = prefix.to_lowercase(); @@ -585,23 +562,20 @@ impl Default for Configuration { /// - **owners** to an empty HashSet /// - **prefix** to "~" fn default() -> Configuration { - Configuration { - allow_dm: true, + let config = Configuration { + __generated_flags: ConfigurationGeneratedFlags::empty(), with_whitespace: WithWhiteSpace::default(), - by_space: true, blocked_guilds: HashSet::default(), blocked_users: HashSet::default(), allowed_channels: HashSet::default(), - case_insensitive: false, delimiters: vec![Delimiter::Single(' ')], disabled_commands: HashSet::default(), dynamic_prefixes: Vec::new(), - ignore_bots: true, - ignore_webhooks: true, - no_dm_prefix: false, on_mention: None, owners: HashSet::default(), prefixes: vec![String::from("~")], - } + }; + + config.allow_dm(true).by_space(true).ignore_bots(true).ignore_webhooks(true) } } diff --git a/src/framework/standard/mod.rs b/src/framework/standard/mod.rs index fae9b909417..4089c557647 100644 --- a/src/framework/standard/mod.rs +++ b/src/framework/standard/mod.rs @@ -203,8 +203,8 @@ impl StandardFramework { fn should_ignore(&self, msg: &Message) -> bool { let config = self.config.read(); - (config.ignore_bots && msg.author.bot) - || (config.ignore_webhooks && msg.webhook_id.is_some()) + (config.get_ignore_bots() && msg.author.bot()) + || (config.get_ignore_webhooks() && msg.webhook_id.is_some()) } async fn should_fail<'a>( @@ -637,7 +637,7 @@ impl Framework for StandardFramework { return; } - if prefix.is_none() && !(config.no_dm_prefix && msg.is_private()) { + if prefix.is_none() && !(config.get_no_dm_prefix() && msg.is_private()) { if let Some(normal) = &self.normal_message { normal(&mut ctx, &msg).await; } @@ -684,7 +684,7 @@ impl Framework for StandardFramework { match invoke { Invoke::Help(name) => { - if !config.allow_dm && msg.is_private() { + if !config.get_allow_dm() && msg.is_private() { return; } diff --git a/src/framework/standard/parse/map.rs b/src/framework/standard/parse/map.rs index 96309e5772a..9cfe9dc1f61 100644 --- a/src/framework/standard/parse/map.rs +++ b/src/framework/standard/parse/map.rs @@ -34,8 +34,11 @@ impl CommandMap { map.min_length = std::cmp::min(len, map.min_length); map.max_length = std::cmp::max(len, map.max_length); - let name = - if conf.case_insensitive { name.to_lowercase() } else { (*name).to_string() }; + let name = if conf.get_case_insensitive() { + name.to_lowercase() + } else { + (*name).to_string() + }; map.cmds.insert(name, (*cmd, Arc::clone(&sub_map))); } diff --git a/src/framework/standard/parse/mod.rs b/src/framework/standard/parse/mod.rs index bd02a80963a..727bf9e5b1d 100644 --- a/src/framework/standard/parse/mod.rs +++ b/src/framework/standard/parse/mod.rs @@ -95,7 +95,7 @@ fn permissions_in( #[inline] fn to_lowercase<'a>(config: &Configuration, s: &'a str) -> Cow<'a, str> { - if config.case_insensitive { + if config.get_case_insensitive() { Cow::Owned(s.to_lowercase()) } else { Cow::Borrowed(s) @@ -209,7 +209,7 @@ async fn check_discrepancy( return Err(DispatchError::OnlyForDM); } - if (!config.allow_dm || options.only_in() == OnlyIn::Guild) && msg.is_private() { + if (!config.get_allow_dm() || options.only_in() == OnlyIn::Guild) && msg.is_private() { return Err(DispatchError::OnlyForGuilds); } @@ -278,7 +278,7 @@ fn parse_cmd<'a>( ) -> BoxFuture<'a, Result<&'static Command, ParseError>> { async move { let (n, r) = - try_parse(stream, map, config.by_space, |s| to_lowercase(config, s).into_owned()); + try_parse(stream, map, config.get_by_space(), |s| to_lowercase(config, s).into_owned()); if config.disabled_commands.contains(&n) { return Err(ParseError::Dispatch { @@ -324,7 +324,7 @@ fn parse_group<'a>( map: &'a GroupMap, ) -> BoxFuture<'a, Result<(&'static CommandGroup, Arc), ParseError>> { async move { - let (n, o) = try_parse(stream, map, config.by_space, ToString::to_string); + let (n, o) = try_parse(stream, map, config.get_by_space(), ToString::to_string); if let Some((group, map, commands)) = o { stream.increment(n.len()); diff --git a/src/lib.rs b/src/lib.rs index b249d2106ab..a35154eb514 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -65,7 +65,6 @@ #![allow( // Allowed to avoid breaking changes. clippy::module_name_repetitions, - clippy::struct_excessive_bools, clippy::unused_self, // Allowed as they are too pedantic clippy::cast_possible_truncation, diff --git a/src/model/application/command.rs b/src/model/application/command.rs index d2c845f7080..42298b5559c 100644 --- a/src/model/application/command.rs +++ b/src/model/application/command.rs @@ -22,8 +22,9 @@ use crate::model::Permissions; /// The base command model that belongs to an application. /// /// [Discord docs](https://discord.com/developers/docs/interactions/application-commands#application-command-object-application-command-structure). +#[bool_to_bitflags::bool_to_bitflags] #[cfg_attr(feature = "typesize", derive(typesize::derive::TypeSize))] -#[derive(Clone, Debug, Deserialize, Serialize)] +#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)] #[non_exhaustive] pub struct Command { /// The command Id. @@ -235,8 +236,9 @@ enum_number! { /// The parameters for an [`Command`]. /// /// [Discord docs](https://discord.com/developers/docs/interactions/application-commands#application-command-object-application-command-option-structure). +#[bool_to_bitflags::bool_to_bitflags] #[cfg_attr(feature = "typesize", derive(typesize::derive::TypeSize))] -#[derive(Clone, Debug, Deserialize, Serialize)] +#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)] #[non_exhaustive] pub struct CommandOption { /// The option type. diff --git a/src/model/channel/message.rs b/src/model/channel/message.rs index d9d9c9b32ff..657fab127b6 100644 --- a/src/model/channel/message.rs +++ b/src/model/channel/message.rs @@ -31,8 +31,9 @@ use crate::utils; /// /// [Discord docs](https://discord.com/developers/docs/resources/channel#message-object) with some /// [extra fields](https://discord.com/developers/docs/topics/gateway-events#message-create-message-create-extra-fields). +#[bool_to_bitflags::bool_to_bitflags] #[cfg_attr(feature = "typesize", derive(typesize::derive::TypeSize))] -#[derive(Clone, Debug, Default, Deserialize, Serialize)] +#[derive(Clone, Debug, Default, serde::Deserialize, serde::Serialize)] #[non_exhaustive] pub struct Message { /// The unique Id of the message. Can be used to calculate the creation date of the message. diff --git a/src/model/channel/mod.rs b/src/model/channel/mod.rs index e16f0c1b319..d7284011c84 100644 --- a/src/model/channel/mod.rs +++ b/src/model/channel/mod.rs @@ -435,8 +435,9 @@ pub struct StageInstance { /// A thread data. /// /// [Discord docs](https://discord.com/developers/docs/resources/channel#thread-metadata-object). +#[bool_to_bitflags::bool_to_bitflags] #[cfg_attr(feature = "typesize", derive(typesize::derive::TypeSize))] -#[derive(Clone, Copy, Debug, Deserialize, Serialize)] +#[derive(Clone, Copy, Debug, serde::Deserialize, serde::Serialize)] #[non_exhaustive] pub struct ThreadMetadata { /// Whether the thread is archived. diff --git a/src/model/channel/reaction.rs b/src/model/channel/reaction.rs index 3c2c508488e..99d8a65ea3e 100644 --- a/src/model/channel/reaction.rs +++ b/src/model/channel/reaction.rs @@ -402,7 +402,7 @@ impl From for ReactionType { impl From for ReactionType { fn from(emoji: Emoji) -> ReactionType { ReactionType::Custom { - animated: emoji.animated, + animated: emoji.animated(), id: emoji.id, name: Some(emoji.name), } diff --git a/src/model/connection.rs b/src/model/connection.rs index 338e4c03727..fcba6e0f186 100644 --- a/src/model/connection.rs +++ b/src/model/connection.rs @@ -6,7 +6,8 @@ use crate::internal::prelude::*; /// Information about a connection between the current user and a third party service. /// /// [Discord docs](https://discord.com/developers/docs/resources/user#connection-object-connection-structure). -#[derive(Clone, Debug, Deserialize, Serialize)] +#[bool_to_bitflags::bool_to_bitflags] +#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)] #[non_exhaustive] pub struct Connection { /// The ID of the account on the other side of this connection. diff --git a/src/model/event.rs b/src/model/event.rs index bae2803c19b..e0d57fa675e 100644 --- a/src/model/event.rs +++ b/src/model/event.rs @@ -10,6 +10,7 @@ use serde::de::Error as DeError; use serde::Serialize; use crate::constants::Opcode; +use crate::internal::prelude::*; use crate::model::prelude::*; use crate::model::utils::{ deserialize_val, @@ -19,8 +20,6 @@ use crate::model::utils::{ remove_from_map_opt, stickers, }; -use crate::constants::Opcode; -use crate::internal::prelude::*; /// Requires no gateway intents. /// @@ -244,8 +243,9 @@ pub struct GuildMemberRemoveEvent { /// Requires [`GatewayIntents::GUILD_MEMBERS`]. /// /// [Discord docs](https://discord.com/developers/docs/topics/gateway-events#guild-member-update). +#[bool_to_bitflags::bool_to_bitflags] #[cfg_attr(feature = "typesize", derive(typesize::derive::TypeSize))] -#[derive(Clone, Debug, Deserialize, Serialize)] +#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)] #[non_exhaustive] pub struct GuildMemberUpdateEvent { pub guild_id: GuildId, @@ -590,15 +590,15 @@ impl MessageUpdateEvent { if let Some(x) = content { message.content.clone_from(x) } if let Some(x) = timestamp { message.timestamp = x.clone() } message.edited_timestamp = *edited_timestamp; - if let Some(x) = tts { message.tts = x.clone() } - if let Some(x) = mention_everyone { message.mention_everyone = x.clone() } + if let Some(x) = tts { message.set_tts(*x) } + if let Some(x) = mention_everyone { message.set_mention_everyone(*x) } if let Some(x) = mentions { message.mentions.clone_from(x) } if let Some(x) = mention_roles { message.mention_roles.clone_from(x) } if let Some(x) = mention_channels { message.mention_channels.clone_from(x) } if let Some(x) = attachments { message.attachments.clone_from(x) } if let Some(x) = embeds { message.embeds.clone_from(x) } if let Some(x) = reactions { message.reactions.clone_from(x) } - if let Some(x) = pinned { message.pinned = x.clone() } + if let Some(x) = pinned { message.set_pinned(*x) } if let Some(x) = webhook_id { message.webhook_id.clone_from(x) } if let Some(x) = kind { message.kind = x.clone() } if let Some(x) = activity { message.activity.clone_from(x) } diff --git a/src/model/gateway.rs b/src/model/gateway.rs index 3c366438f27..ce30e751a52 100644 --- a/src/model/gateway.rs +++ b/src/model/gateway.rs @@ -240,8 +240,9 @@ pub struct ClientStatus { /// /// [Discord docs](https://discord.com/developers/docs/resources/user#user-object), /// [modification description](https://discord.com/developers/docs/topics/gateway-events#presence-update). +#[bool_to_bitflags::bool_to_bitflags] #[cfg_attr(feature = "typesize", derive(typesize::derive::TypeSize))] -#[derive(Clone, Debug, Default, Deserialize, Serialize)] +#[derive(Clone, Debug, Default, serde::Deserialize, serde::Serialize)] #[non_exhaustive] pub struct PresenceUser { pub id: UserId, @@ -263,9 +264,9 @@ impl PresenceUser { /// If one of [`User`]'s required fields is None in `self`, None is returned. #[must_use] pub fn into_user(self) -> Option { - Some(User { + let (bot, verified, mfa_enabled) = (self.bot()?, self.verified(), self.mfa_enabled()); + let mut user = User { avatar: self.avatar, - bot: self.bot?, discriminator: self.discriminator, global_name: None, id: self.id, @@ -274,14 +275,18 @@ impl PresenceUser { banner: None, accent_colour: None, member: None, - system: false, - mfa_enabled: self.mfa_enabled.unwrap_or_default(), locale: None, - verified: self.verified, email: self.email, flags: self.public_flags.unwrap_or_default(), premium_type: PremiumType::None, - }) + __generated_flags: UserGeneratedFlags::empty(), + }; + + user.set_bot(bot); + user.set_verified(verified); + user.set_mfa_enabled(mfa_enabled.unwrap_or_default()); + + Some(user) } /// Attempts to convert this [`PresenceUser`] instance into a [`User`]. diff --git a/src/model/guild/emoji.rs b/src/model/guild/emoji.rs index 6c827ce74e2..01ea316d6b9 100644 --- a/src/model/guild/emoji.rs +++ b/src/model/guild/emoji.rs @@ -9,8 +9,9 @@ use crate::model::utils::default_true; /// integration. Emojis created using the API only work within the guild it was created in. /// /// [Discord docs](https://discord.com/developers/docs/resources/emoji#emoji-object). +#[bool_to_bitflags::bool_to_bitflags] #[cfg_attr(feature = "typesize", derive(typesize::derive::TypeSize))] -#[derive(Clone, Debug, Deserialize, Serialize)] +#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)] #[non_exhaustive] pub struct Emoji { /// Whether the emoji is animated. @@ -62,7 +63,7 @@ impl Emoji { #[inline] #[must_use] pub fn url(&self) -> String { - let extension = if self.animated { "gif" } else { "png" }; + let extension = if self.animated() { "gif" } else { "png" }; cdn!("/emojis/{}.{}", self.id, extension) } } @@ -73,7 +74,7 @@ impl fmt::Display for Emoji { /// This is in the format of either `<:NAME:EMOJI_ID>` for normal emojis, or /// `` for animated emojis. fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - if self.animated { + if self.animated() { f.write_str(" for Member { fn from(partial: PartialMember) -> Self { - Member { + let (pending, deaf, mute) = (partial.pending(), partial.deaf(), partial.mute()); + let mut member = Member { + __generated_flags: MemberGeneratedFlags::empty(), user: partial.user.unwrap_or_default(), nick: partial.nick, avatar: None, roles: partial.roles, joined_at: partial.joined_at, premium_since: partial.premium_since, - deaf: partial.deaf, - mute: partial.mute, flags: GuildMemberFlags::default(), - pending: partial.pending, permissions: partial.permissions, communication_disabled_until: None, guild_id: partial.guild_id.unwrap_or_default(), unusual_dm_activity_until: partial.unusual_dm_activity_until, - } + }; + + member.set_pending(pending); + member.set_deaf(deaf); + member.set_mute(mute); + member } } impl From for PartialMember { fn from(member: Member) -> Self { - PartialMember { - deaf: member.deaf, + let (pending, deaf, mute) = (member.pending(), member.deaf(), member.mute()); + let mut partial = PartialMember { + __generated_flags: PartialMemberGeneratedFlags::empty(), joined_at: member.joined_at, - mute: member.mute, nick: member.nick, roles: member.roles, - pending: member.pending, premium_since: member.premium_since, guild_id: Some(member.guild_id), user: Some(member.user), permissions: member.permissions, unusual_dm_activity_until: member.unusual_dm_activity_until, - } + }; + + partial.set_deaf(deaf); + partial.set_mute(mute); + partial.set_pending(pending); + partial } } diff --git a/src/model/guild/mod.rs b/src/model/guild/mod.rs index 27eb34df05c..392565de428 100644 --- a/src/model/guild/mod.rs +++ b/src/model/guild/mod.rs @@ -90,8 +90,9 @@ pub struct AfkMetadata { /// /// [Discord docs](https://discord.com/developers/docs/resources/guild#guild-object) plus /// [extension](https://discord.com/developers/docs/topics/gateway-events#guild-create). +#[bool_to_bitflags::bool_to_bitflags] #[cfg_attr(feature = "typesize", derive(typesize::derive::TypeSize))] -#[derive(Clone, Debug, Default, Deserialize, Serialize)] +#[derive(Clone, Debug, Default, serde::Deserialize, serde::Serialize)] #[non_exhaustive] pub struct Guild { /// The unique Id identifying the guild. diff --git a/src/model/guild/partial_guild.rs b/src/model/guild/partial_guild.rs index aeec8fc69a8..ee215e9d710 100644 --- a/src/model/guild/partial_guild.rs +++ b/src/model/guild/partial_guild.rs @@ -31,8 +31,9 @@ use crate::model::utils::{emojis, roles, stickers}; /// Partial information about a [`Guild`]. This does not include information like member data. /// /// [Discord docs](https://discord.com/developers/docs/resources/guild#guild-object). +#[bool_to_bitflags::bool_to_bitflags] #[cfg_attr(feature = "typesize", derive(typesize::derive::TypeSize))] -#[derive(Clone, Debug, Deserialize, Serialize)] +#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)] #[serde(remote = "Self")] #[non_exhaustive] pub struct PartialGuild { @@ -1522,7 +1523,7 @@ impl PartialGuild { } // Manual impl needed to insert guild_id into Role's -impl<'de> Deserialize<'de> for PartialGuild { +impl<'de> Deserialize<'de> for PartialGuildGeneratedOriginal { fn deserialize>(deserializer: D) -> StdResult { let mut guild = Self::deserialize(deserializer)?; // calls #[serde(remote)]-generated inherent method guild.roles.values_mut().for_each(|r| r.guild_id = guild.id); @@ -1530,7 +1531,7 @@ impl<'de> Deserialize<'de> for PartialGuild { } } -impl Serialize for PartialGuild { +impl Serialize for PartialGuildGeneratedOriginal { fn serialize(&self, serializer: S) -> StdResult { Self::serialize(self, serializer) // calls #[serde(remote)]-generated inherent method } @@ -1539,12 +1540,15 @@ impl Serialize for PartialGuild { impl From for PartialGuild { /// Converts this [`Guild`] instance into a [`PartialGuild`] fn from(guild: Guild) -> Self { - Self { + let (premium_progress_bar_enabled, widget_enabled) = + (guild.premium_progress_bar_enabled(), guild.widget_enabled()); + + let mut partial = Self { + __generated_flags: PartialGuildGeneratedFlags::empty(), application_id: guild.application_id, id: guild.id, afk_metadata: guild.afk_metadata, default_message_notifications: guild.default_message_notifications, - widget_enabled: guild.widget_enabled, widget_channel_id: guild.widget_channel_id, emojis: guild.emojis, features: guild.features, @@ -1577,7 +1581,9 @@ impl From for PartialGuild { explicit_content_filter: guild.explicit_content_filter, preferred_locale: guild.preferred_locale, max_stage_video_channel_users: guild.max_stage_video_channel_users, - premium_progress_bar_enabled: guild.premium_progress_bar_enabled, - } + }; + partial.set_premium_progress_bar_enabled(premium_progress_bar_enabled); + partial.set_widget_enabled(widget_enabled); + partial } } diff --git a/src/model/guild/role.rs b/src/model/guild/role.rs index 1d18991a7a5..08743cd613b 100644 --- a/src/model/guild/role.rs +++ b/src/model/guild/role.rs @@ -16,8 +16,9 @@ use crate::model::utils::is_false; /// permissions. /// /// [Discord docs](https://discord.com/developers/docs/topics/permissions#role-object). +#[bool_to_bitflags::bool_to_bitflags] #[cfg_attr(feature = "typesize", derive(typesize::derive::TypeSize))] -#[derive(Clone, Debug, Default, Deserialize, Serialize)] +#[derive(Clone, Debug, Default, serde::Deserialize, serde::Serialize)] #[non_exhaustive] pub struct Role { /// The Id of the role. Can be used to calculate the role's creation date. @@ -174,7 +175,8 @@ impl<'a> From<&'a Role> for RoleId { /// The tags of a [`Role`]. /// /// [Discord docs](https://discord.com/developers/docs/topics/permissions#role-object-role-tags-structure). -#[derive(Clone, Debug, Default, Eq, PartialEq, Deserialize, Serialize)] +#[bool_to_bitflags::bool_to_bitflags] +#[derive(Clone, Debug, Default, Eq, PartialEq, serde::Deserialize, serde::Serialize)] #[cfg_attr(feature = "typesize", derive(typesize::derive::TypeSize))] #[non_exhaustive] pub struct RoleTags { @@ -238,14 +240,8 @@ mod tests { #[test] fn premium_subscriber_role_serde() { - let value = RoleTags { - bot_id: None, - integration_id: None, - premium_subscriber: true, - subscription_listing_id: None, - available_for_purchase: false, - guild_connections: false, - }; + let mut value = RoleTags::default(); + value.set_premium_subscriber(true); assert_json( &value, @@ -255,14 +251,7 @@ mod tests { #[test] fn non_premium_subscriber_role_serde() { - let value = RoleTags { - bot_id: None, - integration_id: None, - premium_subscriber: false, - subscription_listing_id: None, - available_for_purchase: false, - guild_connections: false, - }; + let value = RoleTags::default(); assert_json( &value, diff --git a/src/model/mention.rs b/src/model/mention.rs index 543f64b72aa..0011352d07c 100644 --- a/src/model/mention.rs +++ b/src/model/mention.rs @@ -188,14 +188,11 @@ mod test { #[test] fn test_mention() { + let role = Role::default(); let channel = Channel::Guild(GuildChannel { id: ChannelId::new(4), ..Default::default() }); - let role = Role { - id: RoleId::new(2), - ..Default::default() - }; let user = User { id: UserId::new(6), ..Default::default() @@ -209,8 +206,8 @@ mod test { #[cfg(feature = "model")] assert_eq!(channel.mention().to_string(), "<#4>"); assert_eq!(member.mention().to_string(), "<@6>"); - assert_eq!(role.mention().to_string(), "<@&2>"); - assert_eq!(role.id.mention().to_string(), "<@&2>"); + assert_eq!(role.mention().to_string(), "<@&1>"); + assert_eq!(role.id.mention().to_string(), "<@&1>"); assert_eq!(user.mention().to_string(), "<@6>"); assert_eq!(user.id.mention().to_string(), "<@6>"); } diff --git a/src/model/user.rs b/src/model/user.rs index cc444b52948..172adcfa554 100644 --- a/src/model/user.rs +++ b/src/model/user.rs @@ -221,8 +221,9 @@ impl OnlineStatus { /// /// [Discord docs](https://discord.com/developers/docs/resources/user#user-object), existence of /// additional partial member field documented [here](https://discord.com/developers/docs/topics/gateway-events#message-create). +#[bool_to_bitflags::bool_to_bitflags] #[cfg_attr(feature = "typesize", derive(typesize::derive::TypeSize))] -#[derive(Clone, Debug, Default, Deserialize, Serialize)] +#[derive(Clone, Debug, Default, serde::Deserialize, serde::Serialize)] #[non_exhaustive] pub struct User { /// The unique Id of the user. Can be used to calculate the account's creation date. @@ -401,7 +402,7 @@ impl User { /// See [`UserId::create_dm_channel`] for what errors may be returned. #[inline] pub async fn create_dm_channel(&self, cache_http: impl CacheHttp) -> Result { - if self.bot { + if self.bot() { return Err(Error::Model(ModelError::MessagingBot)); } diff --git a/src/model/voice.rs b/src/model/voice.rs index a219702ade3..d258d479346 100644 --- a/src/model/voice.rs +++ b/src/model/voice.rs @@ -11,7 +11,8 @@ use crate::model::Timestamp; /// Information about an available voice region. /// /// [Discord docs](https://discord.com/developers/docs/resources/voice#voice-region-object). -#[derive(Clone, Debug, Deserialize, Serialize)] +#[bool_to_bitflags::bool_to_bitflags] +#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)] #[non_exhaustive] pub struct VoiceRegion { /// Whether it is a custom voice region, which is used for events. @@ -29,8 +30,9 @@ pub struct VoiceRegion { /// A user's state within a voice channel. /// /// [Discord docs](https://discord.com/developers/docs/resources/voice#voice-state-object). +#[bool_to_bitflags::bool_to_bitflags] #[cfg_attr(feature = "typesize", derive(typesize::derive::TypeSize))] -#[derive(Clone, Debug, Deserialize, Serialize)] +#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)] #[serde(remote = "Self")] #[non_exhaustive] pub struct VoiceState { @@ -53,7 +55,7 @@ pub struct VoiceState { } // Manual impl needed to insert guild_id into Member -impl<'de> Deserialize<'de> for VoiceState { +impl<'de> Deserialize<'de> for VoiceStateGeneratedOriginal { fn deserialize>(deserializer: D) -> Result { // calls #[serde(remote)]-generated inherent method let mut state = Self::deserialize(deserializer)?; @@ -64,7 +66,7 @@ impl<'de> Deserialize<'de> for VoiceState { } } -impl Serialize for VoiceState { +impl Serialize for VoiceStateGeneratedOriginal { fn serialize(&self, serializer: S) -> Result { // calls #[serde(remote)]-generated inherent method Self::serialize(self, serializer) diff --git a/src/utils/content_safe.rs b/src/utils/content_safe.rs index c87da4140f0..031eae4910b 100644 --- a/src/utils/content_safe.rs +++ b/src/utils/content_safe.rs @@ -5,77 +5,42 @@ use crate::model::mention::Mention; use crate::model::user::User; /// Struct that allows to alter [`content_safe`]'s behaviour. -#[derive(Clone, Debug)] +#[bool_to_bitflags::bool_to_bitflags( + getter_prefix = "get_", + setter_prefix = "", + private_getters, + document_setters, + owning_setters +)] +#[derive(Copy, Clone, Debug)] pub struct ContentSafeOptions { - clean_role: bool, - clean_user: bool, - clean_channel: bool, - clean_here: bool, - clean_everyone: bool, - show_discriminator: bool, -} - -impl ContentSafeOptions { - #[must_use] - pub fn new() -> Self { - ContentSafeOptions::default() - } - /// [`content_safe`] will replace role mentions (`<@&{id}>`) with its name prefixed with `@` /// (`@rolename`) or with `@deleted-role` if the identifier is invalid. - #[must_use] - pub fn clean_role(mut self, b: bool) -> Self { - self.clean_role = b; - - self - } - + pub clean_role: bool, /// If set to true, [`content_safe`] will replace user mentions (`<@!{id}>` or `<@{id}>`) with /// the user's name prefixed with `@` (`@username`) or with `@invalid-user` if the identifier /// is invalid. - #[must_use] - pub fn clean_user(mut self, b: bool) -> Self { - self.clean_user = b; - - self - } - + pub clean_user: bool, /// If set to true, [`content_safe`] will replace channel mentions (`<#{id}>`) with the /// channel's name prefixed with `#` (`#channelname`) or with `#deleted-channel` if the /// identifier is invalid. - #[must_use] - pub fn clean_channel(mut self, b: bool) -> Self { - self.clean_channel = b; - - self - } - + pub clean_channel: bool, + /// If set, [`content_safe`] will replace `@here` with a non-pinging alternative. + pub clean_here: bool, + /// If set, [`content_safe`] will replace `@everyone` with a non-pinging alternative. + pub clean_everyone: bool, /// If set to true, if [`content_safe`] replaces a user mention it will add their four digit /// discriminator with a preceding `#`, turning `@username` to `@username#discriminator`. /// /// This option is ignored if the username is a next-gen username, and /// therefore does not have a discriminator. - #[must_use] - pub fn show_discriminator(mut self, b: bool) -> Self { - self.show_discriminator = b; - - self - } - - /// If set, [`content_safe`] will replace `@here` with a non-pinging alternative. - #[must_use] - pub fn clean_here(mut self, b: bool) -> Self { - self.clean_here = b; - - self - } + pub show_discriminator: bool, +} - /// If set, [`content_safe`] will replace `@everyone` with a non-pinging alternative. +impl ContentSafeOptions { #[must_use] - pub fn clean_everyone(mut self, b: bool) -> Self { - self.clean_everyone = b; - - self + pub fn new() -> Self { + ContentSafeOptions::default() } } @@ -83,12 +48,7 @@ impl Default for ContentSafeOptions { /// Instantiates with all options set to `true`. fn default() -> Self { ContentSafeOptions { - clean_role: true, - clean_user: true, - clean_channel: true, - clean_here: true, - clean_everyone: true, - show_discriminator: true, + __generated_flags: ContentSafeOptionsGeneratedFlags::all(), } } } @@ -109,7 +69,7 @@ impl Default for ContentSafeOptions { /// use serenity::utils::{content_safe, ContentSafeOptions}; /// /// let with_mention = "@everyone"; -/// let without_mention = content_safe(&guild, &with_mention, &ContentSafeOptions::default(), &[]); +/// let without_mention = content_safe(&guild, &with_mention, ContentSafeOptions::default(), &[]); /// /// assert_eq!("@\u{200B}everyone", without_mention); /// ``` @@ -123,12 +83,7 @@ impl Default for ContentSafeOptions { /// /// fn filter_message(cache: &Cache, message: &Message) -> String { /// if let Some(guild) = message.guild(cache) { -/// content_safe( -/// &guild, -/// &message.content, -/// &ContentSafeOptions::default(), -/// &message.mentions, -/// ) +/// content_safe(&guild, &message.content, ContentSafeOptions::default(), &message.mentions) /// } else { /// // We don't need to clean messages in DMs /// message.content.to_string() @@ -138,16 +93,16 @@ impl Default for ContentSafeOptions { pub fn content_safe( guild: &Guild, s: impl AsRef, - options: &ContentSafeOptions, + options: ContentSafeOptions, users: &[User], ) -> String { let mut content = clean_mentions(guild, s, options, users); - if options.clean_here { + if options.get_clean_here() { content = content.replace("@here", "@\u{200B}here"); } - if options.clean_everyone { + if options.get_clean_everyone() { content = content.replace("@everyone", "@\u{200B}everyone"); } @@ -157,7 +112,7 @@ pub fn content_safe( fn clean_mentions( guild: &Guild, s: impl AsRef, - options: &ContentSafeOptions, + options: ContentSafeOptions, users: &[User], ) -> String { let s = s.as_ref(); @@ -177,12 +132,12 @@ fn clean_mentions( let mut chars = mention_str.chars(); chars.next(); let should_parse = match chars.next() { - Some('#') => options.clean_channel, + Some('#') => options.get_clean_channel(), Some('@') => { if let Some('&') = chars.next() { - options.clean_role + options.get_clean_role() } else { - options.clean_user + options.get_clean_user() } }, _ => false, @@ -213,7 +168,7 @@ fn clean_mentions( fn clean_mention( guild: &Guild, mention: Mention, - options: &ContentSafeOptions, + options: ContentSafeOptions, users: &[User], ) -> Cow<'static, str> { match mention { @@ -230,13 +185,13 @@ fn clean_mention( .map_or(Cow::Borrowed("@deleted-role"), |role| format!("@{}", role.name).into()), Mention::User(id) => { if let Some(member) = guild.members.get(&id) { - if options.show_discriminator { + if options.get_show_discriminator() { format!("@{}", member.distinct()).into() } else { format!("@{}", member.display_name()).into() } } else if let Some(user) = users.iter().find(|u| u.id == id) { - if options.show_discriminator { + if options.get_show_discriminator() { format!("@{}", user.tag()).into() } else { format!("@{}", user.name).into() @@ -307,31 +262,32 @@ mod tests { // User mentions let options = ContentSafeOptions::default(); - assert_eq!(without_user_mentions, content_safe(&guild, with_user_mentions, &options, &[])); + assert_eq!(without_user_mentions, content_safe(&guild, with_user_mentions, options, &[])); assert_eq!( "@invalid-user", - content_safe(&no_member_guild, "<@100000000000000001>", &options, &[]) + content_safe(&no_member_guild, "<@100000000000000001>", options, &[]) ); - let options = ContentSafeOptions::default().show_discriminator(false); + let mut options = ContentSafeOptions::default(); + options = options.show_discriminator(false); assert_eq!( format!("@{}", user.name), - content_safe(&no_member_guild, "<@!100000000000000000>", &options, &[user.clone()]) + content_safe(&no_member_guild, "<@!100000000000000000>", options, &[user.clone()]) ); assert_eq!( "@invalid-user", - content_safe(&no_member_guild, "<@!100000000000000000>", &options, &[]) + content_safe(&no_member_guild, "<@!100000000000000000>", options, &[]) ); assert_eq!( format!("@{}", member.nick.as_ref().unwrap()), - content_safe(&guild, "<@100000000000000000>", &options, &[]) + content_safe(&guild, "<@100000000000000000>", options, &[]) ); - let options = options.clean_user(false); - assert_eq!(with_user_mentions, content_safe(&guild, with_user_mentions, &options, &[])); + options = options.clean_user(false); + assert_eq!(with_user_mentions, content_safe(&guild, with_user_mentions, options, &[])); // Channel mentions let with_channel_mentions = "<#> <#deleted-channel> #deleted-channel <#1> \ @@ -344,13 +300,13 @@ mod tests { assert_eq!( without_channel_mentions, - content_safe(&guild, with_channel_mentions, &options, &[]) + content_safe(&guild, with_channel_mentions, options, &[]) ); - let options = options.clean_channel(false); + options = options.clean_channel(false); assert_eq!( with_channel_mentions, - content_safe(&guild, with_channel_mentions, &options, &[]) + content_safe(&guild, with_channel_mentions, options, &[]) ); // Role mentions @@ -362,25 +318,24 @@ mod tests { @ferris-club-member @deleted-role \ <@&111111111111111111111111111111> <@&@deleted-role"; - assert_eq!(without_role_mentions, content_safe(&guild, with_role_mentions, &options, &[])); + assert_eq!(without_role_mentions, content_safe(&guild, with_role_mentions, options, &[])); - let options = options.clean_role(false); - assert_eq!(with_role_mentions, content_safe(&guild, with_role_mentions, &options, &[])); + options = options.clean_role(false); + assert_eq!(with_role_mentions, content_safe(&guild, with_role_mentions, options, &[])); // Everyone mentions let with_everyone_mention = "@everyone"; - let without_everyone_mention = "@\u{200B}everyone"; assert_eq!( without_everyone_mention, - content_safe(&guild, with_everyone_mention, &options, &[]) + content_safe(&guild, with_everyone_mention, options, &[]) ); - let options = options.clean_everyone(false); + options = options.clean_everyone(false); assert_eq!( with_everyone_mention, - content_safe(&guild, with_everyone_mention, &options, &[]) + content_safe(&guild, with_everyone_mention, options, &[]) ); // Here mentions @@ -388,9 +343,9 @@ mod tests { let without_here_mention = "@\u{200B}here"; - assert_eq!(without_here_mention, content_safe(&guild, with_here_mention, &options, &[])); + assert_eq!(without_here_mention, content_safe(&guild, with_here_mention, options, &[])); - let options = options.clean_here(false); - assert_eq!(with_here_mention, content_safe(&guild, with_here_mention, &options, &[])); + options = options.clean_here(false); + assert_eq!(with_here_mention, content_safe(&guild, with_here_mention, options, &[])); } } diff --git a/src/utils/custom_message.rs b/src/utils/custom_message.rs index 0594ad7bf04..d925b500166 100644 --- a/src/utils/custom_message.rs +++ b/src/utils/custom_message.rs @@ -127,7 +127,7 @@ impl CustomMessage { /// If not used, the default value is `false`. #[inline] pub fn mention_everyone(&mut self, mentions: bool) -> &mut Self { - self.msg.mention_everyone = mentions; + self.msg.set_mention_everyone(mentions); self } @@ -157,7 +157,7 @@ impl CustomMessage { /// If not used, the default value is `false`. #[inline] pub fn pinned(&mut self, pinned: bool) -> &mut Self { - self.msg.pinned = pinned; + self.msg.set_pinned(pinned); self } @@ -187,7 +187,7 @@ impl CustomMessage { /// If not used, the default value is `false`. #[inline] pub fn tts(&mut self, tts: bool) -> &mut Self { - self.msg.tts = tts; + self.msg.set_tts(tts); self } diff --git a/src/utils/message_builder.rs b/src/utils/message_builder.rs index cc1f5984d57..943c8e2951a 100644 --- a/src/utils/message_builder.rs +++ b/src/utils/message_builder.rs @@ -983,6 +983,7 @@ pub enum ContentModifier { Spoiler, } +#[bool_to_bitflags::bool_to_bitflags] /// Describes formatting on string content #[derive(Clone, Debug, Default)] pub struct Content { @@ -1049,22 +1050,22 @@ impl Content { pub fn apply(&mut self, modifier: &ContentModifier) { match *modifier { ContentModifier::Italic => { - self.italic = true; + self.set_italic(true); }, ContentModifier::Bold => { - self.bold = true; + self.set_bold(true); }, ContentModifier::Strikethrough => { - self.strikethrough = true; + self.set_strikethrough(true); }, ContentModifier::Code => { - self.code = true; + self.set_code(true); }, ContentModifier::Underline => { - self.underline = true; + self.set_underline(true); }, ContentModifier::Spoiler => { - self.spoiler = true; + self.set_spoiler(true); }, } } @@ -1072,53 +1073,53 @@ impl Content { impl std::fmt::Display for Content { fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - if self.spoiler { + if self.spoiler() { fmt.write_str("||")?; } - if self.bold { + if self.bold() { fmt.write_str("**")?; } - if self.italic { + if self.italic() { fmt.write_char('*')?; } - if self.strikethrough { + if self.strikethrough() { fmt.write_str("~~")?; } - if self.underline { + if self.underline() { fmt.write_str("__")?; } - if self.code { + if self.code() { fmt.write_char('`')?; } fmt.write_str(&self.inner)?; - if self.code { + if self.code() { fmt.write_char('`')?; } - if self.underline { + if self.underline() { fmt.write_str("__")?; } - if self.strikethrough { + if self.strikethrough() { fmt.write_str("~~")?; } - if self.italic { + if self.italic() { fmt.write_char('*')?; } - if self.bold { + if self.bold() { fmt.write_str("**")?; } - if self.spoiler { + if self.spoiler() { fmt.write_str("||")?; } @@ -1195,18 +1196,18 @@ mod test { #[test] fn mentions() { - let content_emoji = MessageBuilder::new() - .emoji(&Emoji { - animated: false, - available: true, - id: EmojiId::new(32), - name: "Rohrkatze".to_string().into(), - managed: false, - require_colons: true, - roles: vec![].into(), - user: None, - }) - .build(); + let mut emoji = Emoji { + id: EmojiId::new(32), + name: "Rohrkatze".to_string().into(), + roles: vec![].into(), + user: None, + __generated_flags: EmojiGeneratedFlags::empty(), + }; + + emoji.set_available(true); + emoji.set_require_colons(true); + + let content_emoji = MessageBuilder::new().emoji(&emoji).build(); let content_mentions = MessageBuilder::new() .channel(ChannelId::new(1)) .mention(&UserId::new(2))