tulpje-cache 0.4.0

A redis cache for twilight
Documentation
use std::collections::HashSet;

use serde::{Deserialize, Serialize};
use twilight_model::{
    channel::message::{
        Sticker,
        sticker::{StickerFormatType, StickerType},
    },
    id::{
        Id,
        marker::{GuildMarker, StickerMarker, StickerPackMarker, UserMarker},
    },
};

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

#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
pub struct CachedSticker {
    available: bool,
    description: String,
    format_type: StickerFormatType,
    guild_id: Option<Id<GuildMarker>>,
    id: Id<StickerMarker>,
    kind: StickerType,
    name: String,
    pack_id: Option<Id<StickerPackMarker>>,
    sort_value: Option<u64>,
    tags: String,
    user_id: Option<Id<UserMarker>>,
}

impl From<Sticker> for CachedSticker {
    fn from(sticker: Sticker) -> Self {
        let Sticker {
            available,
            description,
            format_type,
            guild_id,
            id,
            kind,
            name,
            pack_id,
            sort_value,
            tags,
            user,
        } = sticker;

        Self {
            available,
            description: description.unwrap_or_default(),
            format_type,
            guild_id,
            id,
            kind,
            name,
            pack_id,
            sort_value,
            tags,
            user_id: user.map(|user| user.id),
        }
    }
}

impl PartialEq<Sticker> for CachedSticker {
    fn eq(&self, other: &Sticker) -> bool {
        self.available == other.available
            && self.description.as_str() == other.description.as_ref().map_or("", String::as_str)
            && self.format_type == other.format_type
            && self.guild_id == other.guild_id
            && self.id == other.id
            && self.kind == other.kind
            && self.name == other.name
            && self.pack_id == other.pack_id
            && self.sort_value == other.sort_value
            && self.tags == other.tags
            && self.user_id == other.user.as_ref().map(|user| user.id)
    }
}

impl Cache {
    pub(crate) async fn cache_stickers(
        &self,
        guild_id: Id<GuildMarker>,
        stickers: Vec<Sticker>,
    ) -> Result<(), Error> {
        let guild_stickers = self.guild_stickers.members(&guild_id).await?;
        if !guild_stickers.is_empty() {
            let incoming: HashSet<_> = stickers.iter().map(|sticker| sticker.id).collect();

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

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

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

        for sticker in stickers {
            self.cache_sticker(guild_id, sticker).await?;
        }

        Ok(())
    }

    pub(crate) async fn cache_sticker(
        &self,
        guild_id: Id<GuildMarker>,
        sticker: Sticker,
    ) -> Result<(), Error> {
        if let Some(cached_sticker) = self.stickers.get(&sticker.id).await?
            && cached_sticker.value == sticker
        {
            return Ok(());
        }

        if let Some(user) = &sticker.user {
            self.cache_user(user, Some(guild_id)).await?;
        }

        self.guild_stickers.insert(&guild_id, &sticker.id).await?;
        self.stickers
            .insert(
                &sticker.id.clone(),
                &GuildResource {
                    guild_id,
                    value: sticker.into(),
                },
            )
            .await?;

        Ok(())
    }
}