twilight_cache_inmemory/model/
guild.rs

1use std::slice::Iter;
2
3use serde::Serialize;
4use twilight_model::{
5    gateway::payload::incoming::GuildUpdate,
6    guild::{
7        scheduled_event::GuildScheduledEvent, AfkTimeout, DefaultMessageNotificationLevel,
8        ExplicitContentFilter, Guild, GuildFeature, MfaLevel, NSFWLevel, Permissions, PremiumTier,
9        SystemChannelFlags, VerificationLevel,
10    },
11    id::{
12        marker::{ApplicationMarker, ChannelMarker, GuildMarker, UserMarker},
13        Id,
14    },
15    util::{ImageHash, Timestamp},
16};
17
18use crate::CacheableGuild;
19
20/// Represents a cached [`Guild`].
21///
22/// [`Guild`]: twilight_model::guild::Guild
23#[derive(Clone, Debug, Eq, PartialEq, Serialize)]
24pub struct CachedGuild {
25    pub(crate) afk_channel_id: Option<Id<ChannelMarker>>,
26    pub(crate) afk_timeout: AfkTimeout,
27    pub(crate) application_id: Option<Id<ApplicationMarker>>,
28    pub(crate) banner: Option<ImageHash>,
29    pub(crate) default_message_notifications: DefaultMessageNotificationLevel,
30    pub(crate) description: Option<String>,
31    pub(crate) discovery_splash: Option<ImageHash>,
32    pub(crate) explicit_content_filter: ExplicitContentFilter,
33    pub(crate) features: Vec<GuildFeature>,
34    pub(crate) guild_scheduled_events: Vec<GuildScheduledEvent>,
35    pub(crate) icon: Option<ImageHash>,
36    pub(crate) id: Id<GuildMarker>,
37    pub(crate) joined_at: Option<Timestamp>,
38    pub(crate) large: bool,
39    pub(crate) max_members: Option<u64>,
40    pub(crate) max_presences: Option<u64>,
41    pub(crate) max_stage_video_channel_users: Option<u64>,
42    pub(crate) max_video_channel_users: Option<u64>,
43    pub(crate) member_count: Option<u64>,
44    pub(crate) mfa_level: MfaLevel,
45    pub(crate) name: String,
46    pub(crate) nsfw_level: NSFWLevel,
47    pub(crate) owner: Option<bool>,
48    pub(crate) owner_id: Id<UserMarker>,
49    pub(crate) permissions: Option<Permissions>,
50    pub(crate) preferred_locale: String,
51    pub(crate) premium_progress_bar_enabled: bool,
52    pub(crate) premium_subscription_count: Option<u64>,
53    pub(crate) premium_tier: PremiumTier,
54    pub(crate) public_updates_channel_id: Option<Id<ChannelMarker>>,
55    pub(crate) rules_channel_id: Option<Id<ChannelMarker>>,
56    pub(crate) safety_alerts_channel_id: Option<Id<ChannelMarker>>,
57    pub(crate) splash: Option<ImageHash>,
58    pub(crate) system_channel_flags: SystemChannelFlags,
59    pub(crate) system_channel_id: Option<Id<ChannelMarker>>,
60    pub(crate) unavailable: Option<bool>,
61    pub(crate) vanity_url_code: Option<String>,
62    pub(crate) verification_level: VerificationLevel,
63    pub(crate) widget_channel_id: Option<Id<ChannelMarker>>,
64    pub(crate) widget_enabled: Option<bool>,
65}
66
67impl CachedGuild {
68    /// ID of the AFK channel.
69    pub const fn afk_channel_id(&self) -> Option<Id<ChannelMarker>> {
70        self.afk_channel_id
71    }
72
73    /// AFK timeout in seconds.
74    pub const fn afk_timeout(&self) -> AfkTimeout {
75        self.afk_timeout
76    }
77
78    /// For bot created guilds, the ID of the creating application.
79    pub const fn application_id(&self) -> Option<Id<ApplicationMarker>> {
80        self.application_id
81    }
82
83    /// Banner hash.
84    ///
85    /// See [Discord Docs/Image Formatting].
86    ///
87    /// [Discord Docs/Image Formatting]: https://discord.com/developers/docs/reference#image-formatting
88    pub const fn banner(&self) -> Option<&ImageHash> {
89        self.banner.as_ref()
90    }
91
92    /// Default message notification level.
93    pub const fn default_message_notifications(&self) -> DefaultMessageNotificationLevel {
94        self.default_message_notifications
95    }
96
97    /// For Community guilds, the description.
98    pub fn description(&self) -> Option<&str> {
99        self.description.as_deref()
100    }
101
102    /// For discoverable guilds, the discovery splash hash.
103    ///
104    /// See [Discord Docs/Image Formatting].
105    ///
106    /// [Discord Docs/Image Formatting]: https://discord.com/developers/docs/reference#image-formatting
107    pub const fn discovery_splash(&self) -> Option<&ImageHash> {
108        self.discovery_splash.as_ref()
109    }
110
111    /// Explicit content filter level.
112    pub const fn explicit_content_filter(&self) -> ExplicitContentFilter {
113        self.explicit_content_filter
114    }
115
116    /// Enabled [guild features].
117    ///
118    /// [guild features]: https://discord.com/developers/docs/resources/guild#guild-object-guild-features
119    pub fn features(&self) -> Features<'_> {
120        Features {
121            inner: self.features.iter(),
122        }
123    }
124
125    /// Scheduled guild events.
126    #[allow(clippy::missing_const_for_fn)]
127    pub fn guild_scheduled_events(&self) -> &[GuildScheduledEvent] {
128        &self.guild_scheduled_events
129    }
130
131    /// Icon hash.
132    ///
133    /// See [Discord Docs/Image Formatting].
134    ///
135    /// [Discord Docs/Image Formatting]: https://discord.com/developers/docs/reference#image-formatting
136    pub const fn icon(&self) -> Option<&ImageHash> {
137        self.icon.as_ref()
138    }
139
140    /// ID of the guild.
141    pub const fn id(&self) -> Id<GuildMarker> {
142        self.id
143    }
144
145    /// [`Timestamp`] of the user's join date.
146    pub const fn joined_at(&self) -> Option<Timestamp> {
147        self.joined_at
148    }
149
150    /// Whether this guild is "large".
151    pub const fn large(&self) -> bool {
152        self.large
153    }
154
155    /// Maximum members.
156    pub const fn max_members(&self) -> Option<u64> {
157        self.max_members
158    }
159
160    /// Maximum presences.
161    pub const fn max_presences(&self) -> Option<u64> {
162        self.max_presences
163    }
164
165    /// Maximum number of users in a stage video channel.
166    pub const fn max_stage_video_channel_users(&self) -> Option<u64> {
167        self.max_stage_video_channel_users
168    }
169
170    /// Maximum number of users in a video channel.
171    pub const fn max_video_channel_users(&self) -> Option<u64> {
172        self.max_video_channel_users
173    }
174
175    /// Total number of members in the guild.
176    pub const fn member_count(&self) -> Option<u64> {
177        self.member_count
178    }
179
180    /// Required MFA level.
181    pub const fn mfa_level(&self) -> MfaLevel {
182        self.mfa_level
183    }
184
185    /// Name of the guild.
186    #[allow(clippy::missing_const_for_fn)]
187    pub fn name(&self) -> &str {
188        &self.name
189    }
190
191    /// NSFW level.
192    pub const fn nsfw_level(&self) -> NSFWLevel {
193        self.nsfw_level
194    }
195
196    /// Whether the current user is the owner of the guild.
197    pub const fn owner(&self) -> Option<bool> {
198        self.owner
199    }
200
201    /// ID of the guild's owner.
202    pub const fn owner_id(&self) -> Id<UserMarker> {
203        self.owner_id
204    }
205
206    /// Total permissions for the current user in the guild, excluding overwrites.
207    pub const fn permissions(&self) -> Option<Permissions> {
208        self.permissions
209    }
210
211    /// Preferred locale for Community guilds.
212    ///
213    /// Used in server discovery and notices from Discord. Defaults to "en-US".
214    #[allow(clippy::missing_const_for_fn)]
215    pub fn preferred_locale(&self) -> &str {
216        &self.preferred_locale
217    }
218
219    /// Whether the premium progress bar is enabled.
220    pub const fn premium_progress_bar_enabled(&self) -> bool {
221        self.premium_progress_bar_enabled
222    }
223
224    /// Number of boosts this guild currently has.
225    pub const fn premium_subscription_count(&self) -> Option<u64> {
226        self.premium_subscription_count
227    }
228
229    /// Server boost level.
230    pub const fn premium_tier(&self) -> PremiumTier {
231        self.premium_tier
232    }
233
234    /// ID of the where moderators of Community guilds receive notices from
235    /// Discord.
236    pub const fn public_updates_channel_id(&self) -> Option<Id<ChannelMarker>> {
237        self.public_updates_channel_id
238    }
239
240    /// For Community guilds, the ID of the rules channel.
241    pub const fn rules_channel_id(&self) -> Option<Id<ChannelMarker>> {
242        self.rules_channel_id
243    }
244
245    /// The ID of the channel where admins and moderators of Community guilds receive safety alerts from Discord.
246    pub const fn safety_alerts_channel_id(&self) -> Option<Id<ChannelMarker>> {
247        self.safety_alerts_channel_id
248    }
249
250    /// Splash hash.
251    ///
252    /// See [Discord Docs/Image Formatting].
253    ///
254    /// [Discord Docs/Image Formatting]: https://discord.com/developers/docs/reference#image-formatting
255    pub const fn splash(&self) -> Option<&ImageHash> {
256        self.splash.as_ref()
257    }
258
259    /// ID of the channel where notices are posted.
260    ///
261    /// Example notices include welcome messages and boost events.
262    pub const fn system_channel_id(&self) -> Option<Id<ChannelMarker>> {
263        self.system_channel_id
264    }
265
266    /// System channel flags.
267    pub const fn system_channel_flags(&self) -> SystemChannelFlags {
268        self.system_channel_flags
269    }
270
271    /// Whether the guild is unavailable due to an outage.
272    pub const fn unavailable(&self) -> Option<bool> {
273        self.unavailable
274    }
275
276    /// Vanity URL code.
277    pub fn vanity_url_code(&self) -> Option<&str> {
278        self.vanity_url_code.as_deref()
279    }
280
281    /// Required verification level.
282    pub const fn verification_level(&self) -> VerificationLevel {
283        self.verification_level
284    }
285
286    /// ID of the channel that a widget generates an invite to.
287    pub const fn widget_channel_id(&self) -> Option<Id<ChannelMarker>> {
288        self.widget_channel_id
289    }
290
291    /// Whether the widget is enabled.
292    pub const fn widget_enabled(&self) -> Option<bool> {
293        self.widget_enabled
294    }
295}
296
297impl From<Guild> for CachedGuild {
298    fn from(guild: Guild) -> Self {
299        let Guild {
300            afk_channel_id,
301            afk_timeout,
302            application_id,
303            approximate_member_count: _,
304            approximate_presence_count: _,
305            banner,
306            default_message_notifications,
307            description,
308            discovery_splash,
309            explicit_content_filter,
310            features,
311            guild_scheduled_events,
312            icon,
313            id,
314            joined_at,
315            large,
316            max_members,
317            max_presences,
318            max_stage_video_channel_users,
319            max_video_channel_users,
320            member_count,
321            mfa_level,
322            name,
323            nsfw_level,
324            owner,
325            owner_id,
326            permissions,
327            preferred_locale,
328            premium_progress_bar_enabled,
329            premium_subscription_count,
330            premium_tier,
331            public_updates_channel_id,
332            rules_channel_id,
333            safety_alerts_channel_id,
334            splash,
335            system_channel_flags,
336            system_channel_id,
337            unavailable,
338            vanity_url_code,
339            verification_level,
340            widget_channel_id,
341            widget_enabled,
342            ..
343        } = guild;
344
345        Self {
346            afk_channel_id,
347            afk_timeout,
348            application_id,
349            banner,
350            default_message_notifications,
351            description,
352            discovery_splash,
353            explicit_content_filter,
354            features,
355            guild_scheduled_events,
356            icon,
357            id,
358            joined_at,
359            large,
360            max_members,
361            max_presences,
362            max_stage_video_channel_users,
363            max_video_channel_users,
364            member_count,
365            mfa_level,
366            name,
367            nsfw_level,
368            owner,
369            owner_id,
370            permissions,
371            preferred_locale,
372            premium_progress_bar_enabled,
373            premium_subscription_count,
374            premium_tier,
375            public_updates_channel_id,
376            rules_channel_id,
377            safety_alerts_channel_id,
378            splash,
379            system_channel_flags,
380            system_channel_id,
381            unavailable,
382            vanity_url_code,
383            verification_level,
384            widget_channel_id,
385            widget_enabled,
386        }
387    }
388}
389
390impl PartialEq<Guild> for CachedGuild {
391    fn eq(&self, other: &Guild) -> bool {
392        self.afk_channel_id == other.afk_channel_id
393            && self.afk_timeout == other.afk_timeout
394            && self.application_id == other.application_id
395            && self.banner == other.banner
396            && self.default_message_notifications == other.default_message_notifications
397            && self.description == other.description
398            && self.discovery_splash == other.discovery_splash
399            && self.explicit_content_filter == other.explicit_content_filter
400            && self.features == other.features
401            && self.icon == other.icon
402            && self.joined_at == other.joined_at
403            && self.large == other.large
404            && self.max_members == other.max_members
405            && self.max_presences == other.max_presences
406            && self.max_video_channel_users == other.max_video_channel_users
407            && self.member_count == other.member_count
408            && self.mfa_level == other.mfa_level
409            && self.name == other.name
410            && self.nsfw_level == other.nsfw_level
411            && self.owner_id == other.owner_id
412            && self.owner == other.owner
413            && self.permissions == other.permissions
414            && self.preferred_locale == other.preferred_locale
415            && self.premium_progress_bar_enabled == other.premium_progress_bar_enabled
416            && self.premium_subscription_count == other.premium_subscription_count
417            && self.premium_tier == other.premium_tier
418            && self.public_updates_channel_id == other.public_updates_channel_id
419            && self.rules_channel_id == other.rules_channel_id
420            && self.safety_alerts_channel_id == other.safety_alerts_channel_id
421            && self.splash == other.splash
422            && self.system_channel_id == other.system_channel_id
423            && self.system_channel_flags == other.system_channel_flags
424            && self.unavailable == other.unavailable
425            && self.vanity_url_code == other.vanity_url_code
426            && self.verification_level == other.verification_level
427            && self.widget_channel_id == other.widget_channel_id
428            && self.widget_enabled == other.widget_enabled
429    }
430}
431
432impl CacheableGuild for CachedGuild {
433    fn id(&self) -> Id<GuildMarker> {
434        self.id
435    }
436
437    #[cfg(feature = "permission-calculator")]
438    fn owner_id(&self) -> Id<UserMarker> {
439        self.owner_id
440    }
441
442    fn set_unavailable(&mut self, unavailable: Option<bool>) {
443        self.unavailable = unavailable;
444    }
445
446    fn update_with_guild_update(&mut self, guild_update: &GuildUpdate) {
447        self.afk_channel_id = guild_update.afk_channel_id;
448        self.afk_timeout = guild_update.afk_timeout;
449        self.banner = guild_update.banner;
450        self.default_message_notifications = guild_update.default_message_notifications;
451        self.description.clone_from(&guild_update.description);
452        self.features.clone_from(&guild_update.features);
453        self.icon = guild_update.icon;
454        self.max_members = guild_update.max_members;
455        self.max_presences = Some(guild_update.max_presences.unwrap_or(25000));
456        self.mfa_level = guild_update.mfa_level;
457        self.name.clone_from(&guild_update.name);
458        self.nsfw_level = guild_update.nsfw_level;
459        self.owner = guild_update.owner;
460        self.owner_id = guild_update.owner_id;
461        self.permissions = guild_update.permissions;
462        self.preferred_locale
463            .clone_from(&guild_update.preferred_locale);
464        self.premium_tier = guild_update.premium_tier;
465        self.premium_subscription_count
466            .replace(guild_update.premium_subscription_count.unwrap_or_default());
467        self.splash = guild_update.splash;
468        self.system_channel_id = guild_update.system_channel_id;
469        self.verification_level = guild_update.verification_level;
470        self.vanity_url_code
471            .clone_from(&guild_update.vanity_url_code);
472        self.widget_channel_id = guild_update.widget_channel_id;
473        self.widget_enabled = guild_update.widget_enabled;
474    }
475
476    fn increase_member_count(&mut self, amount: u64) {
477        self.member_count = self.member_count.map(|count| count + amount);
478    }
479
480    fn decrease_member_count(&mut self, amount: u64) {
481        self.member_count = self.member_count.map(|count| count - amount);
482    }
483}
484
485pub struct Features<'a> {
486    inner: Iter<'a, GuildFeature>,
487}
488
489impl<'a> Iterator for Features<'a> {
490    type Item = &'a GuildFeature;
491
492    fn next(&mut self) -> Option<Self::Item> {
493        self.inner.next()
494    }
495}
496
497#[cfg(test)]
498mod tests {
499    use super::{CachedGuild, Features};
500    use serde::Serialize;
501    use static_assertions::{assert_fields, assert_impl_all};
502    use std::fmt::Debug;
503
504    assert_fields!(
505        CachedGuild: afk_channel_id,
506        afk_timeout,
507        application_id,
508        banner,
509        default_message_notifications,
510        description,
511        discovery_splash,
512        explicit_content_filter,
513        features,
514        icon,
515        id,
516        joined_at,
517        large,
518        max_members,
519        max_presences,
520        max_video_channel_users,
521        member_count,
522        mfa_level,
523        name,
524        nsfw_level,
525        owner_id,
526        owner,
527        permissions,
528        preferred_locale,
529        premium_progress_bar_enabled,
530        premium_subscription_count,
531        premium_tier,
532        rules_channel_id,
533        splash,
534        system_channel_id,
535        system_channel_flags,
536        unavailable,
537        vanity_url_code,
538        verification_level,
539        widget_channel_id,
540        widget_enabled
541    );
542    assert_impl_all!(
543        CachedGuild: Clone,
544        Debug,
545        Eq,
546        PartialEq,
547        Send,
548        Serialize,
549        Sync,
550    );
551    assert_impl_all!(Features<'_>: Iterator, Send, Sync);
552}