Skip to content

Commit

Permalink
Remove typemap without making everything generic (serenity-rs#2720)
Browse files Browse the repository at this point in the history
Replaces the typemap with `dyn Any`, which is what typemap used
internally anyway. This gives the user more control over locking,
removes boilerplate, and is less confusing for beginners to learn.
  • Loading branch information
GnomedDev committed Apr 1, 2024
1 parent 79ebe9f commit bcf4de6
Show file tree
Hide file tree
Showing 13 changed files with 118 additions and 280 deletions.
3 changes: 1 addition & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,6 @@ flate2 = { version = "1.0.28", optional = true }
reqwest = { version = "0.11.22", default-features = false, features = ["multipart", "stream"], optional = true }
static_assertions = { version = "1.1.0", optional = true }
tokio-tungstenite = { version = "0.21.0", optional = true }
typemap_rev = { version = "0.3.0", optional = true }
bytes = { version = "1.5.0", optional = true }
percent-encoding = { version = "2.3.0", optional = true }
mini-moka = { version = "0.10.2", optional = true }
Expand Down Expand Up @@ -92,7 +91,7 @@ cache = ["fxhash", "dashmap", "parking_lot"]
collector = ["gateway", "model"]
# Wraps the gateway and http functionality into a single interface
# TODO: should this require "gateway"?
client = ["http", "typemap_rev"]
client = ["http"]
# Enables the Framework trait which is an abstraction for old-style text commands.
framework = ["client", "model", "utils"]
# Enables gateway support, which allows bots to listen for Discord events.
Expand Down
3 changes: 3 additions & 0 deletions examples/e05_command_framework/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@ version = "0.1.0"
authors = ["my name <my@email.address>"]
edition = "2018"

[dependencies]
dashmap = "5"

[dependencies.serenity]
features = ["framework", "standard_framework", "rustls_backend"]
path = "../../"
Expand Down
55 changes: 21 additions & 34 deletions examples/e05_command_framework/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,10 @@
//! ```
#![allow(deprecated)] // We recommend migrating to poise, instead of using the standard command framework.
use std::borrow::Cow;
use std::collections::{HashMap, HashSet};
use std::collections::HashSet;
use std::env;
use std::fmt::Write;
use std::sync::Arc;
use std::sync::{Arc, OnceLock};

use serenity::async_trait;
use serenity::builder::EditChannel;
Expand Down Expand Up @@ -43,16 +43,9 @@ use serenity::utils::{content_safe, ContentSafeOptions};
// A container type is created for inserting into the Client's `data`, which allows for data to be
// accessible across all events and framework commands, or anywhere else that has a copy of the
// `data` Arc.
struct ShardManagerContainer;

impl TypeMapKey for ShardManagerContainer {
type Value = Arc<ShardManager>;
}

struct CommandCounter;

impl TypeMapKey for CommandCounter {
type Value = HashMap<String, u64>;
struct UserData {
command_counter: dashmap::DashMap<String, u64>,
shard_manager: OnceLock<Arc<ShardManager>>,
}

struct Handler;
Expand Down Expand Up @@ -144,9 +137,8 @@ async fn before(ctx: &Context, msg: &Message, command_name: &str) -> bool {

// Increment the number of times this command has been run once. If the command's name does not
// exist in the counter, add a default value of 0.
let mut data = ctx.data.write().await;
let counter = data.get_mut::<CommandCounter>().expect("Expected CommandCounter in TypeMap.");
let entry = counter.entry(command_name.to_string()).or_insert(0);
let data = ctx.data::<UserData>();
let mut entry = data.command_counter.entry(command_name.to_string()).or_insert(0);
*entry += 1;

true // if `before` returns false, command processing doesn't happen.
Expand Down Expand Up @@ -295,6 +287,11 @@ async fn main() {
.owners(owners),
);

let data = UserData {
command_counter: dashmap::DashMap::new(),
shard_manager: OnceLock::new(),
};

// For this example to run properly, the "Presence Intent" and "Server Members Intent" options
// need to be enabled.
// These are needed so the `required_permissions` macro works on the commands that need to use
Expand All @@ -305,13 +302,13 @@ async fn main() {
let mut client = Client::builder(&token, intents)
.event_handler(Handler)
.framework(framework)
.type_map_insert::<CommandCounter>(HashMap::default())
.data(Arc::new(data) as _)
.await
.expect("Err creating client");

{
let mut data = client.data.write().await;
data.insert::<ShardManagerContainer>(Arc::clone(&client.shard_manager));
let data = client.data::<UserData>();
let _ = data.shard_manager.set(Arc::clone(&client.shard_manager));
}

if let Err(why) = client.start().await {
Expand All @@ -327,10 +324,9 @@ async fn main() {
async fn commands(ctx: &Context, msg: &Message) -> CommandResult {
let mut contents = "Commands used:\n".to_string();

let data = ctx.data.read().await;
let counter = data.get::<CommandCounter>().expect("Expected CommandCounter in TypeMap.");

for (name, amount) in counter {
let counter = &ctx.data::<UserData>().command_counter;
for entry in counter {
let (name, amount) = entry.pair();
writeln!(contents, "- {name}: {amount}")?;
}

Expand Down Expand Up @@ -448,17 +444,8 @@ async fn about(ctx: &Context, msg: &Message) -> CommandResult {
async fn latency(ctx: &Context, msg: &Message) -> CommandResult {
// The shard manager is an interface for mutating, stopping, restarting, and retrieving
// information about shards.
let data = ctx.data.read().await;

let shard_manager = match data.get::<ShardManagerContainer>() {
Some(v) => v,
None => {
msg.reply(ctx, "There was a problem getting the shard manager").await?;

return Ok(());
},
};

let data = ctx.data::<UserData>();
let shard_manager = data.shard_manager.get().expect("should be init before startup");
let runners = shard_manager.runners.lock().await;

// Shards are backed by a "shard runner" responsible for processing events over the shard, so
Expand All @@ -472,7 +459,7 @@ async fn latency(ctx: &Context, msg: &Message) -> CommandResult {
},
};

msg.reply(ctx, &format!("The shard latency is {:?}", runner.latency)).await?;
msg.reply(ctx, format!("The shard latency is {:?}", runner.latency)).await?;

Ok(())
}
Expand Down
15 changes: 5 additions & 10 deletions examples/e06_sample_bot_structure/src/commands/owner.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,21 +3,16 @@ use serenity::framework::standard::CommandResult;
use serenity::model::prelude::*;
use serenity::prelude::*;

use crate::ShardManagerContainer;
use crate::UserData;

#[command]
#[owners_only]
async fn quit(ctx: &Context, msg: &Message) -> CommandResult {
let data = ctx.data.read().await;
let data = ctx.data::<UserData>();
let shard_manager = data.shard_manager.get().expect("should be init before startup");

if let Some(manager) = data.get::<ShardManagerContainer>() {
msg.reply(ctx, "Shutting down!").await?;
manager.shutdown_all().await;
} else {
msg.reply(ctx, "There was a problem getting the shard manager").await?;

return Ok(());
}
msg.reply(ctx, "Shutting down!").await?;
shard_manager.shutdown_all().await;

Ok(())
}
17 changes: 10 additions & 7 deletions examples/e06_sample_bot_structure/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ mod commands;

use std::collections::HashSet;
use std::env;
use std::sync::Arc;
use std::sync::{Arc, OnceLock};

use serenity::async_trait;
use serenity::framework::standard::macros::group;
Expand All @@ -29,10 +29,8 @@ use crate::commands::math::*;
use crate::commands::meta::*;
use crate::commands::owner::*;

pub struct ShardManagerContainer;

impl TypeMapKey for ShardManagerContainer {
type Value = Arc<ShardManager>;
struct UserData {
shard_manager: OnceLock<Arc<ShardManager>>,
}

struct Handler;
Expand Down Expand Up @@ -84,18 +82,23 @@ async fn main() {
let framework = StandardFramework::new().group(&GENERAL_GROUP);
framework.configure(Configuration::new().owners(owners).prefix("~"));

let user_data = UserData {
shard_manager: OnceLock::new(),
};

let intents = GatewayIntents::GUILD_MESSAGES
| GatewayIntents::DIRECT_MESSAGES
| GatewayIntents::MESSAGE_CONTENT;
let mut client = Client::builder(&token, intents)
.framework(framework)
.event_handler(Handler)
.data(Arc::new(user_data) as _)
.await
.expect("Err creating client");

{
let mut data = client.data.write().await;
data.insert::<ShardManagerContainer>(client.shard_manager.clone());
let data = client.data::<UserData>();
let _ = data.shard_manager.set(client.shard_manager.clone());
}

let shard_manager = client.shard_manager.clone();
Expand Down
1 change: 1 addition & 0 deletions examples/e12_global_data/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,4 @@ edition = "2018"
[dependencies]
serenity = { path = "../../" }
tokio = { version = "1.0", features = ["macros", "rt-multi-thread"] }
dashmap = "5"
Loading

0 comments on commit bcf4de6

Please sign in to comment.