tulpje-cache 0.4.0

A redis cache for twilight
Documentation
use twilight_model::{
    channel::message::{EmojiReactionType, Reaction, ReactionCountDetails},
    gateway::payload::incoming::{
        ReactionAdd, ReactionRemove, ReactionRemoveAll, ReactionRemoveEmoji,
    },
};

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

impl UpdateCache for ReactionAdd {
    async fn update(&self, cache: &Cache) -> Result<(), Error> {
        let key = self.0.message_id;

        let Some(mut message) = cache.messages.get(&key).await? else {
            return Ok(());
        };

        if let Some(reaction) = message
            .reactions
            .iter_mut()
            .find(|r| reactions_eq(&r.emoji, &self.0.emoji))
        {
            if !reaction.me
                && let Some(current_user) = cache.current_user.get().await?
                && current_user.id == self.0.user_id
            {
                reaction.me = true;
            }

            reaction.count += 1;
        } else {
            let me = cache
                .current_user
                .get()
                .await?
                .is_some_and(|user| user.id == self.0.user_id);

            message.reactions.push(Reaction {
                burst_colors: Vec::new(),
                count: 1,
                count_details: ReactionCountDetails {
                    burst: 0,
                    normal: 1,
                },
                emoji: self.0.emoji.clone(),
                me,
                me_burst: false,
            });
        }

        cache.messages.insert(&message.id, &message).await?;

        Ok(())
    }
}

impl UpdateCache for ReactionRemove {
    async fn update(&self, cache: &Cache) -> Result<(), Error> {
        let Some(mut message) = cache.messages.get(&self.0.message_id).await? else {
            return Ok(());
        };

        if let Some(reaction) = message
            .reactions
            .iter_mut()
            .find(|r| reactions_eq(&r.emoji, &self.0.emoji))
        {
            if reaction.me
                && let Some(current_user) = cache.current_user.get().await?
                && current_user.id == self.0.user_id
            {
                reaction.me = false;
            }

            if reaction.count > 1 {
                reaction.count -= 1;
            } else {
                message
                    .reactions
                    .retain(|e| !(reactions_eq(&e.emoji, &self.0.emoji)));
            }
        }

        cache.messages.insert(&message.id, &message).await?;

        Ok(())
    }
}

impl UpdateCache for ReactionRemoveAll {
    async fn update(&self, cache: &Cache) -> Result<(), Error> {
        let Some(mut message) = cache.messages.get(&self.message_id).await? else {
            return Ok(());
        };

        message.reactions.clear();
        cache.messages.insert(&message.id, &message).await?;

        Ok(())
    }
}

impl UpdateCache for ReactionRemoveEmoji {
    async fn update(&self, cache: &Cache) -> Result<(), Error> {
        let Some(mut message) = cache.messages.get(&self.message_id).await? else {
            return Ok(());
        };

        let maybe_index = message
            .reactions
            .iter()
            .position(|r| reactions_eq(&r.emoji, &self.emoji));

        if let Some(index) = maybe_index {
            message.reactions.remove(index);
        }

        cache.messages.insert(&message.id, &message).await?;

        Ok(())
    }
}

fn reactions_eq(a: &EmojiReactionType, b: &EmojiReactionType) -> bool {
    match (a, b) {
        (
            EmojiReactionType::Custom { id: id_a, .. },
            EmojiReactionType::Custom { id: id_b, .. },
        ) => id_a == id_b,
        (
            EmojiReactionType::Unicode { name: name_a },
            EmojiReactionType::Unicode { name: name_b },
        ) => name_a == name_b,
        _ => false,
    }
}