diff --git a/twilight-cache-inmemory/src/event/guild.rs b/twilight-cache-inmemory/src/event/guild.rs index 130ebc9da1b..376f3241f66 100644 --- a/twilight-cache-inmemory/src/event/guild.rs +++ b/twilight-cache-inmemory/src/event/guild.rs @@ -288,8 +288,9 @@ mod tests { GuildCreate, GuildUpdate, MemberAdd, MemberRemove, UnavailableGuild, }, guild::{ - DefaultMessageNotificationLevel, ExplicitContentFilter, Guild, MfaLevel, NSFWLevel, - PartialGuild, Permissions, PremiumTier, SystemChannelFlags, VerificationLevel, + AfkTimeout, DefaultMessageNotificationLevel, ExplicitContentFilter, Guild, MfaLevel, + NSFWLevel, PartialGuild, Permissions, PremiumTier, SystemChannelFlags, + VerificationLevel, }, id::Id, util::datetime::{Timestamp, TimestampParseError}, @@ -388,7 +389,7 @@ mod tests { let guild = Guild { afk_channel_id: None, - afk_timeout: 300, + afk_timeout: AfkTimeout::FIFTEEN_MINUTES, application_id: None, approximate_member_count: None, approximate_presence_count: None, diff --git a/twilight-cache-inmemory/src/model/guild.rs b/twilight-cache-inmemory/src/model/guild.rs index a953786bf6e..b6d86ffdcc8 100644 --- a/twilight-cache-inmemory/src/model/guild.rs +++ b/twilight-cache-inmemory/src/model/guild.rs @@ -3,8 +3,8 @@ use std::slice::Iter; use serde::Serialize; use twilight_model::{ guild::{ - DefaultMessageNotificationLevel, ExplicitContentFilter, GuildFeature, MfaLevel, NSFWLevel, - Permissions, PremiumTier, SystemChannelFlags, VerificationLevel, + AfkTimeout, DefaultMessageNotificationLevel, ExplicitContentFilter, GuildFeature, MfaLevel, + NSFWLevel, Permissions, PremiumTier, SystemChannelFlags, VerificationLevel, }, id::{ marker::{ApplicationMarker, ChannelMarker, GuildMarker, UserMarker}, @@ -19,7 +19,7 @@ use twilight_model::{ #[derive(Clone, Debug, Eq, PartialEq, Serialize)] pub struct CachedGuild { pub(crate) afk_channel_id: Option>, - pub(crate) afk_timeout: u64, + pub(crate) afk_timeout: AfkTimeout, pub(crate) application_id: Option>, pub(crate) banner: Option, pub(crate) default_message_notifications: DefaultMessageNotificationLevel, @@ -63,7 +63,7 @@ impl CachedGuild { } /// AFK timeout in seconds. - pub const fn afk_timeout(&self) -> u64 { + pub const fn afk_timeout(&self) -> AfkTimeout { self.afk_timeout } diff --git a/twilight-cache-inmemory/src/permission.rs b/twilight-cache-inmemory/src/permission.rs index d3f6fda0d7e..90598ce1d6c 100644 --- a/twilight-cache-inmemory/src/permission.rs +++ b/twilight-cache-inmemory/src/permission.rs @@ -660,8 +660,8 @@ mod tests { ChannelCreate, GuildCreate, MemberAdd, MemberUpdate, RoleCreate, ThreadCreate, }, guild::{ - DefaultMessageNotificationLevel, ExplicitContentFilter, Guild, MfaLevel, NSFWLevel, - Permissions, PremiumTier, Role, SystemChannelFlags, VerificationLevel, + AfkTimeout, DefaultMessageNotificationLevel, ExplicitContentFilter, Guild, MfaLevel, + NSFWLevel, Permissions, PremiumTier, Role, SystemChannelFlags, VerificationLevel, }, id::{ marker::{ChannelMarker, GuildMarker, RoleMarker, UserMarker}, @@ -708,7 +708,7 @@ mod tests { Guild { id: GUILD_ID, afk_channel_id: None, - afk_timeout: 300, + afk_timeout: AfkTimeout::FIVE_MINUTES, application_id: None, banner: None, channels: Vec::new(), diff --git a/twilight-cache-inmemory/src/test.rs b/twilight-cache-inmemory/src/test.rs index ec34154b79c..1d4a7ea0b18 100644 --- a/twilight-cache-inmemory/src/test.rs +++ b/twilight-cache-inmemory/src/test.rs @@ -12,8 +12,8 @@ use twilight_model::{ GatewayReaction, }, guild::{ - DefaultMessageNotificationLevel, Emoji, ExplicitContentFilter, Guild, Member, MfaLevel, - NSFWLevel, PartialMember, Permissions, PremiumTier, Role, SystemChannelFlags, + AfkTimeout, DefaultMessageNotificationLevel, Emoji, ExplicitContentFilter, Guild, Member, + MfaLevel, NSFWLevel, PartialMember, Permissions, PremiumTier, Role, SystemChannelFlags, VerificationLevel, }, id::{ @@ -361,7 +361,7 @@ pub fn user(id: Id) -> User { pub fn guild(id: Id, member_count: Option) -> Guild { Guild { afk_channel_id: None, - afk_timeout: 0, + afk_timeout: AfkTimeout::FIFTEEN_MINUTES, application_id: None, approximate_member_count: None, approximate_presence_count: None, diff --git a/twilight-http/src/request/guild/create_guild/mod.rs b/twilight-http/src/request/guild/create_guild/mod.rs index 4f55e61f1fe..e7f12b58673 100644 --- a/twilight-http/src/request/guild/create_guild/mod.rs +++ b/twilight-http/src/request/guild/create_guild/mod.rs @@ -14,8 +14,8 @@ use std::{ use twilight_model::{ channel::ChannelType, guild::{ - DefaultMessageNotificationLevel, ExplicitContentFilter, PartialGuild, Permissions, - SystemChannelFlags, VerificationLevel, + AfkTimeout, DefaultMessageNotificationLevel, ExplicitContentFilter, PartialGuild, + Permissions, SystemChannelFlags, VerificationLevel, }, http::permission_overwrite::PermissionOverwrite, id::{ @@ -103,7 +103,7 @@ struct CreateGuildFields<'a> { #[serde(skip_serializing_if = "Option::is_none")] afk_channel_id: Option>, #[serde(skip_serializing_if = "Option::is_none")] - afk_timeout: Option, + afk_timeout: Option, #[serde(skip_serializing_if = "Option::is_none")] channels: Option>, #[serde(skip_serializing_if = "Option::is_none")] @@ -278,7 +278,7 @@ impl<'a> CreateGuild<'a> { } /// Set the AFK timeout, in seconds. - pub const fn afk_timeout(mut self, afk_timeout: u64) -> Self { + pub const fn afk_timeout(mut self, afk_timeout: AfkTimeout) -> Self { self.fields.afk_timeout = Some(afk_timeout); self diff --git a/twilight-model/src/guild/afk_timeout.rs b/twilight-model/src/guild/afk_timeout.rs new file mode 100644 index 00000000000..866cc1fb7e0 --- /dev/null +++ b/twilight-model/src/guild/afk_timeout.rs @@ -0,0 +1,134 @@ +use serde::{Deserialize, Serialize}; +use std::time::Duration; + +/// Duration of a user being AFK before being timed out from a voice channel. +/// +/// This value is configured [for guilds][`Guild::afk_timeout`]. +/// +/// # Examples +/// +/// ``` +/// use twilight_model::guild::AfkTimeout; +/// +/// assert_eq!(300, AfkTimeout::FIVE_MINUTES); +/// ``` +/// +/// [`Guild::afk_timeout`]: super::Guild::afk_timeout +#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)] +#[non_exhaustive] +pub struct AfkTimeout(u16); + +impl AfkTimeout { + /// AFK timeout of one minute. + pub const ONE_MINUTE: Self = Self(60); + + /// AFK timeout of five minutes. + pub const FIVE_MINUTES: Self = Self(300); + + /// AFK timeout of fifteen minutes. + pub const FIFTEEN_MINUTES: Self = Self(900); + + /// AFK timeout of thirty minutes. + pub const THIRTY_MINUTES: Self = Self(1800); + + /// AFK timeout of one hour. + pub const ONE_HOUR: Self = Self(3600); + + /// Retrieve the duration of the AFK timeout in seconds. + /// + /// # Examples + /// + /// ``` + /// use twilight_model::guild::AfkTimeout; + /// + /// assert_eq!(60, AfkTimeout::ONE_MINUTE.get()); + /// ``` + pub const fn get(self) -> u16 { + self.0 + } +} + +impl From for AfkTimeout { + fn from(value: u16) -> Self { + Self(value) + } +} + +impl From for Duration { + fn from(value: AfkTimeout) -> Self { + Self::from_secs(u64::from(value.get())) + } +} + +impl PartialEq for AfkTimeout { + fn eq(&self, other: &u16) -> bool { + self.get() == *other + } +} + +impl PartialEq for u16 { + fn eq(&self, other: &AfkTimeout) -> bool { + *self == other.get() + } +} + +#[cfg(test)] +mod tests { + use super::AfkTimeout; + use serde::{Deserialize, Serialize}; + use serde_test::Token; + use static_assertions::assert_impl_all; + use std::{fmt::Debug, hash::Hash, time::Duration}; + + assert_impl_all!( + AfkTimeout: Clone, + Copy, + Debug, + Deserialize<'static>, + Eq, + Hash, + PartialEq, + Send, + Serialize, + Sync + ); + + const MAP: &[(AfkTimeout, u16)] = &[ + (AfkTimeout::ONE_MINUTE, 60), + (AfkTimeout::FIVE_MINUTES, 300), + (AfkTimeout::FIFTEEN_MINUTES, 900), + (AfkTimeout::THIRTY_MINUTES, 1800), + (AfkTimeout::ONE_HOUR, 3600), + ]; + + #[test] + fn serde() { + for (value, seconds) in MAP { + serde_test::assert_tokens( + value, + &[ + Token::NewtypeStruct { name: "AfkTimeout" }, + Token::U16(*seconds), + ], + ); + assert_eq!(*value, AfkTimeout::from(*seconds)); + assert_eq!(*seconds, value.get()); + } + } + + /// Test two-way equality implementation. + #[test] + fn eq() { + assert_eq!(300, AfkTimeout::FIVE_MINUTES); + assert_eq!(AfkTimeout::FIVE_MINUTES, 300); + } + + /// Test conversion to [`std::time::Duration`]. + #[test] + fn std_time_duration() { + for (kind, _) in MAP { + let std_duration = Duration::from(*kind); + assert_eq!(u64::from(kind.get()), std_duration.as_secs()); + } + } +} diff --git a/twilight-model/src/guild/mod.rs b/twilight-model/src/guild/mod.rs index 19a03adb272..4cce3a9f7a4 100644 --- a/twilight-model/src/guild/mod.rs +++ b/twilight-model/src/guild/mod.rs @@ -15,6 +15,7 @@ pub mod member; pub mod scheduled_event; pub mod template; +mod afk_timeout; mod ban; mod default_message_notification_level; mod emoji; @@ -46,7 +47,8 @@ mod widget; #[doc(inline)] pub use self::member::Member; pub use self::{ - ban::Ban, default_message_notification_level::DefaultMessageNotificationLevel, emoji::Emoji, + afk_timeout::AfkTimeout, ban::Ban, + default_message_notification_level::DefaultMessageNotificationLevel, emoji::Emoji, explicit_content_filter::ExplicitContentFilter, feature::GuildFeature, info::GuildInfo, integration::GuildIntegration, integration_account::IntegrationAccount, integration_application::IntegrationApplication, @@ -79,7 +81,7 @@ use std::fmt::{Formatter, Result as FmtResult}; #[derive(Clone, Debug, Eq, PartialEq, Serialize)] pub struct Guild { pub afk_channel_id: Option>, - pub afk_timeout: u64, + pub afk_timeout: AfkTimeout, pub application_id: Option>, #[serde(skip_serializing_if = "Option::is_none")] pub approximate_member_count: Option, @@ -687,7 +689,7 @@ impl<'de> Deserialize<'de> for Guild { tracing::trace!( ?afk_channel_id, - %afk_timeout, + ?afk_timeout, ?application_id, ?approximate_member_count, ?approximate_presence_count, @@ -865,8 +867,8 @@ impl<'de> Deserialize<'de> for Guild { #[cfg(test)] mod tests { use super::{ - DefaultMessageNotificationLevel, ExplicitContentFilter, Guild, GuildFeature, MfaLevel, - NSFWLevel, Permissions, PremiumTier, SystemChannelFlags, VerificationLevel, + AfkTimeout, DefaultMessageNotificationLevel, ExplicitContentFilter, Guild, GuildFeature, + MfaLevel, NSFWLevel, Permissions, PremiumTier, SystemChannelFlags, VerificationLevel, }; use crate::{ id::Id, @@ -883,7 +885,7 @@ mod tests { let value = Guild { afk_channel_id: Some(Id::new(2)), - afk_timeout: 900, + afk_timeout: AfkTimeout::FIFTEEN_MINUTES, application_id: Some(Id::new(3)), approximate_member_count: Some(1_200), approximate_presence_count: Some(900), @@ -943,7 +945,8 @@ mod tests { Token::NewtypeStruct { name: "Id" }, Token::Str("2"), Token::Str("afk_timeout"), - Token::U64(900), + Token::NewtypeStruct { name: "AfkTimeout" }, + Token::U16(900), Token::Str("application_id"), Token::Some, Token::NewtypeStruct { name: "Id" }, diff --git a/twilight-model/src/guild/partial_guild.rs b/twilight-model/src/guild/partial_guild.rs index d8a7625d7b6..e554de490ce 100644 --- a/twilight-model/src/guild/partial_guild.rs +++ b/twilight-model/src/guild/partial_guild.rs @@ -1,6 +1,6 @@ use super::{ - DefaultMessageNotificationLevel, Emoji, ExplicitContentFilter, GuildFeature, MfaLevel, - NSFWLevel, Permissions, PremiumTier, Role, SystemChannelFlags, VerificationLevel, + AfkTimeout, DefaultMessageNotificationLevel, Emoji, ExplicitContentFilter, GuildFeature, + MfaLevel, NSFWLevel, Permissions, PremiumTier, Role, SystemChannelFlags, VerificationLevel, }; use crate::{ id::{ @@ -13,9 +13,8 @@ use serde::{Deserialize, Serialize}; #[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)] pub struct PartialGuild { - pub id: Id, pub afk_channel_id: Option>, - pub afk_timeout: u64, + pub afk_timeout: AfkTimeout, pub application_id: Option>, pub banner: Option, pub default_message_notifications: DefaultMessageNotificationLevel, @@ -25,6 +24,7 @@ pub struct PartialGuild { pub explicit_content_filter: ExplicitContentFilter, pub features: Vec, pub icon: Option, + pub id: Id, #[serde(skip_serializing_if = "Option::is_none")] pub max_members: Option, #[serde(skip_serializing_if = "Option::is_none")] @@ -60,7 +60,10 @@ pub struct PartialGuild { #[cfg(test)] mod tests { - use crate::{guild::GuildFeature, test::image_hash}; + use crate::{ + guild::{AfkTimeout, GuildFeature}, + test::image_hash, + }; use super::{ DefaultMessageNotificationLevel, ExplicitContentFilter, MfaLevel, NSFWLevel, PartialGuild, @@ -73,9 +76,8 @@ mod tests { #[test] fn partial_guild() { let value = PartialGuild { - id: Id::new(1), afk_channel_id: Some(Id::new(2)), - afk_timeout: 900, + afk_timeout: AfkTimeout::FIFTEEN_MINUTES, application_id: Some(Id::new(3)), banner: Some(image_hash::BANNER), default_message_notifications: DefaultMessageNotificationLevel::Mentions, @@ -85,6 +87,7 @@ mod tests { explicit_content_filter: ExplicitContentFilter::MembersWithoutRole, features: Vec::from([GuildFeature::AnimatedBanner]), icon: Some(image_hash::ICON), + id: Id::new(1), max_members: Some(25_000), max_presences: Some(10_000), member_count: Some(12_000), @@ -116,15 +119,13 @@ mod tests { name: "PartialGuild", len: 34, }, - Token::Str("id"), - Token::NewtypeStruct { name: "Id" }, - Token::Str("1"), Token::Str("afk_channel_id"), Token::Some, Token::NewtypeStruct { name: "Id" }, Token::Str("2"), Token::Str("afk_timeout"), - Token::U64(900), + Token::NewtypeStruct { name: "AfkTimeout" }, + Token::U16(900), Token::Str("application_id"), Token::Some, Token::NewtypeStruct { name: "Id" }, @@ -152,6 +153,9 @@ mod tests { Token::Str("icon"), Token::Some, Token::Str(image_hash::ICON_INPUT), + Token::Str("id"), + Token::NewtypeStruct { name: "Id" }, + Token::Str("1"), Token::Str("max_members"), Token::Some, Token::U64(25_000), diff --git a/twilight-model/src/guild/template/guild.rs b/twilight-model/src/guild/template/guild.rs index 0f17c3798f7..067e6ef0935 100644 --- a/twilight-model/src/guild/template/guild.rs +++ b/twilight-model/src/guild/template/guild.rs @@ -2,7 +2,7 @@ use super::TemplateRole; use crate::{ channel::Channel, guild::{ - DefaultMessageNotificationLevel, ExplicitContentFilter, SystemChannelFlags, + AfkTimeout, DefaultMessageNotificationLevel, ExplicitContentFilter, SystemChannelFlags, VerificationLevel, }, id::{marker::ChannelMarker, Id}, @@ -13,7 +13,7 @@ use serde::{Deserialize, Serialize}; #[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)] pub struct TemplateGuild { pub afk_channel_id: Option>, - pub afk_timeout: u64, + pub afk_timeout: AfkTimeout, pub channels: Vec, pub default_message_notifications: DefaultMessageNotificationLevel, pub description: Option, diff --git a/twilight-model/src/guild/template/mod.rs b/twilight-model/src/guild/template/mod.rs index b75352e082c..197a32cdf84 100644 --- a/twilight-model/src/guild/template/mod.rs +++ b/twilight-model/src/guild/template/mod.rs @@ -41,7 +41,7 @@ mod tests { Channel, ChannelType, }, guild::{ - DefaultMessageNotificationLevel, ExplicitContentFilter, Permissions, + AfkTimeout, DefaultMessageNotificationLevel, ExplicitContentFilter, Permissions, SystemChannelFlags, VerificationLevel, }, id::Id, @@ -201,7 +201,7 @@ mod tests { name: "name".into(), serialized_source_guild: TemplateGuild { afk_channel_id: None, - afk_timeout: 300, + afk_timeout: AfkTimeout::FIVE_MINUTES, channels: Vec::from([ Channel { application_id: None, @@ -473,7 +473,8 @@ mod tests { Token::Str("afk_channel_id"), Token::None, Token::Str("afk_timeout"), - Token::U64(300), + Token::NewtypeStruct { name: "AfkTimeout" }, + Token::U16(300), Token::Str("channels"), Token::Seq { len: Some(4) }, Token::Struct {