twilight_cache_inmemory/event/
sticker.rs

1use std::{borrow::Cow, collections::HashSet};
2
3use crate::{
4    CacheableModels, CacheableSticker, GuildResource, InMemoryCache, UpdateCache,
5    config::ResourceType,
6};
7use twilight_model::{
8    channel::message::Sticker,
9    gateway::payload::incoming::GuildStickersUpdate,
10    id::{Id, marker::GuildMarker},
11};
12
13impl<CacheModels: CacheableModels> InMemoryCache<CacheModels> {
14    pub(crate) fn cache_stickers(&self, guild_id: Id<GuildMarker>, stickers: Vec<Sticker>) {
15        if let Some(mut guild_stickers) = self.guild_stickers.get_mut(&guild_id) {
16            let incoming_sticker_ids = stickers
17                .iter()
18                .map(|sticker| sticker.id)
19                .collect::<HashSet<_>>();
20
21            // Iterate over the set of a guild's stickers, retaining only the
22            // existing stickers that are still present in the updated list of
23            // stickers.
24            //
25            // If one is not, then we remove it both from the guild's set of
26            // stickers and the sticker cache.
27            guild_stickers.retain(|sticker_id| {
28                let retain = incoming_sticker_ids.contains(sticker_id);
29
30                if !retain {
31                    self.stickers.remove(sticker_id);
32                }
33
34                retain
35            });
36        }
37
38        for sticker in stickers {
39            self.cache_sticker(guild_id, sticker);
40        }
41    }
42
43    pub(crate) fn cache_sticker(&self, guild_id: Id<GuildMarker>, sticker: Sticker) {
44        if let Some(cached_sticker) = self.stickers.get(&sticker.id)
45            && cached_sticker.value == sticker
46        {
47            return;
48        }
49
50        if let Some(user) = sticker.user.clone() {
51            self.cache_user(Cow::Owned(user), Some(guild_id));
52        }
53
54        let sticker_id = sticker.id;
55        let cached = CacheModels::Sticker::from(sticker);
56
57        self.stickers.insert(
58            cached.id(),
59            GuildResource {
60                guild_id,
61                value: cached,
62            },
63        );
64
65        self.guild_stickers
66            .entry(guild_id)
67            .or_default()
68            .insert(sticker_id);
69    }
70}
71
72impl<CacheModels: CacheableModels> UpdateCache<CacheModels> for GuildStickersUpdate {
73    fn update(&self, cache: &InMemoryCache<CacheModels>) {
74        if !cache.wants(ResourceType::STICKER) {
75            return;
76        }
77
78        cache.cache_stickers(self.guild_id, self.stickers.clone());
79    }
80}
81
82#[cfg(test)]
83mod tests {
84    use crate::{DefaultCacheModels, InMemoryCache, test};
85    use twilight_model::id::{
86        Id,
87        marker::{GuildMarker, StickerMarker},
88    };
89
90    const GUILD_ID: Id<GuildMarker> = Id::new(1);
91    const STICKER_ONE_ID: Id<StickerMarker> = Id::new(2);
92    const STICKER_TWO_ID: Id<StickerMarker> = Id::new(3);
93
94    fn cache_with_stickers() -> InMemoryCache<DefaultCacheModels> {
95        let cache = test::cache();
96        let one = test::sticker(STICKER_ONE_ID, GUILD_ID);
97        let two = test::sticker(STICKER_TWO_ID, GUILD_ID);
98        cache.cache_stickers(GUILD_ID, Vec::from([one, two]));
99
100        cache
101    }
102
103    /// Test that caching stickers correctly inserts the stickers into the
104    /// sticker cache by testing their identity, and that the map of a guild's
105    /// sticker associated IDs contains all stickers.
106    #[test]
107    fn cache_stickers() {
108        let cache = cache_with_stickers();
109        assert_eq!(cache.stickers.len(), 2);
110        let one = test::sticker(STICKER_ONE_ID, GUILD_ID);
111        let two = test::sticker(STICKER_TWO_ID, GUILD_ID);
112        assert!(
113            cache
114                .stickers
115                .get(&STICKER_ONE_ID)
116                .is_some_and(|r| r.id == STICKER_ONE_ID)
117        );
118        assert!(
119            cache
120                .stickers
121                .get(&STICKER_TWO_ID)
122                .is_some_and(|r| r.id == STICKER_TWO_ID)
123        );
124
125        let guild_stickers = cache
126            .guild_stickers
127            .get(&GUILD_ID)
128            .expect("cache has stickers for guild");
129        assert_eq!(guild_stickers.len(), 2);
130        assert!(guild_stickers.contains(&one.id));
131        assert!(guild_stickers.contains(&two.id));
132    }
133
134    /// Test that caching an updated list of a guild's stickers removes one of
135    /// the existing stickers if not in the updated list, meaning the sticker no
136    /// longer exists.
137    ///
138    /// For example, if two stickers for a guild named "foo" and "bar" are
139    /// cached and a new list of stickers with only "foo" is cached, then "bar"
140    /// will be removed.
141    #[test]
142    fn cache_stickers_removal() {
143        let cache = cache_with_stickers();
144        let one = test::sticker(STICKER_ONE_ID, GUILD_ID);
145        cache.cache_stickers(GUILD_ID, Vec::from([one]));
146        assert_eq!(cache.stickers.len(), 1);
147        assert!(
148            cache
149                .stickers
150                .get(&STICKER_ONE_ID)
151                .is_some_and(|r| r.id == STICKER_ONE_ID)
152        );
153        let guild_stickers = cache
154            .guild_stickers
155            .get(&GUILD_ID)
156            .expect("cache has stickers for guild");
157        assert_eq!(guild_stickers.len(), 1);
158        assert!(guild_stickers.contains(&STICKER_ONE_ID));
159    }
160}