twilight_cache_inmemory/event/
member.rs1use std::borrow::Cow;
2
3use crate::{
4 config::ResourceType,
5 model::member::ComputedInteractionMember,
6 traits::{CacheableGuild, CacheableMember},
7 CacheableModels, InMemoryCache, UpdateCache,
8};
9use twilight_model::{
10 application::interaction::InteractionMember,
11 gateway::payload::incoming::{MemberAdd, MemberChunk, MemberRemove, MemberUpdate},
12 guild::{Member, PartialMember},
13 id::{
14 marker::{GuildMarker, UserMarker},
15 Id,
16 },
17};
18
19impl<CacheModels: CacheableModels> InMemoryCache<CacheModels> {
20 pub(crate) fn cache_members(
21 &self,
22 guild_id: Id<GuildMarker>,
23 members: impl IntoIterator<Item = Member>,
24 ) {
25 for member in members {
26 self.cache_member(guild_id, member);
27 }
28 }
29
30 pub(crate) fn cache_member(&self, guild_id: Id<GuildMarker>, member: Member) {
31 let member_id = member.user.id;
32 let id = (guild_id, member_id);
33
34 if let Some(m) = self.members.get(&id) {
35 if *m == member {
36 return;
37 }
38 }
39
40 self.cache_user(Cow::Borrowed(&member.user), Some(guild_id));
41 let cached = CacheModels::Member::from(member);
42 self.members.insert(id, cached);
43 self.guild_members
44 .entry(guild_id)
45 .or_default()
46 .insert(member_id);
47 }
48
49 pub(crate) fn cache_borrowed_partial_member(
50 &self,
51 guild_id: Id<GuildMarker>,
52 member: &PartialMember,
53 user_id: Id<UserMarker>,
54 ) {
55 let id = (guild_id, user_id);
56
57 if let Some(m) = self.members.get(&id) {
58 if &*m == member {
59 return;
60 }
61 }
62
63 self.guild_members
64 .entry(guild_id)
65 .or_default()
66 .insert(user_id);
67
68 let cached = CacheModels::Member::from((user_id, member.clone()));
69 self.members.insert(id, cached);
70 }
71
72 pub(crate) fn cache_borrowed_interaction_member(
73 &self,
74 guild_id: Id<GuildMarker>,
75 member: &InteractionMember,
76 user_id: Id<UserMarker>,
77 ) {
78 let id = (guild_id, user_id);
79
80 let (avatar, deaf, mute) = match self.members.get(&id) {
81 Some(m) if &*m == member => return,
82 Some(m) => (m.avatar(), m.deaf(), m.mute()),
83 None => (None, None, None),
84 };
85
86 self.guild_members
87 .entry(guild_id)
88 .or_default()
89 .insert(user_id);
90
91 let cached = CacheModels::Member::from(ComputedInteractionMember {
92 avatar,
93 deaf,
94 interaction_member: member.clone(),
95 mute,
96 user_id,
97 });
98
99 self.members.insert(id, cached);
100 }
101}
102
103impl<CacheModels: CacheableModels> UpdateCache<CacheModels> for MemberAdd {
104 fn update(&self, cache: &InMemoryCache<CacheModels>) {
105 if cache.wants(ResourceType::GUILD) {
106 if let Some(mut guild) = cache.guilds.get_mut(&self.guild_id) {
107 guild.increase_member_count(1);
108 }
109 }
110
111 if !cache.wants(ResourceType::MEMBER) {
112 return;
113 }
114
115 cache.cache_member(self.guild_id, self.member.clone());
116 }
117}
118
119impl<CacheModels: CacheableModels> UpdateCache<CacheModels> for MemberChunk {
120 fn update(&self, cache: &InMemoryCache<CacheModels>) {
121 if !cache.wants(ResourceType::MEMBER) {
122 return;
123 }
124
125 if self.members.is_empty() {
126 return;
127 }
128
129 cache.cache_members(self.guild_id, self.members.clone());
130 }
131}
132
133impl<CacheModels: CacheableModels> UpdateCache<CacheModels> for MemberRemove {
134 fn update(&self, cache: &InMemoryCache<CacheModels>) {
135 if cache.wants(ResourceType::GUILD) {
136 if let Some(mut guild) = cache.guilds.get_mut(&self.guild_id) {
137 guild.decrease_member_count(1);
138 }
139 }
140
141 if !cache.wants(ResourceType::MEMBER) {
142 return;
143 }
144
145 cache.members.remove(&(self.guild_id, self.user.id));
146
147 if let Some(mut members) = cache.guild_members.get_mut(&self.guild_id) {
148 members.remove(&self.user.id);
149 }
150
151 let mut remove_user = false;
154
155 if let Some(mut user_guilds) = cache.user_guilds.get_mut(&self.user.id) {
156 user_guilds.remove(&self.guild_id);
157
158 remove_user = user_guilds.is_empty();
159 }
160
161 if remove_user {
162 cache.users.remove(&self.user.id);
163 }
164 }
165}
166
167impl<CacheModels: CacheableModels> UpdateCache<CacheModels> for MemberUpdate {
168 fn update(&self, cache: &InMemoryCache<CacheModels>) {
169 if !cache.wants(ResourceType::MEMBER) {
170 return;
171 }
172
173 let key = (self.guild_id, self.user.id);
174
175 if let Some(mut member) = cache.members.get_mut(&key) {
176 member.update_with_member_update(self);
177 cache.cache_user(Cow::Borrowed(&self.user), Some(self.guild_id));
178 }
179 }
180}
181
182#[cfg(test)]
183mod tests {
184 use crate::{test, DefaultInMemoryCache};
185 use std::borrow::Cow;
186 use twilight_model::{
187 gateway::payload::incoming::{MemberRemove, MemberUpdate},
188 guild::{Member, MemberFlags},
189 id::Id,
190 };
191
192 #[test]
193 fn cache_guild_member() {
194 let cache = DefaultInMemoryCache::new();
195
196 {
198 let guild_1_user_ids = (1..=10).map(Id::new).collect::<Vec<_>>();
199 let guild_1_members = guild_1_user_ids
200 .iter()
201 .copied()
202 .map(test::member)
203 .collect::<Vec<_>>();
204
205 for member in guild_1_members {
206 cache.cache_member(Id::new(1), member);
207 }
208
209 let cached_roles = cache.guild_members(Id::new(1)).unwrap();
211 assert_eq!(cached_roles.len(), guild_1_user_ids.len());
212 assert!(guild_1_user_ids.iter().all(|id| cached_roles.contains(id)));
213
214 assert!(guild_1_user_ids
216 .iter()
217 .all(|id| cache.member(Id::new(1), *id).is_some()));
218
219 assert!(guild_1_user_ids.iter().all(|id| cache.user(*id).is_some()));
221 }
222
223 {
225 let guild_2_user_ids = (1..=10).map(Id::new).collect::<Vec<_>>();
226 let guild_2_members = guild_2_user_ids
227 .iter()
228 .copied()
229 .map(test::member)
230 .collect::<Vec<_>>();
231 cache.cache_members(Id::new(2), guild_2_members);
232
233 let cached_roles = cache.guild_members(Id::new(1)).unwrap();
235 assert_eq!(cached_roles.len(), guild_2_user_ids.len());
236 assert!(guild_2_user_ids.iter().all(|id| cached_roles.contains(id)));
237
238 assert!(guild_2_user_ids
240 .iter()
241 .copied()
242 .all(|id| cache.member(Id::new(1), id).is_some()));
243
244 assert!(guild_2_user_ids.iter().all(|id| cache.user(*id).is_some()));
246 }
247 }
248
249 #[test]
250 fn cache_user_guild_state() {
251 let user_id = Id::new(2);
252 let cache = DefaultInMemoryCache::new();
253 cache.cache_user(Cow::Owned(test::user(user_id)), Some(Id::new(1)));
254
255 {
257 let user_guilds = cache.user_guilds(user_id).unwrap();
258 assert!(user_guilds.contains(&Id::new(1)));
259 assert_eq!(1, user_guilds.len());
260 }
261
262 cache.cache_user(Cow::Owned(test::user(user_id)), Some(Id::new(3)));
264
265 {
266 let user_guilds = cache.user_guilds(user_id).unwrap();
267 assert!(user_guilds.contains(&Id::new(3)));
268 assert_eq!(2, user_guilds.len());
269 }
270
271 cache.update(&MemberRemove {
274 guild_id: Id::new(3),
275 user: test::user(user_id),
276 });
277
278 {
279 let user_guilds = cache.user_guilds(user_id).unwrap();
280 assert!(!user_guilds.contains(&Id::new(3)));
281 assert_eq!(1, user_guilds.len());
282 }
283
284 cache.update(&MemberRemove {
287 guild_id: Id::new(1),
288 user: test::user(user_id),
289 });
290 assert!(!cache.users.contains_key(&user_id));
291 }
292
293 #[test]
294 fn member_update_updates_user() {
295 let user_id = Id::new(2);
296 let guild_id = Id::new(3);
297 let cache = DefaultInMemoryCache::new();
298
299 let member = Member {
300 avatar: None,
301 communication_disabled_until: None,
302 deaf: false,
303 flags: MemberFlags::empty(),
304 joined_at: None,
305 mute: false,
306 nick: None,
307 pending: false,
308 premium_since: None,
309 roles: Vec::new(),
310 user: test::user(user_id),
311 };
312
313 cache.cache_member(guild_id, member);
314
315 let mut updated_user = test::user(user_id);
316 updated_user.name = String::from("updated_username");
317
318 cache.update(&MemberUpdate {
320 avatar: None,
321 communication_disabled_until: None,
322 guild_id,
323 flags: None,
324 deaf: None,
325 joined_at: None,
326 mute: None,
327 nick: None,
328 pending: false,
329 premium_since: None,
330 roles: Vec::new(),
331 user: updated_user.clone(),
332 });
333
334 let cached_user = cache.user(user_id).unwrap();
335 assert_eq!(cached_user.value(), &updated_user);
336 }
337}