Skip to content

Commit 9f7d223

Browse files
committed
Bitpack boolean fields using fancy proc macro (#2673)
This uses the `bool_to_bitflags` macro to remove boolean (and optional boolean) fields from structs and pack them into a bitflags invocation, so a struct with many bools will only use one or two bytes, instead of a byte per bool as is. This requires using getters and setters for the boolean fields, which changes user experience and is hard to document, which is a significant downside, but is such a nice change and will just become more and more efficient as time goes on.
1 parent 327f318 commit 9f7d223

File tree

29 files changed

+260
-308
lines changed

29 files changed

+260
-308
lines changed

Cargo.toml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ secrecy = { version = "0.8.0", features = ["serde"] }
3232
arrayvec = { version = "0.7.4", features = ["serde"] }
3333
serde_cow = { version = "0.1.0" }
3434
small-fixed-array = { git = "https://github.com/GnomedDev/small-fixed-array", features = ["serde", "log_using_tracing"] }
35+
bool_to_bitflags = { git = "https://github.com/GnomedDev/bool-to-bitflags", version = "0.1.0" }
3536
# Optional dependencies
3637
fxhash = { version = "0.2.1", optional = true }
3738
simd-json = { version = "0.13.4", optional = true }
@@ -126,7 +127,7 @@ simd_json = ["simd-json", "typesize?/simd_json"]
126127
# Enables temporary caching in functions that retrieve data via the HTTP API.
127128
temp_cache = ["cache", "mini-moka", "typesize?/mini_moka"]
128129

129-
typesize = ["dep:typesize", "small-fixed-array/typesize"]
130+
typesize = ["dep:typesize", "small-fixed-array/typesize", "bool_to_bitflags/typesize"]
130131

131132
# Removed feature (https://github.com/serenity-rs/serenity/pull/2246)
132133
absolute_ratelimits = []

examples/e05_command_framework/src/main.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -349,10 +349,10 @@ async fn say(ctx: &Context, msg: &Message, mut args: Args) -> CommandResult {
349349
if let Some(guild) = msg.guild(&ctx.cache) {
350350
// By default roles, users, and channel mentions are cleaned.
351351
let settings = ContentSafeOptions::default()
352-
// We do not want to clean channal mentions as they do not ping users.
352+
// We do not want to clean channel mentions as they do not ping users.
353353
.clean_channel(false);
354354

355-
x = content_safe(&guild, x, &settings, &msg.mentions);
355+
x = content_safe(&guild, x, settings, &msg.mentions);
356356
}
357357

358358
msg.channel_id.say(&ctx.http, x).await?;

src/builder/create_command.rs

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -30,8 +30,7 @@ impl CreateCommandOption {
3030
name_localizations: None,
3131
description: description.into().into(),
3232
description_localizations: None,
33-
required: false,
34-
autocomplete: false,
33+
__generated_flags: CommandOptionGeneratedFlags::empty(),
3534
min_value: None,
3635
max_value: None,
3736
min_length: None,
@@ -104,7 +103,7 @@ impl CreateCommandOption {
104103
///
105104
/// **Note**: This defaults to `false`.
106105
pub fn required(mut self, required: bool) -> Self {
107-
self.0.required = required;
106+
self.0.set_required(required);
108107
self
109108
}
110109

@@ -203,7 +202,7 @@ impl CreateCommandOption {
203202
/// - May not be set to `true` if `choices` are set
204203
/// - Options using `autocomplete` are not confined to only use given choices
205204
pub fn set_autocomplete(mut self, value: bool) -> Self {
206-
self.0.autocomplete = value;
205+
self.0.set_autocomplete(value);
207206
self
208207
}
209208

src/builder/edit_role.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -77,8 +77,8 @@ impl<'a> EditRole<'a> {
7777
/// Creates a new builder with the values of the given [`Role`].
7878
pub fn from_role(role: &Role) -> Self {
7979
EditRole {
80-
hoist: Some(role.hoist),
81-
mentionable: Some(role.mentionable),
80+
hoist: Some(role.hoist()),
81+
mentionable: Some(role.mentionable()),
8282
name: Some(role.name.clone()),
8383
permissions: Some(role.permissions.bits()),
8484
position: Some(role.position),

src/cache/event.rs

Lines changed: 15 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ use crate::model::event::{
3434
VoiceStateUpdateEvent,
3535
};
3636
use crate::model::gateway::ShardInfo;
37-
use crate::model::guild::{Guild, GuildMemberFlags, Member, Role};
37+
use crate::model::guild::{Guild, GuildMemberFlags, Member, MemberGeneratedFlags, Role};
3838
use crate::model::id::ShardId;
3939
use crate::model::user::{CurrentUser, OnlineStatus};
4040
use crate::model::voice::VoiceState;
@@ -190,36 +190,40 @@ impl CacheUpdate for GuildMemberUpdateEvent {
190190
member.nick.clone_from(&self.nick);
191191
member.roles.clone_from(&self.roles);
192192
member.user.clone_from(&self.user);
193-
member.pending.clone_from(&self.pending);
194193
member.premium_since.clone_from(&self.premium_since);
195-
member.deaf.clone_from(&self.deaf);
196-
member.mute.clone_from(&self.mute);
197194
member.avatar.clone_from(&self.avatar);
198195
member.communication_disabled_until.clone_from(&self.communication_disabled_until);
199196
member.unusual_dm_activity_until.clone_from(&self.unusual_dm_activity_until);
197+
member.set_pending(self.pending());
198+
member.set_deaf(self.deaf());
199+
member.set_mute(self.mute());
200200

201201
item
202202
} else {
203203
None
204204
};
205205

206206
if item.is_none() {
207-
guild.members.insert(self.user.id, Member {
208-
deaf: false,
207+
let mut new_member = Member {
208+
__generated_flags: MemberGeneratedFlags::empty(),
209209
guild_id: self.guild_id,
210210
joined_at: Some(self.joined_at),
211-
mute: false,
212211
nick: self.nick.clone(),
213212
roles: self.roles.clone(),
214213
user: self.user.clone(),
215-
pending: self.pending,
216214
premium_since: self.premium_since,
217215
permissions: None,
218216
avatar: self.avatar,
219217
communication_disabled_until: self.communication_disabled_until,
220218
flags: GuildMemberFlags::default(),
221219
unusual_dm_activity_until: self.unusual_dm_activity_until,
222-
});
220+
};
221+
222+
new_member.set_pending(self.pending());
223+
new_member.set_deaf(self.deaf());
224+
new_member.set_mute(self.mute());
225+
226+
guild.members.insert(self.user.id, new_member);
223227
}
224228

225229
item
@@ -318,7 +322,7 @@ impl CacheUpdate for GuildUpdateEvent {
318322
guild.system_channel_id = self.guild.system_channel_id;
319323
guild.verification_level = self.guild.verification_level;
320324
guild.widget_channel_id = self.guild.widget_channel_id;
321-
guild.widget_enabled = self.guild.widget_enabled;
325+
guild.set_widget_enabled(self.guild.widget_enabled());
322326
}
323327

324328
None
@@ -416,20 +420,18 @@ impl CacheUpdate for PresenceUpdateEvent {
416420
// Create a partial member instance out of the presence update data.
417421
if let Some(user) = self.presence.user.to_user() {
418422
guild.members.entry(self.presence.user.id).or_insert_with(|| Member {
419-
deaf: false,
420423
guild_id,
421424
joined_at: None,
422-
mute: false,
423425
nick: None,
424426
user,
425427
roles: FixedArray::default(),
426-
pending: false,
427428
premium_since: None,
428429
permissions: None,
429430
avatar: None,
430431
communication_disabled_until: None,
431432
flags: GuildMemberFlags::default(),
432433
unusual_dm_activity_until: None,
434+
__generated_flags: MemberGeneratedFlags::empty(),
433435
});
434436
}
435437
}

src/framework/standard/configuration.rs

Lines changed: 44 additions & 70 deletions
Original file line numberDiff line numberDiff line change
@@ -101,24 +101,56 @@ impl From<(bool, bool, bool)> for WithWhiteSpace {
101101
/// [`Client`]: crate::Client
102102
/// [`StandardFramework`]: super::StandardFramework
103103
/// [default implementation]: Self::default
104+
#[bool_to_bitflags::bool_to_bitflags(
105+
getter_prefix = "get_",
106+
setter_prefix = "",
107+
private_getters,
108+
document_setters,
109+
owning_setters
110+
)]
104111
#[derive(Clone)]
105112
pub struct Configuration {
106-
pub(crate) allow_dm: bool,
107113
pub(crate) with_whitespace: WithWhiteSpace,
108-
pub(crate) by_space: bool,
109114
pub(crate) blocked_guilds: HashSet<GuildId>,
110115
pub(crate) blocked_users: HashSet<UserId>,
111116
pub(crate) allowed_channels: HashSet<ChannelId>,
112117
pub(crate) disabled_commands: HashSet<String>,
113118
pub(crate) dynamic_prefixes: Vec<DynamicPrefixHook>,
114-
pub(crate) ignore_bots: bool,
115-
pub(crate) ignore_webhooks: bool,
116119
pub(crate) on_mention: Option<String>,
117120
pub(crate) owners: HashSet<UserId>,
118121
pub(crate) prefixes: Vec<String>,
119-
pub(crate) no_dm_prefix: bool,
120122
pub(crate) delimiters: Vec<Delimiter>,
121-
pub(crate) case_insensitive: bool,
123+
/// If set to false, bot will ignore any private messages.
124+
///
125+
/// **Note**: Defaults to `true`.
126+
pub allow_dm: bool,
127+
/// Whether the framework should split the message by a space first to parse the group or
128+
/// command. If set to false, it will only test part of the message by the *length* of the
129+
/// group's or command's names.
130+
///
131+
/// **Note**: Defaults to `true`
132+
pub by_space: bool,
133+
/// Whether the bot should respond to other bots.
134+
///
135+
/// For example, if this is set to false, then the bot will respond to any other bots including
136+
/// itself.
137+
///
138+
/// **Note**: Defaults to `true`.
139+
pub ignore_bots: bool,
140+
/// If set to true, bot will ignore all commands called by webhooks.
141+
///
142+
/// **Note**: Defaults to `true`.
143+
pub ignore_webhooks: bool,
144+
/// Sets whether command execution can be done without a prefix. Works only in private
145+
/// channels.
146+
///
147+
/// **Note**: Defaults to `false`.
148+
///
149+
/// # Note
150+
///
151+
/// The `cache` feature is required. If disabled this does absolutely nothing.
152+
pub no_dm_prefix: bool,
153+
case_insensitive: bool,
122154
}
123155

124156
impl Configuration {
@@ -128,15 +160,6 @@ impl Configuration {
128160
Self::default()
129161
}
130162

131-
/// If set to false, bot will ignore any private messages.
132-
///
133-
/// **Note**: Defaults to `true`.
134-
#[must_use]
135-
pub fn allow_dm(mut self, allow_dm: bool) -> Self {
136-
self.allow_dm = allow_dm;
137-
self
138-
}
139-
140163
/// Whether to allow whitespace being optional between a prefix/group-prefix/command and a
141164
/// command.
142165
///
@@ -165,17 +188,6 @@ impl Configuration {
165188
self
166189
}
167190

168-
/// Whether the framework should split the message by a space first to parse the group or
169-
/// command. If set to false, it will only test part of the message by the *length* of the
170-
/// group's or command's names.
171-
///
172-
/// **Note**: Defaults to `true`
173-
#[must_use]
174-
pub fn by_space(mut self, b: bool) -> Self {
175-
self.by_space = b;
176-
self
177-
}
178-
179191
/// HashSet of channels Ids where commands will be working.
180192
///
181193
/// **Note**: Defaults to an empty HashSet.
@@ -351,27 +363,6 @@ impl Configuration {
351363
self
352364
}
353365

354-
/// Whether the bot should respond to other bots.
355-
///
356-
/// For example, if this is set to false, then the bot will respond to any other bots including
357-
/// itself.
358-
///
359-
/// **Note**: Defaults to `true`.
360-
#[must_use]
361-
pub fn ignore_bots(mut self, ignore_bots: bool) -> Self {
362-
self.ignore_bots = ignore_bots;
363-
self
364-
}
365-
366-
/// If set to true, bot will ignore all commands called by webhooks.
367-
///
368-
/// **Note**: Defaults to `true`.
369-
#[must_use]
370-
pub fn ignore_webhooks(mut self, ignore_webhooks: bool) -> Self {
371-
self.ignore_webhooks = ignore_webhooks;
372-
self
373-
}
374-
375366
/// Whether or not to respond to commands initiated with `id_to_mention`.
376367
///
377368
/// **Note**: that this can be used in conjunction with [`Self::prefix`].
@@ -485,20 +476,6 @@ impl Configuration {
485476
self
486477
}
487478

488-
/// Sets whether command execution can be done without a prefix. Works only in private channels.
489-
///
490-
/// **Note**: Defaults to `false`.
491-
///
492-
/// # Note
493-
///
494-
/// The `cache` feature is required. If disabled this does absolutely nothing.
495-
#[inline]
496-
#[must_use]
497-
pub fn no_dm_prefix(mut self, b: bool) -> Self {
498-
self.no_dm_prefix = b;
499-
self
500-
}
501-
502479
/// Sets a single delimiter to be used when splitting the content after a command.
503480
///
504481
/// **Note**: Defaults to a vector with a single element of `' '`.
@@ -555,7 +532,7 @@ impl Configuration {
555532
/// **Note**: Defaults to `false`.
556533
#[must_use]
557534
pub fn case_insensitivity(mut self, cs: bool) -> Self {
558-
self.case_insensitive = cs;
535+
self = self.case_insensitive(cs);
559536

560537
for prefix in &mut self.prefixes {
561538
*prefix = prefix.to_lowercase();
@@ -585,23 +562,20 @@ impl Default for Configuration {
585562
/// - **owners** to an empty HashSet
586563
/// - **prefix** to "~"
587564
fn default() -> Configuration {
588-
Configuration {
589-
allow_dm: true,
565+
let config = Configuration {
566+
__generated_flags: ConfigurationGeneratedFlags::empty(),
590567
with_whitespace: WithWhiteSpace::default(),
591-
by_space: true,
592568
blocked_guilds: HashSet::default(),
593569
blocked_users: HashSet::default(),
594570
allowed_channels: HashSet::default(),
595-
case_insensitive: false,
596571
delimiters: vec![Delimiter::Single(' ')],
597572
disabled_commands: HashSet::default(),
598573
dynamic_prefixes: Vec::new(),
599-
ignore_bots: true,
600-
ignore_webhooks: true,
601-
no_dm_prefix: false,
602574
on_mention: None,
603575
owners: HashSet::default(),
604576
prefixes: vec![String::from("~")],
605-
}
577+
};
578+
579+
config.allow_dm(true).by_space(true).ignore_bots(true).ignore_webhooks(true)
606580
}
607581
}

src/framework/standard/mod.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -203,8 +203,8 @@ impl StandardFramework {
203203
fn should_ignore(&self, msg: &Message) -> bool {
204204
let config = self.config.read();
205205

206-
(config.ignore_bots && msg.author.bot)
207-
|| (config.ignore_webhooks && msg.webhook_id.is_some())
206+
(config.get_ignore_bots() && msg.author.bot())
207+
|| (config.get_ignore_webhooks() && msg.webhook_id.is_some())
208208
}
209209

210210
async fn should_fail<'a>(
@@ -637,7 +637,7 @@ impl Framework for StandardFramework {
637637
return;
638638
}
639639

640-
if prefix.is_none() && !(config.no_dm_prefix && msg.is_private()) {
640+
if prefix.is_none() && !(config.get_no_dm_prefix() && msg.is_private()) {
641641
if let Some(normal) = &self.normal_message {
642642
normal(&mut ctx, &msg).await;
643643
}
@@ -684,7 +684,7 @@ impl Framework for StandardFramework {
684684

685685
match invoke {
686686
Invoke::Help(name) => {
687-
if !config.allow_dm && msg.is_private() {
687+
if !config.get_allow_dm() && msg.is_private() {
688688
return;
689689
}
690690

src/framework/standard/parse/map.rs

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,8 +34,11 @@ impl CommandMap {
3434
map.min_length = std::cmp::min(len, map.min_length);
3535
map.max_length = std::cmp::max(len, map.max_length);
3636

37-
let name =
38-
if conf.case_insensitive { name.to_lowercase() } else { (*name).to_string() };
37+
let name = if conf.get_case_insensitive() {
38+
name.to_lowercase()
39+
} else {
40+
(*name).to_string()
41+
};
3942

4043
map.cmds.insert(name, (*cmd, Arc::clone(&sub_map)));
4144
}

0 commit comments

Comments
 (0)