tulpje-cache 0.4.0

A redis cache for twilight
Documentation
use serde::{Deserialize, Serialize};
use twilight_model::{
    guild::Emoji,
    id::{
        Id,
        marker::{EmojiMarker, GuildMarker, RoleMarker, UserMarker},
    },
};

use crate::{Cache, Error, GuildResource};

#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
pub struct CachedEmoji {
    pub animated: bool,
    pub available: bool,
    pub id: Id<EmojiMarker>,
    pub managed: bool,
    pub name: String,
    pub require_colons: bool,
    pub roles: Vec<Id<RoleMarker>>,
    pub user_id: Option<Id<UserMarker>>,
}

impl From<Emoji> for CachedEmoji {
    fn from(emoji: Emoji) -> Self {
        let Emoji {
            animated,
            available,
            id,
            managed,
            name,
            require_colons,
            roles,
            user,
        } = emoji;

        Self {
            animated,
            available,
            id,
            managed,
            name,
            require_colons,
            roles,
            user_id: user.map(|user| user.id),
        }
    }
}

impl PartialEq<Emoji> for CachedEmoji {
    fn eq(&self, other: &Emoji) -> bool {
        self.id == other.id
            && self.animated == other.animated
            && self.managed == other.managed
            && self.name == other.name
            && self.require_colons == other.require_colons
            && self.roles == other.roles
            && self.user_id == other.user.as_ref().map(|user| user.id)
            && self.available == other.available
    }
}

impl Cache {
    pub(crate) async fn cache_emojis(
        &self,
        guild_id: Id<GuildMarker>,
        emojis: Vec<Emoji>,
    ) -> Result<(), Error> {
        let guild_emojis = self.guild_emojis.members(&guild_id).await?;
        if !guild_emojis.is_empty() {
            let incoming: Vec<_> = emojis.iter().map(|e| e.id).collect();

            let removal_filter: Vec<_> = guild_emojis
                .iter()
                .copied()
                .filter(|e| !incoming.contains(e))
                .collect();

            self.guild_emojis
                .remove_multi(&guild_id, &removal_filter)
                .await?;

            self.emojis.remove_multi(&removal_filter).await?;
        }

        for emoji in &emojis {
            self.cache_emoji(guild_id, emoji).await?;
        }

        Ok(())
    }

    pub(crate) async fn cache_emoji(
        &self,
        guild_id: Id<GuildMarker>,
        emoji: &Emoji,
    ) -> Result<(), Error> {
        if let Some(user) = emoji.user.as_ref() {
            self.cache_user(user, Some(guild_id)).await?;
        }

        if let Some(cached_emoji) = self.emojis.get(&emoji.id).await?
            && cached_emoji.value == *emoji
        {
            return Ok(());
        }

        let emoji_id = emoji.id;
        let cached = CachedEmoji::from(emoji.clone());

        self.emojis
            .insert(
                &emoji_id,
                &GuildResource {
                    guild_id,
                    value: cached,
                },
            )
            .await?;

        self.guild_emojis.insert(&guild_id, &emoji_id).await?;

        Ok(())
    }
}