use crate::{
cache::Cache,
client::DiscordHttpClient,
error::Result,
route::Route,
types::{Channel, ChannelId, Guild, GuildId, User, UserId, UserProfile},
};
#[allow(async_fn_in_trait)]
pub trait CacheHttp {
fn http(&self) -> &DiscordHttpClient;
fn cache(&self) -> Option<&Cache> {
None
}
async fn guild(&self, guild_id: &GuildId) -> Result<Guild> {
if let Some(guild) = self.cache().and_then(|c| c.guild(&guild_id.to_string())) {
return Ok(guild);
}
self.http().get(Route::GetGuild { guild_id: guild_id.get(), with_counts: false }).await
}
async fn channel(&self, channel_id: &ChannelId) -> Result<Channel> {
self.http().get(Route::GetChannel { channel_id: channel_id.get() }).await
}
async fn user(&self, user_id: &UserId) -> Result<User> {
if let Some(user) = self.cache().and_then(|c| c.user(&user_id.to_string())) {
return Ok(user);
}
let profile: UserProfile = self.http().get(Route::GetUserProfile { user_id: user_id.get(), guild_id: None }).await?;
Ok(profile.user)
}
}
impl CacheHttp for DiscordHttpClient {
fn http(&self) -> &DiscordHttpClient {
self
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::cache::{Cache, CacheSettings};
struct FakeCtx {
http: DiscordHttpClient,
cache: Cache,
}
impl CacheHttp for FakeCtx {
fn http(&self) -> &DiscordHttpClient {
&self.http
}
fn cache(&self) -> Option<&Cache> {
Some(&self.cache)
}
}
fn fake_guild(id: &str) -> Guild {
serde_json::from_value(serde_json::json!({
"id": id, "name": "Cached Guild", "icon": null,
"splash": null, "banner": null, "description": null,
"owner_id": null, "member_count": null,
"premium_subscription_count": 0, "premium_tier": 0,
"verification_level": 0, "nsfw_level": 0, "nsfw": false,
"features": [], "roles": [], "channels": [], "emojis": [],
"stickers": [], "joined_at": null, "large": false, "lazy": false
}))
.unwrap()
}
fn fake_user(id: &str) -> User {
serde_json::from_value(serde_json::json!({
"id": id, "username": "cached_user",
"discriminator": "0000", "avatar": null
}))
.unwrap()
}
fn make_ctx(settings: CacheSettings) -> FakeCtx {
FakeCtx { http: DiscordHttpClient::new("fake_token", None, false), cache: Cache::with_settings(settings) }
}
#[tokio::test]
async fn guild_returns_cached_value() {
let ctx = make_ctx(CacheSettings::default());
let guild = fake_guild("111222333444555666");
ctx.cache.upsert_guild(guild);
let id: GuildId = "111222333444555666".parse().unwrap();
let result = ctx.guild(&id).await.expect("cache hit should succeed");
assert_eq!(result.id, "111222333444555666");
assert_eq!(result.name.as_deref(), Some("Cached Guild"));
}
#[tokio::test]
async fn user_returns_cached_value() {
let ctx = make_ctx(CacheSettings::default());
ctx.cache.upsert_user(fake_user("999888777666555444"));
let id: UserId = "999888777666555444".parse().unwrap();
let result = ctx.user(&id).await.expect("cache hit should succeed");
assert_eq!(result.username, "cached_user");
}
}