1use std::ops::Deref;
2
3use serde::Serialize;
4use twilight_model::{
5 application::interaction::InteractionMember,
6 gateway::payload::incoming::MemberUpdate,
7 guild::{Member, MemberFlags, PartialMember},
8 id::{
9 marker::{RoleMarker, UserMarker},
10 Id,
11 },
12 util::{ImageHash, Timestamp},
13};
14
15use crate::CacheableMember;
16
17#[derive(Clone, Debug, Eq, PartialEq)]
20pub struct ComputedInteractionMember {
21 pub avatar: Option<ImageHash>,
23 pub deaf: Option<bool>,
25 pub interaction_member: InteractionMember,
27 pub mute: Option<bool>,
29 pub user_id: Id<UserMarker>,
31}
32
33impl Deref for ComputedInteractionMember {
34 type Target = InteractionMember;
35
36 fn deref(&self) -> &Self::Target {
37 &self.interaction_member
38 }
39}
40
41#[derive(Clone, Debug, Eq, PartialEq, Serialize)]
45pub struct CachedMember {
46 pub(crate) avatar: Option<ImageHash>,
47 pub(crate) communication_disabled_until: Option<Timestamp>,
48 pub(crate) deaf: Option<bool>,
49 pub(crate) flags: MemberFlags,
50 pub(crate) joined_at: Option<Timestamp>,
51 pub(crate) mute: Option<bool>,
52 pub(crate) nick: Option<String>,
53 pub(crate) pending: bool,
54 pub(crate) premium_since: Option<Timestamp>,
55 pub(crate) roles: Vec<Id<RoleMarker>>,
56 pub(crate) user_id: Id<UserMarker>,
57}
58
59impl CachedMember {
60 pub const fn avatar(&self) -> Option<ImageHash> {
62 self.avatar
63 }
64
65 pub const fn communication_disabled_until(&self) -> Option<Timestamp> {
74 self.communication_disabled_until
75 }
76
77 pub const fn deaf(&self) -> Option<bool> {
79 self.deaf
80 }
81
82 pub const fn flags(&self) -> MemberFlags {
86 self.flags
87 }
88
89 pub const fn joined_at(&self) -> Option<Timestamp> {
91 self.joined_at
92 }
93
94 pub const fn mute(&self) -> Option<bool> {
96 self.mute
97 }
98
99 pub fn nick(&self) -> Option<&str> {
101 self.nick.as_deref()
102 }
103
104 pub const fn pending(&self) -> bool {
107 self.pending
108 }
109
110 pub const fn premium_since(&self) -> Option<Timestamp> {
112 self.premium_since
113 }
114
115 pub fn roles(&self) -> &[Id<RoleMarker>] {
117 &self.roles
118 }
119
120 pub const fn user_id(&self) -> Id<UserMarker> {
122 self.user_id
123 }
124}
125
126impl From<Member> for CachedMember {
127 fn from(member: Member) -> Self {
128 let Member {
129 avatar,
130 communication_disabled_until,
131 deaf,
132 flags,
133 joined_at,
134 mute,
135 nick,
136 pending,
137 premium_since,
138 roles,
139 user,
140 } = member;
141
142 Self {
143 avatar,
144 communication_disabled_until,
145 deaf: Some(deaf),
146 flags,
147 joined_at,
148 mute: Some(mute),
149 nick,
150 pending,
151 premium_since,
152 roles,
153 user_id: user.id,
154 }
155 }
156}
157
158impl From<ComputedInteractionMember> for CachedMember {
159 fn from(member: ComputedInteractionMember) -> Self {
160 let ComputedInteractionMember {
161 avatar,
162 deaf,
163 mute,
164 user_id,
165 interaction_member,
166 } = member;
167 let InteractionMember {
168 avatar: _,
169 communication_disabled_until,
170 flags,
171 joined_at,
172 nick,
173 pending,
174 permissions: _,
175 premium_since,
176 roles,
177 } = interaction_member;
178
179 Self {
180 avatar,
181 communication_disabled_until,
182 deaf,
183 flags,
184 joined_at,
185 mute,
186 nick,
187 pending,
188 premium_since,
189 roles,
190 user_id,
191 }
192 }
193}
194
195impl From<(Id<UserMarker>, PartialMember)> for CachedMember {
196 fn from((user_id, member): (Id<UserMarker>, PartialMember)) -> Self {
197 let PartialMember {
198 avatar,
199 communication_disabled_until,
200 deaf,
201 flags,
202 joined_at,
203 mute,
204 nick,
205 permissions: _,
206 premium_since,
207 roles,
208 user,
209 } = member;
210
211 Self {
212 avatar,
213 communication_disabled_until,
214 deaf: Some(deaf),
215 flags,
216 joined_at,
217 mute: Some(mute),
218 nick,
219 pending: false,
220 premium_since,
221 roles,
222 user_id: user.map_or(user_id, |user| user.id),
223 }
224 }
225}
226
227impl PartialEq<Member> for CachedMember {
228 fn eq(&self, other: &Member) -> bool {
229 self.avatar == other.avatar
230 && self.communication_disabled_until == other.communication_disabled_until
231 && self.deaf == Some(other.deaf)
232 && self.joined_at == other.joined_at
233 && self.mute == Some(other.mute)
234 && self.nick == other.nick
235 && self.pending == other.pending
236 && self.premium_since == other.premium_since
237 && self.roles == other.roles
238 && self.user_id == other.user.id
239 }
240}
241
242impl PartialEq<PartialMember> for CachedMember {
243 fn eq(&self, other: &PartialMember) -> bool {
244 self.communication_disabled_until == other.communication_disabled_until
245 && self.deaf == Some(other.deaf)
246 && self.joined_at == other.joined_at
247 && self.mute == Some(other.mute)
248 && self.nick == other.nick
249 && self.premium_since == other.premium_since
250 && self.roles == other.roles
251 }
252}
253
254impl PartialEq<InteractionMember> for CachedMember {
255 fn eq(&self, other: &InteractionMember) -> bool {
256 self.joined_at == other.joined_at
257 && self.nick == other.nick
258 && self.premium_since == other.premium_since
259 && self.roles == other.roles
260 }
261}
262
263impl CacheableMember for CachedMember {
264 fn roles(&self) -> &[Id<RoleMarker>] {
265 &self.roles
266 }
267
268 #[cfg(feature = "permission-calculator")]
269 fn communication_disabled_until(&self) -> Option<Timestamp> {
270 self.communication_disabled_until
271 }
272
273 fn avatar(&self) -> Option<ImageHash> {
274 self.avatar
275 }
276
277 fn deaf(&self) -> Option<bool> {
278 self.deaf
279 }
280
281 fn mute(&self) -> Option<bool> {
282 self.mute
283 }
284
285 fn update_with_member_update(&mut self, member_update: &MemberUpdate) {
286 self.avatar = member_update.avatar;
287 self.deaf = member_update.deaf.or_else(|| self.deaf());
288 self.mute = member_update.mute.or_else(|| self.mute());
289 self.nick.clone_from(&member_update.nick);
290 self.roles.clone_from(&member_update.roles);
291 self.joined_at = member_update.joined_at;
292 self.pending = member_update.pending;
293 self.communication_disabled_until = member_update.communication_disabled_until;
294 }
295}
296
297#[cfg(test)]
298mod tests {
299 use super::CachedMember;
300 use static_assertions::assert_fields;
301 use twilight_model::{
302 guild::{Member, MemberFlags, PartialMember},
303 id::Id,
304 user::User,
305 util::Timestamp,
306 };
307
308 assert_fields!(
309 CachedMember: deaf,
310 joined_at,
311 mute,
312 nick,
313 pending,
314 premium_since,
315 roles,
316 user_id
317 );
318
319 fn cached_member() -> CachedMember {
320 let joined_at = Some(Timestamp::from_secs(1_632_072_645).expect("non zero"));
321 let flags = MemberFlags::BYPASSES_VERIFICATION | MemberFlags::DID_REJOIN;
322 CachedMember {
323 avatar: None,
324 communication_disabled_until: None,
325 deaf: Some(false),
326 flags,
327 joined_at,
328 mute: Some(true),
329 nick: Some("member nick".to_owned()),
330 pending: false,
331 premium_since: None,
332 roles: Vec::new(),
333 user_id: user().id,
334 }
335 }
336
337 fn user() -> User {
338 User {
339 accent_color: None,
340 avatar: None,
341 avatar_decoration: None,
342 avatar_decoration_data: None,
343 banner: None,
344 bot: false,
345 discriminator: 1,
346 email: None,
347 flags: None,
348 global_name: Some("test".to_owned()),
349 id: Id::new(1),
350 locale: None,
351 mfa_enabled: None,
352 name: "bar".to_owned(),
353 premium_type: None,
354 public_flags: None,
355 system: None,
356 verified: None,
357 }
358 }
359
360 #[test]
361 fn eq_member() {
362 let joined_at = Some(Timestamp::from_secs(1_632_072_645).expect("non zero"));
363 let flags = MemberFlags::BYPASSES_VERIFICATION | MemberFlags::DID_REJOIN;
364
365 let member = Member {
366 avatar: None,
367 communication_disabled_until: None,
368 deaf: false,
369 flags,
370 joined_at,
371 mute: true,
372 nick: Some("member nick".to_owned()),
373 pending: false,
374 premium_since: None,
375 roles: Vec::new(),
376 user: user(),
377 };
378
379 assert_eq!(cached_member(), member);
380 }
381
382 #[test]
383 fn eq_partial_member() {
384 let joined_at = Some(Timestamp::from_secs(1_632_072_645).expect("non zero"));
385 let flags = MemberFlags::BYPASSES_VERIFICATION | MemberFlags::DID_REJOIN;
386
387 let member = PartialMember {
388 avatar: None,
389 communication_disabled_until: None,
390 deaf: false,
391 flags,
392 joined_at,
393 mute: true,
394 nick: Some("member nick".to_owned()),
395 permissions: None,
396 premium_since: None,
397 roles: Vec::new(),
398 user: None,
399 };
400
401 assert_eq!(cached_member(), member);
402 }
403}