twilight_cache_inmemory/event/
member.rs1use std::borrow::Cow;
2
3use crate::{
4 CacheableModels, InMemoryCache, UpdateCache,
5 config::ResourceType,
6 model::member::ComputedInteractionMember,
7 traits::{CacheableGuild, CacheableMember},
8};
9use twilight_model::{
10 application::interaction::InteractionMember,
11 gateway::payload::incoming::{MemberAdd, MemberChunk, MemberRemove, MemberUpdate},
12 guild::{Member, PartialMember},
13 id::{
14 Id,
15 marker::{GuildMarker, UserMarker},
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 && *m == member
36 {
37 return;
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 && &*m == member
59 {
60 return;
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 && let Some(mut guild) = cache.guilds.get_mut(&self.guild_id)
107 {
108 guild.increase_member_count(1);
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 && let Some(mut guild) = cache.guilds.get_mut(&self.guild_id)
137 {
138 guild.decrease_member_count(1);
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::{DefaultInMemoryCache, test};
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!(
216 guild_1_user_ids
217 .iter()
218 .all(|id| cache.member(Id::new(1), *id).is_some())
219 );
220
221 assert!(guild_1_user_ids.iter().all(|id| cache.user(*id).is_some()));
223 }
224
225 {
227 let guild_2_user_ids = (1..=10).map(Id::new).collect::<Vec<_>>();
228 let guild_2_members = guild_2_user_ids
229 .iter()
230 .copied()
231 .map(test::member)
232 .collect::<Vec<_>>();
233 cache.cache_members(Id::new(2), guild_2_members);
234
235 let cached_roles = cache.guild_members(Id::new(1)).unwrap();
237 assert_eq!(cached_roles.len(), guild_2_user_ids.len());
238 assert!(guild_2_user_ids.iter().all(|id| cached_roles.contains(id)));
239
240 assert!(
242 guild_2_user_ids
243 .iter()
244 .copied()
245 .all(|id| cache.member(Id::new(1), id).is_some())
246 );
247
248 assert!(guild_2_user_ids.iter().all(|id| cache.user(*id).is_some()));
250 }
251 }
252
253 #[test]
254 fn cache_user_guild_state() {
255 let user_id = Id::new(2);
256 let cache = DefaultInMemoryCache::new();
257 cache.cache_user(Cow::Owned(test::user(user_id)), Some(Id::new(1)));
258
259 {
261 let user_guilds = cache.user_guilds(user_id).unwrap();
262 assert!(user_guilds.contains(&Id::new(1)));
263 assert_eq!(1, user_guilds.len());
264 }
265
266 cache.cache_user(Cow::Owned(test::user(user_id)), Some(Id::new(3)));
268
269 {
270 let user_guilds = cache.user_guilds(user_id).unwrap();
271 assert!(user_guilds.contains(&Id::new(3)));
272 assert_eq!(2, user_guilds.len());
273 }
274
275 cache.update(&MemberRemove {
278 guild_id: Id::new(3),
279 user: test::user(user_id),
280 });
281
282 {
283 let user_guilds = cache.user_guilds(user_id).unwrap();
284 assert!(!user_guilds.contains(&Id::new(3)));
285 assert_eq!(1, user_guilds.len());
286 }
287
288 cache.update(&MemberRemove {
291 guild_id: Id::new(1),
292 user: test::user(user_id),
293 });
294 assert!(!cache.users.contains_key(&user_id));
295 }
296
297 #[test]
298 fn member_update_updates_user() {
299 let user_id = Id::new(2);
300 let guild_id = Id::new(3);
301 let cache = DefaultInMemoryCache::new();
302
303 let member = Member {
304 avatar: None,
305 avatar_decoration_data: None,
306 banner: None,
307 communication_disabled_until: None,
308 deaf: false,
309 flags: MemberFlags::empty(),
310 joined_at: None,
311 mute: false,
312 nick: None,
313 pending: false,
314 premium_since: None,
315 roles: Vec::new(),
316 user: test::user(user_id),
317 };
318
319 cache.cache_member(guild_id, member);
320
321 let mut updated_user = test::user(user_id);
322 updated_user.name = String::from("updated_username");
323
324 cache.update(&MemberUpdate {
326 avatar: None,
327 communication_disabled_until: None,
328 guild_id,
329 flags: None,
330 deaf: None,
331 joined_at: None,
332 mute: None,
333 nick: None,
334 pending: false,
335 premium_since: None,
336 roles: Vec::new(),
337 user: updated_user.clone(),
338 });
339
340 let cached_user = cache.user(user_id).unwrap();
341 assert_eq!(cached_user.value(), &updated_user);
342 }
343}