twilight_cache_inmemory/event/
emoji.rs

1use crate::{config::ResourceType, CacheableModels, GuildResource, InMemoryCache, UpdateCache};
2use std::borrow::Cow;
3use twilight_model::{
4    gateway::payload::incoming::GuildEmojisUpdate,
5    guild::Emoji,
6    id::{marker::GuildMarker, Id},
7};
8
9impl<CacheModels: CacheableModels> InMemoryCache<CacheModels> {
10    pub(crate) fn cache_emojis(&self, guild_id: Id<GuildMarker>, emojis: Vec<Emoji>) {
11        if let Some(mut guild_emojis) = self.guild_emojis.get_mut(&guild_id) {
12            let incoming: Vec<_> = emojis.iter().map(|e| e.id).collect();
13
14            let removal_filter: Vec<_> = guild_emojis
15                .iter()
16                .copied()
17                .filter(|e| !incoming.contains(e))
18                .collect();
19
20            for to_remove in &removal_filter {
21                guild_emojis.remove(to_remove);
22            }
23
24            for to_remove in &removal_filter {
25                self.emojis.remove(to_remove);
26            }
27        }
28
29        for emoji in emojis {
30            self.cache_emoji(guild_id, emoji);
31        }
32    }
33
34    pub(crate) fn cache_emoji(&self, guild_id: Id<GuildMarker>, emoji: Emoji) {
35        if let Some(cached_emoji) = self.emojis.get(&emoji.id) {
36            if cached_emoji.value == emoji {
37                return;
38            }
39        }
40
41        if let Some(user) = emoji.user.as_ref() {
42            self.cache_user(Cow::Borrowed(user), Some(guild_id));
43        }
44
45        let emoji_id = emoji.id;
46        let cached = CacheModels::Emoji::from(emoji);
47
48        self.emojis.insert(
49            emoji_id,
50            GuildResource {
51                guild_id,
52                value: cached,
53            },
54        );
55
56        self.guild_emojis
57            .entry(guild_id)
58            .or_default()
59            .insert(emoji_id);
60    }
61}
62
63impl<CacheModels: CacheableModels> UpdateCache<CacheModels> for GuildEmojisUpdate {
64    fn update(&self, cache: &InMemoryCache<CacheModels>) {
65        if !cache.wants(ResourceType::EMOJI) {
66            return;
67        }
68
69        cache.cache_emojis(self.guild_id, self.emojis.clone());
70    }
71}
72
73#[cfg(test)]
74mod tests {
75    use crate::{test, DefaultInMemoryCache};
76    use twilight_model::{
77        gateway::payload::incoming::GuildEmojisUpdate,
78        id::{marker::EmojiMarker, Id},
79        user::User,
80    };
81
82    #[test]
83    fn cache_emoji() {
84        // The user to do some of the inserts
85        fn user_mod(id: Id<EmojiMarker>) -> Option<User> {
86            if id.get() % 2 == 0 {
87                // Only use user for half
88                Some(test::user(Id::new(1)))
89            } else {
90                None
91            }
92        }
93
94        let cache = DefaultInMemoryCache::new();
95
96        // Single inserts
97        {
98            let guild_1_emoji_ids = (1..=10).map(Id::new).collect::<Vec<_>>();
99            let guild_1_emoji = guild_1_emoji_ids
100                .iter()
101                .copied()
102                .map(|id| test::emoji(id, user_mod(id)))
103                .collect::<Vec<_>>();
104
105            for emoji in guild_1_emoji {
106                cache.cache_emoji(Id::new(1), emoji);
107            }
108
109            for id in guild_1_emoji_ids.iter().copied() {
110                let global_emoji = cache.emoji(id);
111                assert!(global_emoji.is_some());
112            }
113
114            // Ensure the emoji has been added to the per-guild lookup map to prevent
115            // issues like #551 from returning
116            let guild_emojis = cache.guild_emojis(Id::new(1));
117            assert!(guild_emojis.is_some());
118            let guild_emojis = guild_emojis.unwrap();
119
120            assert_eq!(guild_1_emoji_ids.len(), guild_emojis.len());
121            assert!(guild_1_emoji_ids.iter().all(|id| guild_emojis.contains(id)));
122        }
123
124        // Bulk inserts
125        {
126            let guild_2_emoji_ids = (11..=20).map(Id::new).collect::<Vec<_>>();
127            let guild_2_emojis = guild_2_emoji_ids
128                .iter()
129                .copied()
130                .map(|id| test::emoji(id, user_mod(id)))
131                .collect::<Vec<_>>();
132            cache.cache_emojis(Id::new(2), guild_2_emojis);
133
134            for id in guild_2_emoji_ids.iter().copied() {
135                let global_emoji = cache.emoji(id);
136                assert!(global_emoji.is_some());
137            }
138
139            let guild_emojis = cache.guild_emojis(Id::new(2));
140
141            assert!(guild_emojis.is_some());
142            let guild_emojis = guild_emojis.unwrap();
143            assert_eq!(guild_2_emoji_ids.len(), guild_emojis.len());
144            assert!(guild_2_emoji_ids.iter().all(|id| guild_emojis.contains(id)));
145        }
146    }
147
148    #[test]
149    fn emoji_removal() {
150        let cache = DefaultInMemoryCache::new();
151
152        let guild_id = Id::new(1);
153
154        let emote = test::emoji(Id::new(1), None);
155        let emote_2 = test::emoji(Id::new(2), None);
156        let emote_3 = test::emoji(Id::new(3), None);
157
158        cache.cache_emoji(guild_id, emote.clone());
159        cache.cache_emoji(guild_id, emote_2.clone());
160        cache.cache_emoji(guild_id, emote_3.clone());
161
162        cache.update(&GuildEmojisUpdate {
163            emojis: vec![emote.clone(), emote_3.clone()],
164            guild_id,
165        });
166
167        assert_eq!(cache.emojis.len(), 2);
168        assert_eq!(cache.guild_emojis.get(&guild_id).unwrap().len(), 2);
169        assert!(cache.emoji(emote.id).is_some());
170        assert!(cache.emoji(emote_2.id).is_none());
171        assert!(cache.emoji(emote_3.id).is_some());
172
173        cache.update(&GuildEmojisUpdate {
174            emojis: vec![emote.clone()],
175            guild_id,
176        });
177
178        assert_eq!(cache.emojis.len(), 1);
179        assert_eq!(cache.guild_emojis.get(&guild_id).unwrap().len(), 1);
180        assert!(cache.emoji(emote.id).is_some());
181        assert!(cache.emoji(emote_2.id).is_none());
182
183        let emote_4 = test::emoji(Id::new(4), None);
184
185        cache.update(&GuildEmojisUpdate {
186            emojis: vec![emote_4.clone()],
187            guild_id,
188        });
189
190        assert_eq!(cache.emojis.len(), 1);
191        assert_eq!(cache.guild_emojis.get(&guild_id).unwrap().len(), 1);
192        assert!(cache.emoji(emote_4.id).is_some());
193        assert!(cache.emoji(emote.id).is_none());
194
195        cache.update(&GuildEmojisUpdate {
196            emojis: vec![],
197            guild_id,
198        });
199
200        assert!(cache.emojis.is_empty());
201        assert!(cache.guild_emojis.get(&guild_id).unwrap().is_empty());
202    }
203}