diff --git a/book/src/chapter_1_crates/section_7_first_party/section_4_util.md b/book/src/chapter_1_crates/section_7_first_party/section_4_util.md index eb92bf47b54..71a68d28912 100644 --- a/book/src/chapter_1_crates/section_7_first_party/section_4_util.md +++ b/book/src/chapter_1_crates/section_7_first_party/section_4_util.md @@ -30,7 +30,7 @@ use twilight_util::builder::command::{BooleanBuilder, CommandBuilder, StringBuil CommandBuilder::new( "blep", "Send a random adorable animal photo", - CommandType::ChatInput, + CommandType::CHAT_INPUT, ) .option( StringBuilder::new("animal", "The type of animal") diff --git a/examples/model-webhook-slash.rs b/examples/model-webhook-slash.rs index 5f0fa9b144c..81cec58d965 100644 --- a/examples/model-webhook-slash.rs +++ b/examples/model-webhook-slash.rs @@ -93,9 +93,9 @@ where match interaction.kind { // Return a Pong if a Ping is received. - InteractionType::Ping => { + InteractionType::PING => { let response = InteractionResponse { - kind: InteractionResponseType::Pong, + kind: InteractionResponseType::PONG, data: None, }; @@ -107,7 +107,7 @@ where .body(json.into())?) } // Respond to a slash command. - InteractionType::ApplicationCommand => { + InteractionType::APPLICATION_COMMAND => { // Run the handler to gain a response. let data = match interaction.data { Some(InteractionData::ApplicationCommand(data)) => Some(data), @@ -144,7 +144,7 @@ async fn handler(data: Box) -> anyhow::Result /// Example of a handler that returns the formatted version of the interaction. async fn debug(data: Box) -> anyhow::Result { Ok(InteractionResponse { - kind: InteractionResponseType::ChannelMessageWithSource, + kind: InteractionResponseType::CHANNEL_MESSAGE_WITH_SOURCE, data: Some(InteractionResponseData { content: Some(format!("```rust\n{data:?}\n```")), ..Default::default() @@ -155,7 +155,7 @@ async fn debug(data: Box) -> anyhow::Result { /// Example of interaction that responds with a message saying "Vroom vroom". async fn vroom(_: Box) -> anyhow::Result { Ok(InteractionResponse { - kind: InteractionResponseType::ChannelMessageWithSource, + kind: InteractionResponseType::CHANNEL_MESSAGE_WITH_SOURCE, data: Some(InteractionResponseData { content: Some("Vroom vroom".to_owned()), ..Default::default() diff --git a/twilight-cache-inmemory/src/event/guild.rs b/twilight-cache-inmemory/src/event/guild.rs index 9d5503f67e8..8c8c502ee8b 100644 --- a/twilight-cache-inmemory/src/event/guild.rs +++ b/twilight-cache-inmemory/src/event/guild.rs @@ -320,7 +320,7 @@ mod tests { icon: None, id: Id::new(111), invitable: None, - kind: ChannelType::GuildText, + kind: ChannelType::GUILD_TEXT, last_message_id: None, last_pin_timestamp: None, member: None, @@ -357,7 +357,7 @@ mod tests { icon: None, id: Id::new(222), invitable: None, - kind: ChannelType::PublicThread, + kind: ChannelType::PUBLIC_THREAD, last_message_id: None, last_pin_timestamp: None, member: Some(ThreadMember { @@ -382,7 +382,7 @@ mod tests { rtc_region: None, thread_metadata: Some(ThreadMetadata { archived: false, - auto_archive_duration: AutoArchiveDuration::Hour, + auto_archive_duration: AutoArchiveDuration::HOUR, archive_timestamp: timestamp, create_timestamp: Some(timestamp), invitable: None, @@ -401,11 +401,11 @@ mod tests { approximate_presence_count: None, banner: None, channels, - default_message_notifications: DefaultMessageNotificationLevel::Mentions, + default_message_notifications: DefaultMessageNotificationLevel::MENTIONS, description: None, discovery_splash: None, emojis: Vec::new(), - explicit_content_filter: ExplicitContentFilter::AllMembers, + explicit_content_filter: ExplicitContentFilter::ALL_MEMBERS, features: vec![], icon: None, id: Id::new(123), @@ -416,16 +416,16 @@ mod tests { max_video_channel_users: None, member_count: Some(25), members: Vec::new(), - mfa_level: MfaLevel::Elevated, + mfa_level: MfaLevel::ELEVATED, name: "this is a guild".to_owned(), - nsfw_level: NSFWLevel::AgeRestricted, + nsfw_level: NSFWLevel::AGE_RESTRICTED, owner_id: Id::new(456), owner: Some(false), permissions: Some(Permissions::SEND_MESSAGES), preferred_locale: "en-GB".to_owned(), premium_progress_bar_enabled: true, premium_subscription_count: Some(0), - premium_tier: PremiumTier::None, + premium_tier: PremiumTier::NONE, presences: Vec::new(), public_updates_channel_id: None, roles: Vec::new(), @@ -438,7 +438,7 @@ mod tests { threads, unavailable: false, vanity_url_code: None, - verification_level: VerificationLevel::VeryHigh, + verification_level: VerificationLevel::VERY_HIGH, voice_states: Vec::new(), widget_channel_id: None, widget_enabled: None, diff --git a/twilight-cache-inmemory/src/event/interaction.rs b/twilight-cache-inmemory/src/event/interaction.rs index b7f9906e2d4..d0fddc86e8b 100644 --- a/twilight-cache-inmemory/src/event/interaction.rs +++ b/twilight-cache-inmemory/src/event/interaction.rs @@ -106,7 +106,7 @@ mod tests { guild_id: None, id: Id::new(5), name: "command name".into(), - kind: CommandType::ChatInput, // This isn't actually a valid command, so just mark it as a slash command. + kind: CommandType::CHAT_INPUT, // This isn't actually a valid command, so just mark it as a slash command. options: Vec::new(), resolved: Some(CommandInteractionDataResolved { attachments: HashMap::new(), @@ -158,7 +158,7 @@ mod tests { guild_id: Some(Id::new(1)), id: Id::new(4), interaction: None, - kind: MessageType::Regular, + kind: MessageType::REGULAR, member: Some(PartialMember { avatar: None, communication_disabled_until: None, @@ -181,7 +181,7 @@ mod tests { reference: None, role_subscription_data: None, sticker_items: vec![MessageSticker { - format_type: StickerFormatType::Png, + format_type: StickerFormatType::PNG, id: Id::new(1), name: "sticker name".to_owned(), }], @@ -234,7 +234,7 @@ mod tests { guild_id: Some(Id::new(3)), guild_locale: None, id: Id::new(4), - kind: InteractionType::ApplicationCommand, + kind: InteractionType::APPLICATION_COMMAND, locale: Some("en-GB".to_owned()), member: Some(PartialMember { avatar: None, diff --git a/twilight-cache-inmemory/src/event/message.rs b/twilight-cache-inmemory/src/event/message.rs index b27be8effd6..e20e95f3a50 100644 --- a/twilight-cache-inmemory/src/event/message.rs +++ b/twilight-cache-inmemory/src/event/message.rs @@ -181,7 +181,7 @@ mod tests { guild_id: Some(Id::new(1)), id: Id::new(4), interaction: None, - kind: MessageType::Regular, + kind: MessageType::REGULAR, member: Some(PartialMember { avatar: None, communication_disabled_until: None, diff --git a/twilight-cache-inmemory/src/event/presence.rs b/twilight-cache-inmemory/src/event/presence.rs index 54873eba6cc..e335fdfb98c 100644 --- a/twilight-cache-inmemory/src/event/presence.rs +++ b/twilight-cache-inmemory/src/event/presence.rs @@ -60,12 +60,12 @@ mod tests { let payload = PresenceUpdate(Presence { activities: Vec::new(), client_status: ClientStatus { - desktop: Some(Status::Online), + desktop: Some(Status::ONLINE), mobile: None, web: None, }, guild_id, - status: Status::Online, + status: Status::ONLINE, user: UserOrId::User(test::user(user_id)), }); cache.update(&Event::PresenceUpdate(Box::new(payload))); diff --git a/twilight-cache-inmemory/src/event/stage_instance.rs b/twilight-cache-inmemory/src/event/stage_instance.rs index c90e0f91d84..7e11068cc61 100644 --- a/twilight-cache-inmemory/src/event/stage_instance.rs +++ b/twilight-cache-inmemory/src/event/stage_instance.rs @@ -94,7 +94,7 @@ mod tests { guild_id: Id::new(2), guild_scheduled_event_id: Some(Id::new(3)), id: Id::new(4), - privacy_level: PrivacyLevel::GuildOnly, + privacy_level: PrivacyLevel::GUILD_ONLY, topic: "topic".into(), }; diff --git a/twilight-cache-inmemory/src/model/sticker.rs b/twilight-cache-inmemory/src/model/sticker.rs index 01f2f92891f..eff81803cb2 100644 --- a/twilight-cache-inmemory/src/model/sticker.rs +++ b/twilight-cache-inmemory/src/model/sticker.rs @@ -190,10 +190,10 @@ mod tests { let sticker = Sticker { available: true, description: Some("sticker".into()), - format_type: StickerFormatType::Png, + format_type: StickerFormatType::PNG, guild_id: Some(Id::new(1)), id: Id::new(2), - kind: StickerType::Guild, + kind: StickerType::GUILD, name: "stick".into(), pack_id: Some(Id::new(3)), sort_value: Some(1), @@ -210,7 +210,7 @@ mod tests { locale: Some("en-us".to_owned()), mfa_enabled: Some(true), name: "test".to_owned(), - premium_type: Some(PremiumType::Nitro), + premium_type: Some(PremiumType::NITRO), public_flags: Some( UserFlags::PREMIUM_EARLY_SUPPORTER | UserFlags::VERIFIED_DEVELOPER, ), @@ -222,10 +222,10 @@ mod tests { let cached = CachedSticker { available: true, description: "sticker".into(), - format_type: StickerFormatType::Png, + format_type: StickerFormatType::PNG, guild_id: Some(Id::new(1)), id: Id::new(2), - kind: StickerType::Guild, + kind: StickerType::GUILD, name: "stick".into(), pack_id: Some(Id::new(3)), sort_value: Some(1), diff --git a/twilight-cache-inmemory/src/permission.rs b/twilight-cache-inmemory/src/permission.rs index 008053f8338..75e63a919c0 100644 --- a/twilight-cache-inmemory/src/permission.rs +++ b/twilight-cache-inmemory/src/permission.rs @@ -419,9 +419,9 @@ impl<'a> InMemoryCachePermissions<'a> { .map_err(ChannelError::from_member_roles)?; let overwrites = match channel.kind { - ChannelType::AnnouncementThread - | ChannelType::PrivateThread - | ChannelType::PublicThread => self.parent_overwrites(&channel)?, + ChannelType::ANNOUNCEMENT_THREAD + | ChannelType::PRIVATE_THREAD + | ChannelType::PUBLIC_THREAD => self.parent_overwrites(&channel)?, _ => channel.permission_overwrites.clone().unwrap_or_default(), }; @@ -715,11 +715,11 @@ mod tests { application_id: None, banner: None, channels: Vec::new(), - default_message_notifications: DefaultMessageNotificationLevel::Mentions, + default_message_notifications: DefaultMessageNotificationLevel::MENTIONS, description: None, discovery_splash: None, emojis: Vec::new(), - explicit_content_filter: ExplicitContentFilter::AllMembers, + explicit_content_filter: ExplicitContentFilter::ALL_MEMBERS, features: Vec::new(), icon: None, joined_at: None, @@ -728,16 +728,16 @@ mod tests { max_presences: None, member_count: None, members: Vec::new(), - mfa_level: MfaLevel::Elevated, + mfa_level: MfaLevel::ELEVATED, name: "this is a guild".to_owned(), - nsfw_level: NSFWLevel::AgeRestricted, + nsfw_level: NSFWLevel::AGE_RESTRICTED, owner: Some(false), owner_id: OWNER_ID, permissions: None, preferred_locale: "en-GB".to_owned(), premium_progress_bar_enabled: false, premium_subscription_count: Some(0), - premium_tier: PremiumTier::None, + premium_tier: PremiumTier::NONE, presences: Vec::new(), public_updates_channel_id: None, roles: Vec::from([ @@ -756,7 +756,7 @@ mod tests { threads: Vec::new(), rules_channel_id: None, unavailable: false, - verification_level: VerificationLevel::VeryHigh, + verification_level: VerificationLevel::VERY_HIGH, voice_states: Vec::new(), vanity_url_code: None, widget_channel_id: None, @@ -783,7 +783,7 @@ mod tests { icon: None, id: CHANNEL_ID, invitable: None, - kind: ChannelType::GuildText, + kind: ChannelType::GUILD_TEXT, last_message_id: None, last_pin_timestamp: None, member: None, @@ -799,13 +799,13 @@ mod tests { allow: Permissions::empty(), deny: Permissions::CREATE_INVITE, id: EVERYONE_ROLE_ID.cast(), - kind: PermissionOverwriteType::Role, + kind: PermissionOverwriteType::ROLE, }, PermissionOverwrite { allow: Permissions::EMBED_LINKS, deny: Permissions::empty(), id: USER_ID.cast(), - kind: PermissionOverwriteType::Member, + kind: PermissionOverwriteType::MEMBER, }, ])), position: Some(0), @@ -835,7 +835,7 @@ mod tests { icon: None, id: THREAD_ID, invitable: None, - kind: ChannelType::PublicThread, + kind: ChannelType::PUBLIC_THREAD, last_message_id: None, last_pin_timestamp: None, member: None, @@ -850,7 +850,7 @@ mod tests { allow: Permissions::ATTACH_FILES, deny: Permissions::empty(), id: EVERYONE_ROLE_ID.cast(), - kind: PermissionOverwriteType::Role, + kind: PermissionOverwriteType::ROLE, }])), position: Some(0), rate_limit_per_user: None, diff --git a/twilight-cache-inmemory/src/test.rs b/twilight-cache-inmemory/src/test.rs index 15715a7a50d..dd934dff3b8 100644 --- a/twilight-cache-inmemory/src/test.rs +++ b/twilight-cache-inmemory/src/test.rs @@ -67,7 +67,7 @@ pub fn cache_with_message_and_reactions() -> InMemoryCache { guild_id: Some(Id::new(1)), id: Id::new(4), interaction: None, - kind: MessageType::Regular, + kind: MessageType::REGULAR, member: Some(PartialMember { avatar: None, communication_disabled_until: None, @@ -243,7 +243,7 @@ pub fn guild_channel_text() -> (Id, Id, Channel) { icon: None, id: channel_id, invitable: None, - kind: ChannelType::GuildText, + kind: ChannelType::GUILD_TEXT, last_message_id: None, last_pin_timestamp: None, member: None, @@ -307,10 +307,10 @@ pub const fn sticker(id: Id, guild_id: Id) -> Sticke Sticker { available: false, description: None, - format_type: StickerFormatType::Png, + format_type: StickerFormatType::PNG, guild_id: Some(guild_id), id, - kind: StickerType::Standard, + kind: StickerType::STANDARD, name: String::new(), pack_id: None, sort_value: None, @@ -373,11 +373,11 @@ pub fn guild(id: Id, member_count: Option) -> Guild { approximate_presence_count: None, banner: None, channels: Vec::new(), - default_message_notifications: DefaultMessageNotificationLevel::Mentions, + default_message_notifications: DefaultMessageNotificationLevel::MENTIONS, description: None, discovery_splash: None, emojis: Vec::new(), - explicit_content_filter: ExplicitContentFilter::None, + explicit_content_filter: ExplicitContentFilter::NONE, features: Vec::new(), icon: None, id, @@ -388,16 +388,16 @@ pub fn guild(id: Id, member_count: Option) -> Guild { max_video_channel_users: None, member_count, members: Vec::new(), - mfa_level: MfaLevel::None, + mfa_level: MfaLevel::NONE, name: "test".to_owned(), - nsfw_level: NSFWLevel::Default, + nsfw_level: NSFWLevel::DEFAULT, owner_id: Id::new(1), owner: None, permissions: None, preferred_locale: "en_us".to_owned(), premium_progress_bar_enabled: false, premium_subscription_count: None, - premium_tier: PremiumTier::None, + premium_tier: PremiumTier::NONE, presences: Vec::new(), public_updates_channel_id: None, roles: Vec::new(), @@ -410,7 +410,7 @@ pub fn guild(id: Id, member_count: Option) -> Guild { threads: Vec::new(), unavailable: false, vanity_url_code: None, - verification_level: VerificationLevel::VeryHigh, + verification_level: VerificationLevel::VERY_HIGH, voice_states: Vec::new(), widget_channel_id: None, widget_enabled: None, diff --git a/twilight-gateway/src/config.rs b/twilight-gateway/src/config.rs index ae2b51fe41c..6c326b0c115 100644 --- a/twilight-gateway/src/config.rs +++ b/twilight-gateway/src/config.rs @@ -306,14 +306,14 @@ impl ConfigBuilder { /// let config = Config::builder(env::var("DISCORD_TOKEN")?, Intents::empty()) /// .presence(UpdatePresencePayload::new( /// vec![MinimalActivity { - /// kind: ActivityType::Playing, + /// kind: ActivityType::PLAYING, /// name: "Not accepting commands".into(), /// url: None, /// } /// .into()], /// false, /// None, - /// Status::Idle, + /// Status::IDLE, /// )?) /// .build(); /// diff --git a/twilight-gateway/src/error.rs b/twilight-gateway/src/error.rs index 98d757c149a..50e74768fd8 100644 --- a/twilight-gateway/src/error.rs +++ b/twilight-gateway/src/error.rs @@ -167,7 +167,11 @@ impl Display for ReceiveMessageError { ReceiveMessageErrorType::FatallyClosed { close_code } => { f.write_str("shard fatally closed: ")?; - Display::fmt(&close_code, f) + if let Some(name) = close_code.name() { + Display::fmt(&name, f) + } else { + Display::fmt(&close_code.get(), f) + } } ReceiveMessageErrorType::Io => f.write_str("websocket connection error"), ReceiveMessageErrorType::Process => { @@ -338,9 +342,9 @@ mod tests { ), ( ReceiveMessageErrorType::FatallyClosed { - close_code: CloseCode::InvalidIntents, + close_code: CloseCode::INVALID_INTENTS, }, - "shard fatally closed: Invalid Intents", + "shard fatally closed: INVALID_INTENTS", ), (ReceiveMessageErrorType::Io, "websocket connection error"), ( @@ -368,7 +372,7 @@ mod tests { fn receive_message_error_is_fatal() { let fatal = ReceiveMessageError { kind: ReceiveMessageErrorType::FatallyClosed { - close_code: CloseCode::AuthenticationFailed, + close_code: CloseCode::AUTHENTICATION_FAILED, }, source: None, }; diff --git a/twilight-gateway/src/event.rs b/twilight-gateway/src/event.rs index 7647f810625..d4e7e8fa034 100644 --- a/twilight-gateway/src/event.rs +++ b/twilight-gateway/src/event.rs @@ -322,76 +322,76 @@ bitflags! { impl From for EventTypeFlags { fn from(event_type: EventType) -> Self { match event_type { - EventType::AutoModerationActionExecution => Self::AUTO_MODERATION_ACTION_EXECUTION, - EventType::AutoModerationRuleCreate => Self::AUTO_MODERATION_RULE_CREATE, - EventType::AutoModerationRuleDelete => Self::AUTO_MODERATION_RULE_DELETE, - EventType::AutoModerationRuleUpdate => Self::AUTO_MODERATION_RULE_UPDATE, - EventType::BanAdd => Self::BAN_ADD, - EventType::BanRemove => Self::BAN_REMOVE, - EventType::ChannelCreate => Self::CHANNEL_CREATE, - EventType::ChannelDelete => Self::CHANNEL_DELETE, - EventType::ChannelPinsUpdate => Self::CHANNEL_PINS_UPDATE, - EventType::ChannelUpdate => Self::CHANNEL_UPDATE, - EventType::CommandPermissionsUpdate => Self::COMMAND_PERMISSIONS_UPDATE, - EventType::GatewayClose => Self::empty(), - EventType::GatewayHeartbeat => Self::GATEWAY_HEARTBEAT, - EventType::GatewayHeartbeatAck => Self::GATEWAY_HEARTBEAT_ACK, - EventType::GatewayHello => Self::GATEWAY_HELLO, - EventType::GatewayInvalidateSession => Self::GATEWAY_INVALIDATE_SESSION, - EventType::GatewayReconnect => Self::GATEWAY_RECONNECT, - EventType::GiftCodeUpdate => Self::GIFT_CODE_UPDATE, - EventType::GuildAuditLogEntryCreate => Self::GUILD_AUDIT_LOG_ENTRY_CREATE, - EventType::GuildCreate => Self::GUILD_CREATE, - EventType::GuildDelete => Self::GUILD_DELETE, - EventType::GuildEmojisUpdate => Self::GUILD_EMOJIS_UPDATE, - EventType::GuildIntegrationsUpdate => Self::GUILD_INTEGRATIONS_UPDATE, - EventType::GuildScheduledEventCreate => Self::GUILD_SCHEDULED_EVENT_CREATE, - EventType::GuildScheduledEventDelete => Self::GUILD_SCHEDULED_EVENT_DELETE, - EventType::GuildScheduledEventUpdate => Self::GUILD_SCHEDULED_EVENT_UPDATE, - EventType::GuildScheduledEventUserAdd => Self::GUILD_SCHEDULED_EVENT_USER_ADD, - EventType::GuildScheduledEventUserRemove => Self::GUILD_SCHEDULED_EVENT_USER_REMOVE, - EventType::GuildStickersUpdate => Self::GUILD_STICKERS_UPDATE, - EventType::GuildUpdate => Self::GUILD_UPDATE, - EventType::IntegrationCreate => Self::INTEGRATION_CREATE, - EventType::IntegrationDelete => Self::INTEGRATION_DELETE, - EventType::IntegrationUpdate => Self::INTEGRATION_UPDATE, - EventType::InteractionCreate => Self::INTERACTION_CREATE, - EventType::InviteCreate => Self::INVITE_CREATE, - EventType::InviteDelete => Self::INVITE_DELETE, - EventType::MemberAdd => Self::MEMBER_ADD, - EventType::MemberRemove => Self::MEMBER_REMOVE, - EventType::MemberUpdate => Self::MEMBER_UPDATE, - EventType::MemberChunk => Self::MEMBER_CHUNK, - EventType::MessageCreate => Self::MESSAGE_CREATE, - EventType::MessageDelete => Self::MESSAGE_DELETE, - EventType::MessageDeleteBulk => Self::MESSAGE_DELETE_BULK, - EventType::MessageUpdate => Self::MESSAGE_UPDATE, - EventType::PresenceUpdate => Self::PRESENCE_UPDATE, - EventType::PresencesReplace => Self::PRESENCES_REPLACE, - EventType::ReactionAdd => Self::REACTION_ADD, - EventType::ReactionRemove => Self::REACTION_REMOVE, - EventType::ReactionRemoveAll => Self::REACTION_REMOVE_ALL, - EventType::ReactionRemoveEmoji => Self::REACTION_REMOVE_EMOJI, - EventType::Ready => Self::READY, - EventType::Resumed => Self::RESUMED, - EventType::RoleCreate => Self::ROLE_CREATE, - EventType::RoleDelete => Self::ROLE_DELETE, - EventType::RoleUpdate => Self::ROLE_UPDATE, - EventType::StageInstanceCreate => Self::STAGE_INSTANCE_CREATE, - EventType::StageInstanceDelete => Self::STAGE_INSTANCE_DELETE, - EventType::StageInstanceUpdate => Self::STAGE_INSTANCE_UPDATE, - EventType::ThreadCreate => Self::THREAD_CREATE, - EventType::ThreadDelete => Self::THREAD_DELETE, - EventType::ThreadListSync => Self::THREAD_LIST_SYNC, - EventType::ThreadMembersUpdate => Self::THREAD_MEMBERS_UPDATE, - EventType::ThreadMemberUpdate => Self::THREAD_MEMBER_UPDATE, - EventType::ThreadUpdate => Self::THREAD_UPDATE, - EventType::TypingStart => Self::TYPING_START, - EventType::UnavailableGuild => Self::UNAVAILABLE_GUILD, - EventType::UserUpdate => Self::USER_UPDATE, - EventType::VoiceServerUpdate => Self::VOICE_SERVER_UPDATE, - EventType::VoiceStateUpdate => Self::VOICE_STATE_UPDATE, - EventType::WebhooksUpdate => Self::WEBHOOKS_UPDATE, + EventType::AUTO_MODERATION_ACTION_EXECUTION => Self::AUTO_MODERATION_ACTION_EXECUTION, + EventType::AUTO_MODERATION_RULE_CREATE => Self::AUTO_MODERATION_RULE_CREATE, + EventType::AUTO_MODERATION_RULE_DELETE => Self::AUTO_MODERATION_RULE_DELETE, + EventType::AUTO_MODERATION_RULE_UPDATE => Self::AUTO_MODERATION_RULE_UPDATE, + EventType::BAN_ADD => Self::BAN_ADD, + EventType::BAN_REMOVE => Self::BAN_REMOVE, + EventType::CHANNEL_CREATE => Self::CHANNEL_CREATE, + EventType::CHANNEL_DELETE => Self::CHANNEL_DELETE, + EventType::CHANNEL_PINS_UPDATE => Self::CHANNEL_PINS_UPDATE, + EventType::CHANNEL_UPDATE => Self::CHANNEL_UPDATE, + EventType::COMMAND_PERMISSIONS_UPDATE => Self::COMMAND_PERMISSIONS_UPDATE, + EventType::GATEWAY_HEARTBEAT => Self::GATEWAY_HEARTBEAT, + EventType::GATEWAY_HEARTBEAT_ACK => Self::GATEWAY_HEARTBEAT_ACK, + EventType::GATEWAY_HELLO => Self::GATEWAY_HELLO, + EventType::GATEWAY_INVALIDATE_SESSION => Self::GATEWAY_INVALIDATE_SESSION, + EventType::GATEWAY_RECONNECT => Self::GATEWAY_RECONNECT, + EventType::GIFT_CODE_UPDATE => Self::GIFT_CODE_UPDATE, + EventType::GUILD_AUDIT_LOG_ENTRY_CREATE => Self::GUILD_AUDIT_LOG_ENTRY_CREATE, + EventType::GUILD_CREATE => Self::GUILD_CREATE, + EventType::GUILD_DELETE => Self::GUILD_DELETE, + EventType::GUILD_EMOJIS_UPDATE => Self::GUILD_EMOJIS_UPDATE, + EventType::GUILD_INTEGRATIONS_UPDATE => Self::GUILD_INTEGRATIONS_UPDATE, + EventType::GUILD_SCHEDULED_EVENT_CREATE => Self::GUILD_SCHEDULED_EVENT_CREATE, + EventType::GUILD_SCHEDULED_EVENT_DELETE => Self::GUILD_SCHEDULED_EVENT_DELETE, + EventType::GUILD_SCHEDULED_EVENT_UPDATE => Self::GUILD_SCHEDULED_EVENT_UPDATE, + EventType::GUILD_SCHEDULED_EVENT_USER_ADD => Self::GUILD_SCHEDULED_EVENT_USER_ADD, + EventType::GUILD_SCHEDULED_EVENT_USER_REMOVE => Self::GUILD_SCHEDULED_EVENT_USER_REMOVE, + EventType::GUILD_STICKERS_UPDATE => Self::GUILD_STICKERS_UPDATE, + EventType::GUILD_UPDATE => Self::GUILD_UPDATE, + EventType::INTEGRATION_CREATE => Self::INTEGRATION_CREATE, + EventType::INTEGRATION_DELETE => Self::INTEGRATION_DELETE, + EventType::INTEGRATION_UPDATE => Self::INTEGRATION_UPDATE, + EventType::INTERACTION_CREATE => Self::INTERACTION_CREATE, + EventType::INVITE_CREATE => Self::INVITE_CREATE, + EventType::INVITE_DELETE => Self::INVITE_DELETE, + EventType::MEMBER_ADD => Self::MEMBER_ADD, + EventType::MEMBER_REMOVE => Self::MEMBER_REMOVE, + EventType::MEMBER_UPDATE => Self::MEMBER_UPDATE, + EventType::MEMBER_CHUNK => Self::MEMBER_CHUNK, + EventType::MESSAGE_CREATE => Self::MESSAGE_CREATE, + EventType::MESSAGE_DELETE => Self::MESSAGE_DELETE, + EventType::MESSAGE_DELETE_BULK => Self::MESSAGE_DELETE_BULK, + EventType::MESSAGE_UPDATE => Self::MESSAGE_UPDATE, + EventType::PRESENCE_UPDATE => Self::PRESENCE_UPDATE, + EventType::PRESENCES_REPLACE => Self::PRESENCES_REPLACE, + EventType::REACTION_ADD => Self::REACTION_ADD, + EventType::REACTION_REMOVE => Self::REACTION_REMOVE, + EventType::REACTION_REMOVE_ALL => Self::REACTION_REMOVE_ALL, + EventType::REACTION_REMOVE_EMOJI => Self::REACTION_REMOVE_EMOJI, + EventType::READY => Self::READY, + EventType::RESUMED => Self::RESUMED, + EventType::ROLE_CREATE => Self::ROLE_CREATE, + EventType::ROLE_DELETE => Self::ROLE_DELETE, + EventType::ROLE_UPDATE => Self::ROLE_UPDATE, + EventType::STAGE_INSTANCE_CREATE => Self::STAGE_INSTANCE_CREATE, + EventType::STAGE_INSTANCE_DELETE => Self::STAGE_INSTANCE_DELETE, + EventType::STAGE_INSTANCE_UPDATE => Self::STAGE_INSTANCE_UPDATE, + EventType::THREAD_CREATE => Self::THREAD_CREATE, + EventType::THREAD_DELETE => Self::THREAD_DELETE, + EventType::THREAD_LIST_SYNC => Self::THREAD_LIST_SYNC, + EventType::THREAD_MEMBERS_UPDATE => Self::THREAD_MEMBERS_UPDATE, + EventType::THREAD_MEMBER_UPDATE => Self::THREAD_MEMBER_UPDATE, + EventType::THREAD_UPDATE => Self::THREAD_UPDATE, + EventType::TYPING_START => Self::TYPING_START, + EventType::UNAVAILABLE_GUILD => Self::UNAVAILABLE_GUILD, + EventType::USER_UPDATE => Self::USER_UPDATE, + EventType::VOICE_SERVER_UPDATE => Self::VOICE_SERVER_UPDATE, + EventType::VOICE_STATE_UPDATE => Self::VOICE_STATE_UPDATE, + EventType::WEBHOOKS_UPDATE => Self::WEBHOOKS_UPDATE, + _ => Self::empty(), } } } @@ -401,11 +401,11 @@ impl TryFrom<(OpCode, Option<&str>)> for EventTypeFlags { fn try_from((op, event_type): (OpCode, Option<&str>)) -> Result { match (op, event_type) { - (OpCode::Heartbeat, _) => Ok(Self::GATEWAY_HEARTBEAT), - (OpCode::Reconnect, _) => Ok(Self::GATEWAY_RECONNECT), - (OpCode::InvalidSession, _) => Ok(Self::GATEWAY_INVALIDATE_SESSION), - (OpCode::Hello, _) => Ok(Self::GATEWAY_HELLO), - (OpCode::HeartbeatAck, _) => Ok(Self::GATEWAY_HEARTBEAT_ACK), + (OpCode::HEARTBEAT, _) => Ok(Self::GATEWAY_HEARTBEAT), + (OpCode::RECONNECT, _) => Ok(Self::GATEWAY_RECONNECT), + (OpCode::INVALID_SESSION, _) => Ok(Self::GATEWAY_INVALIDATE_SESSION), + (OpCode::HELLO, _) => Ok(Self::GATEWAY_HELLO), + (OpCode::HEARTBEAT_ACK, _) => Ok(Self::GATEWAY_HEARTBEAT_ACK), (_, Some(event_type)) => EventType::try_from(event_type) .map(Self::from) .map_err(|_| ()), diff --git a/twilight-gateway/src/json.rs b/twilight-gateway/src/json.rs index ec1c93f6c7c..e4d59f89bf6 100644 --- a/twilight-gateway/src/json.rs +++ b/twilight-gateway/src/json.rs @@ -47,23 +47,14 @@ pub fn parse( }); }; - let opcode = if let Some(opcode) = OpCode::from(gateway_deserializer.op()) { - opcode - } else { - let opcode = gateway_deserializer.op(); - - return Err(ReceiveMessageError { - kind: ReceiveMessageErrorType::Deserializing { event }, - source: Some(format!("unknown opcode: {opcode}").into()), - }); - }; + let opcode = OpCode::new(gateway_deserializer.op()); let event_type = gateway_deserializer.event_type(); let event_type = if let Ok(event_type) = EventTypeFlags::try_from((opcode, event_type)) { event_type } else { - let opcode = opcode as u8; + let opcode = gateway_deserializer.op(); let source = format!("unknown opcode/dispatch event type: {opcode}/{event_type:?}"); return Err(ReceiveMessageError { diff --git a/twilight-gateway/src/shard.rs b/twilight-gateway/src/shard.rs index d7eda3f9254..aae0349cb1f 100644 --- a/twilight-gateway/src/shard.rs +++ b/twilight-gateway/src/shard.rs @@ -156,8 +156,8 @@ pub enum ConnectionStatus { /// [invalid intents], or other reasons. Refer to the documentation for /// [`CloseCode`] for possible reasons. /// - /// [failed authentication]: CloseCode::AuthenticationFailed - /// [invalid intents]: CloseCode::InvalidIntents + /// [failed authentication]: CloseCode::AUTHENTICATION_FAILED + /// [invalid intents]: CloseCode::INVALID_INTENTS FatallyClosed { /// Close code of the close message. close_code: CloseCode, @@ -961,7 +961,7 @@ impl Shard { } match OpCode::from(raw_opcode) { - Some(OpCode::Dispatch) => { + OpCode::DISPATCH => { let event_type = maybe_event_type.ok_or(ProcessError { kind: ProcessErrorType::Deserializing { event: event.to_owned(), @@ -1011,11 +1011,11 @@ impl Shard { tracing::info!("unable to store sequence"); } } - Some(OpCode::Heartbeat) => { + OpCode::HEARTBEAT => { tracing::debug!("received heartbeat"); self.heartbeat().await.map_err(ProcessError::from_send)?; } - Some(OpCode::HeartbeatAck) => { + OpCode::HEARTBEAT_ACK => { let requested = self.latency.received().is_none() && self.latency.sent().is_some(); if requested { tracing::debug!("received heartbeat ack"); @@ -1024,7 +1024,7 @@ impl Shard { tracing::info!("received unrequested heartbeat ack"); } } - Some(OpCode::Hello) => { + OpCode::HELLO => { let event = Self::parse_event::(event)?; let heartbeat_interval = Duration::from_millis(event.data.heartbeat_interval); // First heartbeat should have some jitter, see @@ -1066,7 +1066,7 @@ impl Shard { } } } - Some(OpCode::InvalidSession) => { + OpCode::INVALID_SESSION => { let resumable = Self::parse_event(event)?.data; tracing::debug!(resumable, "received invalid session"); if resumable { @@ -1080,7 +1080,7 @@ impl Shard { .map_err(ProcessError::from_send)?; } } - Some(OpCode::Reconnect) => { + OpCode::RECONNECT => { tracing::debug!("received reconnect"); self.session = self .close(CloseFrame::RESUME) @@ -1200,7 +1200,7 @@ mod tests { } ); - let non_fatal_code = CloseCode::SessionTimedOut as u16; + let non_fatal_code = CloseCode::SESSION_TIMED_OUT.get(); let non_fatal_status = ConnectionStatus::from_close_code(Some(non_fatal_code)); assert_eq!( @@ -1211,8 +1211,8 @@ mod tests { } ); - let fatal_code = CloseCode::AuthenticationFailed; - let fatal_status = ConnectionStatus::from_close_code(Some(fatal_code as u16)); + let fatal_code = CloseCode::AUTHENTICATION_FAILED; + let fatal_status = ConnectionStatus::from_close_code(Some(fatal_code.get())); assert_eq!( fatal_status, diff --git a/twilight-http/src/client/mod.rs b/twilight-http/src/client/mod.rs index f07d07462cd..b76a614a07e 100644 --- a/twilight-http/src/client/mod.rs +++ b/twilight-http/src/client/mod.rs @@ -363,7 +363,7 @@ impl Client { /// /// let guild_id = Id::new(1); /// client - /// .create_auto_moderation_rule(guild_id, "no darns", AutoModerationEventType::MessageSend) + /// .create_auto_moderation_rule(guild_id, "no darns", AutoModerationEventType::MESSAGE_SEND) /// .action_block_message() /// .enabled(true) /// .with_keyword(&["darn"]) @@ -648,7 +648,7 @@ impl Client { /// allow: Some(Permissions::VIEW_CHANNEL), /// deny: Some(Permissions::SEND_MESSAGES), /// id: role_id.cast(), - /// kind: PermissionOverwriteType::Role, + /// kind: PermissionOverwriteType::ROLE, /// }; /// /// client @@ -1835,7 +1835,7 @@ impl Client { /// Automatic archive durations are not locked behind the guild's boost /// level. /// - /// To make a [`PrivateThread`], the guild must also have the + /// To make a [`PRIVATE_THREAD`], the guild must also have the /// `PRIVATE_THREADS` feature. /// /// # Errors @@ -1846,7 +1846,7 @@ impl Client { /// Returns an error of type [`TypeInvalid`] if the channel is not a thread. /// /// [`NameInvalid`]: twilight_validate::channel::ChannelValidationErrorType::NameInvalid - /// [`PrivateThread`]: twilight_model::channel::ChannelType::PrivateThread + /// [`PRIVATE_THREAD`]: twilight_model::channel::ChannelType::PRIVATE_THREAD /// [`TypeInvalid`]: twilight_validate::channel::ChannelValidationErrorType::TypeInvalid pub fn create_thread<'a>( &'a self, @@ -1859,11 +1859,11 @@ impl Client { /// Create a new thread from an existing message. /// - /// When called on a [`GuildText`] channel, this creates a - /// [`PublicThread`]. + /// When called on a [`GUILD_TEXT`] channel, this creates a + /// [`PUBLIC_THREAD`]. /// - /// When called on a [`GuildAnnouncement`] channel, this creates a - /// [`AnnouncementThread`]. + /// When called on a [`GUILD_ANNOUNCEMENT`] channel, this creates a + /// [`ANNOUNCEMENT_THREAD`]. /// /// Automatic archive durations are not locked behind the guild's boost /// level. @@ -1878,11 +1878,11 @@ impl Client { /// /// Returns an error of type [`TypeInvalid`] if the channel is not a thread. /// - /// [`AnnouncementThread`]: twilight_model::channel::ChannelType::AnnouncementThread - /// [`GuildAnnouncement`]: twilight_model::channel::ChannelType::GuildAnnouncement - /// [`GuildText`]: twilight_model::channel::ChannelType::GuildText + /// [`ANNOUNCEMENT_THREAD`]: twilight_model::channel::ChannelType::ANNOUNCEMENT_THREAD + /// [`GUILD_ANNOUNCEMENT`]: twilight_model::channel::ChannelType::GUILD_ANNOUNCEMENT + /// [`GUILD_TEXT`]: twilight_model::channel::ChannelType::GUILD_TEXT /// [`NameInvalid`]: twilight_validate::channel::ChannelValidationErrorType::NameInvalid - /// [`PublicThread`]: twilight_model::channel::ChannelType::PublicThread + /// [`PUBLIC_THREAD`]: twilight_model::channel::ChannelType::PUBLIC_THREAD /// [`TypeInvalid`]: twilight_validate::channel::ChannelValidationErrorType::TypeInvalid pub fn create_thread_from_message<'a>( &'a self, @@ -1935,15 +1935,15 @@ impl Client { /// /// Threads are ordered by [`archive_timestamp`] in descending order. /// - /// When called in a [`GuildText`] channel, returns [`PublicThread`]s. + /// When called in a [`GUILD_TEXT`] channel, returns [`PUBLIC_THREAD`]s. /// - /// When called in a [`GuildAnnouncement`] channel, returns [`AnnouncementThread`]s. + /// When called in a [`GUILD_ANNOUNCEMENT`] channel, returns [`ANNOUNCEMENT_THREAD`]s. /// - /// [`AnnouncementThread`]: twilight_model::channel::ChannelType::AnnouncementThread + /// [`ANNOUNCEMENT_THREAD`]: twilight_model::channel::ChannelType::ANNOUNCEMENT_THREAD /// [`archive_timestamp`]: twilight_model::channel::thread::ThreadMetadata::archive_timestamp - /// [`GuildAnnouncement`]: twilight_model::channel::ChannelType::GuildAnnouncement - /// [`GuildText`]: twilight_model::channel::ChannelType::GuildText - /// [`PublicThread`]: twilight_model::channel::ChannelType::PublicThread + /// [`GUILD_ANNOUNCEMENT`]: twilight_model::channel::ChannelType::GUILD_ANNOUNCEMENT + /// [`GUILD_TEXT`]: twilight_model::channel::ChannelType::GUILD_TEXT + /// [`PUBLIC_THREAD`]: twilight_model::channel::ChannelType::PUBLIC_THREAD /// [`READ_MESSAGE_HISTORY`]: twilight_model::guild::Permissions::READ_MESSAGE_HISTORY pub const fn public_archived_threads( &self, @@ -1957,11 +1957,11 @@ impl Client { /// Requires that the thread is not archived. /// /// Requires the [`MANAGE_THREADS`] permission, unless both the thread is a - /// [`PrivateThread`], and the current user is the creator of the + /// [`PRIVATE_THREAD`], and the current user is the creator of the /// thread. /// /// [`MANAGE_THREADS`]: twilight_model::guild::Permissions::MANAGE_THREADS - /// [`PrivateThread`]: twilight_model::channel::ChannelType::PrivateThread + /// [`PRIVATE_THREAD`]: twilight_model::channel::ChannelType::PRIVATE_THREAD pub const fn remove_thread_member( &self, channel_id: Id, @@ -2230,7 +2230,7 @@ impl Client { /// let garfield_start_time = Timestamp::parse("2022-01-01T14:00:00+00:00")?; /// /// client - /// .create_guild_scheduled_event(guild_id, PrivacyLevel::GuildOnly) + /// .create_guild_scheduled_event(guild_id, PrivacyLevel::GUILD_ONLY) /// .stage_instance( /// channel_id, /// "Garfield Appreciation Hour", @@ -2254,7 +2254,7 @@ impl Client { /// let garfield_con_end_time = Timestamp::parse("2022-01-06T17:00:00+00:00")?; /// /// client - /// .create_guild_scheduled_event(guild_id, PrivacyLevel::GuildOnly) + /// .create_guild_scheduled_event(guild_id, PrivacyLevel::GUILD_ONLY) /// .external( /// "Garfield Con 2022", /// "Baltimore Convention Center", @@ -2317,17 +2317,17 @@ impl Client { /// Update a scheduled event in a guild. /// /// This endpoint supports changing the type of event. When changing the - /// entity type to either [`EntityType::StageInstance`] or - /// [`EntityType::Voice`], an [`Id`] must be provided if it + /// entity type to either [`EntityType::STAGE_INSTANCE`] or + /// [`EntityType::VOICE`], an [`Id`] must be provided if it /// does not already exist. /// - /// When changing the entity type to [`EntityType::External`], the + /// When changing the entity type to [`EntityType::EXTERNAL`], the /// `channel_id` field is cleared and the [`channel_id`] method has no /// effect. Additionally, you must set a location with [`location`]. /// - /// [`EntityType::External`]: twilight_model::guild::scheduled_event::EntityType::External - /// [`EntityType::StageInstance`]: twilight_model::guild::scheduled_event::EntityType::StageInstance - /// [`EntityType::Voice`]: twilight_model::guild::scheduled_event::EntityType::Voice + /// [`EntityType::EXTERNAL`]: twilight_model::guild::scheduled_event::EntityType::EXTERNAL + /// [`EntityType::STAGE_INSTANCE`]: twilight_model::guild::scheduled_event::EntityType::STAGE_INSTANCE + /// [`EntityType::VOICE`]: twilight_model::guild::scheduled_event::EntityType::VOICE /// [`channel_id`]: UpdateGuildScheduledEvent::channel_id /// [`location`]: UpdateGuildScheduledEvent::location pub const fn update_guild_scheduled_event( diff --git a/twilight-http/src/request/application/command/create_global_command/chat_input.rs b/twilight-http/src/request/application/command/create_global_command/chat_input.rs index aab26051dc2..517ca4af15a 100644 --- a/twilight-http/src/request/application/command/create_global_command/chat_input.rs +++ b/twilight-http/src/request/application/command/create_global_command/chat_input.rs @@ -196,7 +196,7 @@ impl TryIntoRequest for CreateGlobalChatInputCommand<'_> { dm_permission: self.dm_permission, description: Some(self.description), description_localizations: self.description_localizations, - kind: CommandType::ChatInput, + kind: CommandType::CHAT_INPUT, name: self.name, name_localizations: self.name_localizations, nsfw: self.nsfw, diff --git a/twilight-http/src/request/application/command/create_global_command/message.rs b/twilight-http/src/request/application/command/create_global_command/message.rs index 47945f5fa83..7a3a135c820 100644 --- a/twilight-http/src/request/application/command/create_global_command/message.rs +++ b/twilight-http/src/request/application/command/create_global_command/message.rs @@ -133,7 +133,7 @@ impl TryIntoRequest for CreateGlobalMessageCommand<'_> { dm_permission: self.dm_permission, description: None, description_localizations: None, - kind: CommandType::Message, + kind: CommandType::MESSAGE, name: self.name, name_localizations: self.name_localizations, nsfw: self.nsfw, diff --git a/twilight-http/src/request/application/command/create_global_command/user.rs b/twilight-http/src/request/application/command/create_global_command/user.rs index db2f23d3713..0756a19d5b2 100644 --- a/twilight-http/src/request/application/command/create_global_command/user.rs +++ b/twilight-http/src/request/application/command/create_global_command/user.rs @@ -133,7 +133,7 @@ impl TryIntoRequest for CreateGlobalUserCommand<'_> { dm_permission: self.dm_permission, description: None, description_localizations: None, - kind: CommandType::User, + kind: CommandType::USER, name: self.name, name_localizations: self.name_localizations, nsfw: self.nsfw, diff --git a/twilight-http/src/request/application/command/create_guild_command/chat_input.rs b/twilight-http/src/request/application/command/create_guild_command/chat_input.rs index fe777b39f18..a68a6c61130 100644 --- a/twilight-http/src/request/application/command/create_guild_command/chat_input.rs +++ b/twilight-http/src/request/application/command/create_guild_command/chat_input.rs @@ -192,7 +192,7 @@ impl TryIntoRequest for CreateGuildChatInputCommand<'_> { dm_permission: None, description: Some(self.description), description_localizations: self.description_localizations, - kind: CommandType::ChatInput, + kind: CommandType::CHAT_INPUT, name: self.name, name_localizations: self.name_localizations, nsfw: self.nsfw, diff --git a/twilight-http/src/request/application/command/create_guild_command/message.rs b/twilight-http/src/request/application/command/create_guild_command/message.rs index 65ec7e67bbc..30e74d6c7a7 100644 --- a/twilight-http/src/request/application/command/create_guild_command/message.rs +++ b/twilight-http/src/request/application/command/create_guild_command/message.rs @@ -129,7 +129,7 @@ impl TryIntoRequest for CreateGuildMessageCommand<'_> { dm_permission: None, description: None, description_localizations: None, - kind: CommandType::Message, + kind: CommandType::MESSAGE, name: self.name, name_localizations: self.name_localizations, nsfw: self.nsfw, diff --git a/twilight-http/src/request/application/command/create_guild_command/user.rs b/twilight-http/src/request/application/command/create_guild_command/user.rs index dc13827a5d7..10361ca210c 100644 --- a/twilight-http/src/request/application/command/create_guild_command/user.rs +++ b/twilight-http/src/request/application/command/create_guild_command/user.rs @@ -129,7 +129,7 @@ impl TryIntoRequest for CreateGuildUserCommand<'_> { dm_permission: None, description: None, description_localizations: None, - kind: CommandType::User, + kind: CommandType::USER, name: self.name, name_localizations: self.name_localizations, nsfw: self.nsfw, diff --git a/twilight-http/src/request/application/command/mod.rs b/twilight-http/src/request/application/command/mod.rs index e4bb13d35b2..d2cea9a683e 100644 --- a/twilight-http/src/request/application/command/mod.rs +++ b/twilight-http/src/request/application/command/mod.rs @@ -88,7 +88,7 @@ mod tests { )])), guild_id: Some(Id::new(2)), id: Some(Id::new(3)), - kind: CommandType::ChatInput, + kind: CommandType::CHAT_INPUT, name: "command name".to_owned(), name_localizations: Some(HashMap::from([( "en-US".to_owned(), @@ -105,7 +105,7 @@ mod tests { dm_permission: command.dm_permission, description: Some(&command.description), description_localizations: command.description_localizations.as_ref(), - kind: CommandType::ChatInput, + kind: CommandType::CHAT_INPUT, name: &command.name, name_localizations: command.name_localizations.as_ref(), nsfw: command.nsfw, diff --git a/twilight-http/src/request/application/interaction/create_response.rs b/twilight-http/src/request/application/interaction/create_response.rs index bf5d083757e..0a0ac1187d6 100644 --- a/twilight-http/src/request/application/interaction/create_response.rs +++ b/twilight-http/src/request/application/interaction/create_response.rs @@ -112,7 +112,7 @@ mod tests { let client = Client::new(String::new()); let response = InteractionResponse { - kind: InteractionResponseType::DeferredUpdateMessage, + kind: InteractionResponseType::DEFERRED_UPDATE_MESSAGE, data: None, }; diff --git a/twilight-http/src/request/channel/invite/create_invite.rs b/twilight-http/src/request/channel/invite/create_invite.rs index 4197657fcc1..ea089291e7d 100644 --- a/twilight-http/src/request/channel/invite/create_invite.rs +++ b/twilight-http/src/request/channel/invite/create_invite.rs @@ -173,7 +173,7 @@ impl<'a> CreateInvite<'a> { /// Set the target application ID for this invite. /// - /// This only works if [`target_type`] is set to [`TargetType::EmbeddedApplication`]. + /// This only works if [`target_type`] is set to [`TargetType::EMBEDDED_APPLICATION`]. /// /// [`target_type`]: Self::target_type pub const fn target_application_id( diff --git a/twilight-http/src/request/channel/thread/create_thread.rs b/twilight-http/src/request/channel/thread/create_thread.rs index c0731f06a44..0e52c5b90fd 100644 --- a/twilight-http/src/request/channel/thread/create_thread.rs +++ b/twilight-http/src/request/channel/thread/create_thread.rs @@ -28,10 +28,10 @@ struct CreateThreadFields<'a> { /// Start a thread that is not connected to a message. /// -/// To make a [`PrivateThread`], the guild must also have the +/// To make a [`PRIVATE_THREAD`], the guild must also have the /// `PRIVATE_THREADS` feature. /// -/// [`PrivateThread`]: twilight_model::channel::ChannelType::PrivateThread +/// [`PRIVATE_THREAD`]: twilight_model::channel::ChannelType::PRIVATE_THREAD #[must_use = "requests must be configured and executed"] pub struct CreateThread<'a> { channel_id: Id, diff --git a/twilight-http/src/request/channel/thread/create_thread_from_message.rs b/twilight-http/src/request/channel/thread/create_thread_from_message.rs index 2816fb69c05..c2ce2408de2 100644 --- a/twilight-http/src/request/channel/thread/create_thread_from_message.rs +++ b/twilight-http/src/request/channel/thread/create_thread_from_message.rs @@ -25,24 +25,24 @@ struct CreateThreadFromMessageFields<'a> { /// Create a new thread from an existing message. /// -/// When called on a [`GuildText`] channel, this creates a -/// [`PublicThread`]. +/// When called on a [`GUILD_TEXT`] channel, this creates a +/// [`PUBLIC_THREAD`]. /// -/// When called on a [`GuildAnnouncement`] channel, this creates a [`AnnouncementThread`]. +/// When called on a [`GUILD_ANNOUNCEMENT`] channel, this creates a [`ANNOUNCEMENT_THREAD`]. /// -/// This request does not work when called on a [`GuildForum`] channel. +/// This request does not work when called on a [`GUILD_FORUM`] channel. /// /// Automatic archive durations are not locked behind the guild's boost level. /// /// The thread's ID will be the same as its parent message. This ensures only /// one thread can be created per message. /// -/// [`AnnouncementThread`]: twilight_model::channel::ChannelType::AnnouncementThread -/// [`GuildAnnouncement`]: twilight_model::channel::ChannelType::GuildAnnouncement -/// [`GuildForum`]: twilight_model::channel::ChannelType::GuildForum -/// [`GuildPublicThread`]: twilight_model::channel::ChannelType::GuildPublicThread -/// [`GuildText`]: twilight_model::channel::ChannelType::GuildText -/// [`PublicThread`]: twilight_model::channel::ChannelType::PublicThread +/// [`ANNOUNCEMENT_THREAD`]: twilight_model::channel::ChannelType::ANNOUNCEMENT_THREAD +/// [`GUILD_ANNOUNCEMENT`]: twilight_model::channel::ChannelType::GUILD_ANNOUNCEMENT +/// [`GUILD_FORUM`]: twilight_model::channel::ChannelType::GUILD_FORUM +/// [`GUILD_PUBLIC_THREAD`]: twilight_model::channel::ChannelType::GUILD_PUBLIC_THREAD +/// [`GUILD_TEXT`]: twilight_model::channel::ChannelType::GUILD_TEXT +/// [`PUBLIC_THREAD`]: twilight_model::channel::ChannelType::PUBLIC_THREAD #[must_use = "requests must be configured and executed"] pub struct CreateThreadFromMessage<'a> { channel_id: Id, diff --git a/twilight-http/src/request/channel/thread/get_public_archived_threads.rs b/twilight-http/src/request/channel/thread/get_public_archived_threads.rs index a493b3cac91..d9975cd6311 100644 --- a/twilight-http/src/request/channel/thread/get_public_archived_threads.rs +++ b/twilight-http/src/request/channel/thread/get_public_archived_threads.rs @@ -17,15 +17,15 @@ use twilight_model::{ /// /// Threads are ordered by [`archive_timestamp`] in descending order. /// -/// When called in a [`GuildText`] channel, returns [`PublicThread`]s. +/// When called in a [`GUILD_TEXT`] channel, returns [`PUBLIC_THREAD`]s. /// -/// When called in a [`GuildAnnouncement`] channel, returns [`AnnouncementThread`]s. +/// When called in a [`GUILD_ANNOUNCEMENT`] channel, returns [`ANNOUNCEMENT_THREAD`]s. /// -/// [`AnnouncementThread`]: twilight_model::channel::ChannelType::AnnouncementThread +/// [`ANNOUNCEMENT_THREAD`]: twilight_model::channel::ChannelType::ANNOUNCEMENT_THREAD /// [`archive_timestamp`]: twilight_model::channel::thread::ThreadMetadata::archive_timestamp -/// [`GuildAnnouncement`]: twilight_model::channel::ChannelType::GuildAnnouncement -/// [`GuildText`]: twilight_model::channel::ChannelType::GuildText -/// [`PublicThread`]: twilight_model::channel::ChannelType::PublicThread +/// [`GUILD_ANNOUNCEMENT`]: twilight_model::channel::ChannelType::GUILD_ANNOUNCEMENT +/// [`GUILD_TEXT`]: twilight_model::channel::ChannelType::GUILD_TEXT +/// [`PUBLIC_THREAD`]: twilight_model::channel::ChannelType::PUBLIC_THREAD /// [`READ_MESSAGE_HISTORY`]: twilight_model::guild::Permissions::READ_MESSAGE_HISTORY #[must_use = "requests must be configured and executed"] pub struct GetPublicArchivedThreads<'a> { diff --git a/twilight-http/src/request/channel/thread/remove_thread_member.rs b/twilight-http/src/request/channel/thread/remove_thread_member.rs index 3941f86c68e..1d57d575d7a 100644 --- a/twilight-http/src/request/channel/thread/remove_thread_member.rs +++ b/twilight-http/src/request/channel/thread/remove_thread_member.rs @@ -16,9 +16,9 @@ use twilight_model::id::{ /// Requires that the thread is not archived. /// /// Requires the [`MANAGE_THREADS`] permission, unless both the thread is a -/// [`PrivateThread`], and the current user is the creator of the thread. +/// [`PRIVATE_THREAD`], and the current user is the creator of the thread. /// -/// [`PrivateThread`]: twilight_model::channel::ChannelType::PrivateThread +/// [`PRIVATE_THREAD`]: twilight_model::channel::ChannelType::PRIVATE_THREAD /// [`MANAGE_THREADS`]: twilight_model::guild::Permissions::MANAGE_THREADS #[must_use = "requests must be configured and executed"] pub struct RemoveThreadMember<'a> { diff --git a/twilight-http/src/request/channel/update_channel.rs b/twilight-http/src/request/channel/update_channel.rs index 131eb44c40a..12b25994ee2 100644 --- a/twilight-http/src/request/channel/update_channel.rs +++ b/twilight-http/src/request/channel/update_channel.rs @@ -205,11 +205,11 @@ impl<'a> UpdateChannel<'a> { /// # Errors /// /// Returns an error of type [`ForumTopicInvalid`] if the channel type is - /// [`GuildForum`] and the topic is invalid. + /// [`GUILD_FORUM`] and the topic is invalid. /// /// [Discord Docs/Channel Object]: https://discordapp.com/developers/docs/resources/channel#channel-object-channel-structure /// [`ForumTopicInvalid`]: twilight_validate::channel::ChannelValidationErrorType::ForumTopicInvalid - /// [`GuildForum`]: twilight_model::channel::ChannelType::GuildForum + /// [`GUILD_FORUM`]: twilight_model::channel::ChannelType::GUILD_FORUM pub fn forum_topic(mut self, topic: Option<&'a str>) -> Result { if let Some(topic) = topic { validate_forum_topic(topic)?; diff --git a/twilight-http/src/request/channel/update_channel_permission.rs b/twilight-http/src/request/channel/update_channel_permission.rs index 875474c876b..9ed028fad9b 100644 --- a/twilight-http/src/request/channel/update_channel_permission.rs +++ b/twilight-http/src/request/channel/update_channel_permission.rs @@ -50,7 +50,7 @@ struct UpdateChannelPermissionFields { /// allow: Some(Permissions::VIEW_CHANNEL), /// deny: Some(Permissions::SEND_MESSAGES), /// id: Id::new(432), -/// kind: PermissionOverwriteType::Role, +/// kind: PermissionOverwriteType::ROLE, /// }; /// /// client @@ -144,7 +144,7 @@ mod tests { allow: None, deny: Some(Permissions::SEND_MESSAGES), id: Id::new(2), - kind: PermissionOverwriteType::Member, + kind: PermissionOverwriteType::MEMBER, }; let client = Client::new("foo".to_owned()); @@ -156,7 +156,7 @@ mod tests { let body = crate::json::to_vec(&UpdateChannelPermissionFields { allow: None, deny: Some(Permissions::SEND_MESSAGES), - kind: PermissionOverwriteType::Member, + kind: PermissionOverwriteType::MEMBER, }) .expect("failed to serialize payload"); let route = Route::UpdatePermissionOverwrite { diff --git a/twilight-http/src/request/guild/auto_moderation/create_auto_moderation_rule.rs b/twilight-http/src/request/guild/auto_moderation/create_auto_moderation_rule.rs index 8f7d9f55e21..1825ceaa98f 100644 --- a/twilight-http/src/request/guild/auto_moderation/create_auto_moderation_rule.rs +++ b/twilight-http/src/request/guild/auto_moderation/create_auto_moderation_rule.rs @@ -83,7 +83,7 @@ struct CreateAutoModerationRuleFields<'a> { /// /// let guild_id = Id::new(1); /// client -/// .create_auto_moderation_rule(guild_id, "no darns", AutoModerationEventType::MessageSend) +/// .create_auto_moderation_rule(guild_id, "no darns", AutoModerationEventType::MESSAGE_SEND) /// .action_block_message() /// .enabled(true) /// .with_keyword(&["darn"]) @@ -124,13 +124,13 @@ impl<'a> CreateAutoModerationRule<'a> { } } - /// Append an action of type [`BlockMessage`]. + /// Append an action of type [`BLOCK_MESSAGE`]. /// - /// [`BlockMessage`]: AutoModerationActionType::BlockMessage + /// [`BLOCK_MESSAGE`]: AutoModerationActionType::BLOCK_MESSAGE pub fn action_block_message(mut self) -> Self { self.fields.actions.get_or_insert_with(Vec::new).push( CreateAutoModerationRuleFieldsAction { - kind: AutoModerationActionType::BlockMessage, + kind: AutoModerationActionType::BLOCK_MESSAGE, metadata: CreateAutoModerationRuleFieldsActionMetadata::default(), }, ); @@ -138,13 +138,13 @@ impl<'a> CreateAutoModerationRule<'a> { self } - /// Append an action of type [`SendAlertMessage`]. + /// Append an action of type [`SEND_ALERT_MESSAGE`]. /// - /// [`SendAlertMessage`]: AutoModerationActionType::SendAlertMessage + /// [`SEND_ALERT_MESSAGE`]: AutoModerationActionType::SEND_ALERT_MESSAGE pub fn action_send_alert_message(mut self, channel_id: Id) -> Self { self.fields.actions.get_or_insert_with(Vec::new).push( CreateAutoModerationRuleFieldsAction { - kind: AutoModerationActionType::SendAlertMessage, + kind: AutoModerationActionType::SEND_ALERT_MESSAGE, metadata: CreateAutoModerationRuleFieldsActionMetadata { channel_id: Some(channel_id), ..Default::default() @@ -155,13 +155,13 @@ impl<'a> CreateAutoModerationRule<'a> { self } - /// Append an action of type [`Timeout`]. + /// Append an action of type [`TIMEOUT`]. /// - /// [`Timeout`]: AutoModerationActionType::Timeout + /// [`TIMEOUT`]: AutoModerationActionType::TIMEOUT pub fn action_timeout(mut self, duration_seconds: u32) -> Self { self.fields.actions.get_or_insert_with(Vec::new).push( CreateAutoModerationRuleFieldsAction { - kind: AutoModerationActionType::Timeout, + kind: AutoModerationActionType::TIMEOUT, metadata: CreateAutoModerationRuleFieldsActionMetadata { duration_seconds: Some(duration_seconds), ..Default::default() @@ -193,13 +193,13 @@ impl<'a> CreateAutoModerationRule<'a> { self } - /// Create the request with the trigger type [`Keyword`], then execute it. + /// Create the request with the trigger type [`KEYWORD`], then execute it. /// /// Rules of this type require the `keyword_filter` field specified, and /// this method ensures this. See [Discord Docs/Keyword Matching Strategies] /// and [Discord Docs/Trigger Metadata]. /// - /// [`Keyword`]: AutoModerationTriggerType::Keyword + /// [`KEYWORD`]: AutoModerationTriggerType::KEYWORD /// [Discord Docs/Keyword Matching Strategies]: https://discord.com/developers/docs/resources/auto-moderation#auto-moderation-rule-object-keyword-matching-strategies /// [Discord Docs/Trigger Metadata]: https://discord.com/developers/docs/resources/auto-moderation#auto-moderation-rule-object-trigger-metadata pub fn with_keyword( @@ -213,28 +213,28 @@ impl<'a> CreateAutoModerationRule<'a> { mention_total_limit: None, }); - self.fields.trigger_type = Some(AutoModerationTriggerType::Keyword); + self.fields.trigger_type = Some(AutoModerationTriggerType::KEYWORD); self.exec() } - /// Create the request with the trigger type [`Spam`], then execute it. + /// Create the request with the trigger type [`SPAM`], then execute it. /// - /// [`Spam`]: AutoModerationTriggerType::Spam + /// [`SPAM`]: AutoModerationTriggerType::SPAM pub fn with_spam(mut self) -> ResponseFuture { - self.fields.trigger_type = Some(AutoModerationTriggerType::Spam); + self.fields.trigger_type = Some(AutoModerationTriggerType::SPAM); self.exec() } - /// Create the request with the trigger type [`KeywordPreset`], then execute - /// it. + /// Create the request with the trigger type [`KEYWORD_PRESET`], then + /// execute it. /// /// Rules of this type require the `presets` and `allow_list` fields /// specified, and this method ensures this. See [Discord Docs/Trigger /// Metadata]. /// - /// [`KeywordPreset`]: AutoModerationTriggerType::KeywordPreset + /// [`KEYWORD_PRESET`]: AutoModerationTriggerType::KEYWORD_PRESET /// [Discord Docs/Trigger Metadata]: https://discord.com/developers/docs/resources/auto-moderation#auto-moderation-rule-object-trigger-metadata pub fn with_keyword_preset( mut self, @@ -248,12 +248,12 @@ impl<'a> CreateAutoModerationRule<'a> { mention_total_limit: None, }); - self.fields.trigger_type = Some(AutoModerationTriggerType::KeywordPreset); + self.fields.trigger_type = Some(AutoModerationTriggerType::KEYWORD_PRESET); self.exec() } - /// Create the request with the trigger type [`MentionSpam`], then execute + /// Create the request with the trigger type [`MENTION_SPAM`], then execute /// it. /// /// Rules of this type requires the `mention_total_limit` field specified, @@ -265,7 +265,7 @@ impl<'a> CreateAutoModerationRule<'a> { /// the limit is invalid. /// /// [`AutoModerationMetadataMentionTotalLimit`]: twilight_validate::request::ValidationErrorType::AutoModerationMetadataMentionTotalLimit - /// [`MentionSpam`]: AutoModerationTriggerType::MentionSpam + /// [`MENTION_SPAM`]: AutoModerationTriggerType::MENTION_SPAM /// [Discord Docs/Trigger Metadata]: https://discord.com/developers/docs/resources/auto-moderation#auto-moderation-rule-object-trigger-metadata pub fn with_mention_spam( mut self, @@ -285,7 +285,7 @@ impl<'a> CreateAutoModerationRule<'a> { mention_total_limit: Some(mention_total_limit), }); - self.fields.trigger_type = Some(AutoModerationTriggerType::MentionSpam); + self.fields.trigger_type = Some(AutoModerationTriggerType::MENTION_SPAM); Ok(self.exec()) } diff --git a/twilight-http/src/request/guild/create_guild/builder.rs b/twilight-http/src/request/guild/create_guild/builder.rs index 41fb3469df2..9829dee37d0 100644 --- a/twilight-http/src/request/guild/create_guild/builder.rs +++ b/twilight-http/src/request/guild/create_guild/builder.rs @@ -316,7 +316,7 @@ impl TextFieldsBuilder { Ok(Self(TextFields { id: Id::new(1), - kind: ChannelType::GuildText, + kind: ChannelType::GUILD_TEXT, name, nsfw: None, permission_overwrites: None, @@ -490,7 +490,7 @@ impl VoiceFieldsBuilder { Ok(Self(VoiceFields { bitrate: None, id: Id::new(1), - kind: ChannelType::GuildVoice, + kind: ChannelType::GUILD_VOICE, name, permission_overwrites: None, parent_id: None, @@ -642,7 +642,7 @@ impl CategoryFieldsBuilder { fields: CategoryFields { id: Id::new(1), name, - kind: ChannelType::GuildCategory, + kind: ChannelType::GUILD_CATEGORY, permission_overwrites: None, }, channels: Vec::new(), @@ -776,7 +776,7 @@ mod tests { allow: Some(perms()), deny: Some(Permissions::empty()), id: Id::new(2), - kind: PermissionOverwriteType::Role, + kind: PermissionOverwriteType::ROLE, } } @@ -836,13 +836,13 @@ mod tests { VoiceFields { bitrate: Some(96_000), id: Id::new(1), - kind: ChannelType::GuildVoice, + kind: ChannelType::GUILD_VOICE, name: String::from("voicename"), permission_overwrites: Some(vec![PermissionOverwrite { allow: Some(perms()), deny: Some(Permissions::empty()), id: Id::new(2), - kind: PermissionOverwriteType::Role, + kind: PermissionOverwriteType::ROLE, }]), parent_id: None, user_limit: Some(40), @@ -874,14 +874,14 @@ mod tests { text(), TextFields { id: Id::new(1), - kind: ChannelType::GuildText, + kind: ChannelType::GUILD_TEXT, name: String::from("textname"), nsfw: Some(true), permission_overwrites: Some(vec![PermissionOverwrite { allow: Some(perms()), deny: Some(Permissions::empty()), id: Id::new(2), - kind: PermissionOverwriteType::Role + kind: PermissionOverwriteType::ROLE }]), parent_id: None, rate_limit_per_user: Some(4_000), @@ -913,13 +913,13 @@ mod tests { vec![ GuildChannelFields::Category(CategoryFields { id: Id::new(2), - kind: ChannelType::GuildCategory, + kind: ChannelType::GUILD_CATEGORY, name: String::from("category"), permission_overwrites: None, }), GuildChannelFields::Text(TextFields { id: Id::new(1), - kind: ChannelType::GuildText, + kind: ChannelType::GUILD_TEXT, name: String::from("textname"), nsfw: Some(true), permission_overwrites: Some(vec![PermissionOverwrite { @@ -930,7 +930,7 @@ mod tests { ), deny: Some(Permissions::empty()), id: Id::new(2), - kind: PermissionOverwriteType::Role, + kind: PermissionOverwriteType::ROLE, }]), parent_id: Some(Id::new(2)), rate_limit_per_user: Some(4_000), @@ -939,7 +939,7 @@ mod tests { GuildChannelFields::Voice(VoiceFields { bitrate: Some(96_000), id: Id::new(1), - kind: ChannelType::GuildVoice, + kind: ChannelType::GUILD_VOICE, name: String::from("voicename"), permission_overwrites: Some(vec![PermissionOverwrite { allow: Some( @@ -949,7 +949,7 @@ mod tests { ), deny: Some(Permissions::empty()), id: Id::new(2), - kind: PermissionOverwriteType::Role, + kind: PermissionOverwriteType::ROLE, }]), parent_id: Some(Id::new(2)), user_limit: Some(40), @@ -969,7 +969,7 @@ mod tests { vec![ GuildChannelFields::Text(TextFields { id: Id::new(1), - kind: ChannelType::GuildText, + kind: ChannelType::GUILD_TEXT, name: String::from("textname"), nsfw: Some(true), permission_overwrites: Some(vec![PermissionOverwrite { @@ -980,7 +980,7 @@ mod tests { ), deny: Some(Permissions::empty()), id: Id::new(2), - kind: PermissionOverwriteType::Role, + kind: PermissionOverwriteType::ROLE, }]), parent_id: None, rate_limit_per_user: Some(4_000), @@ -989,7 +989,7 @@ mod tests { GuildChannelFields::Voice(VoiceFields { bitrate: Some(96_000), id: Id::new(1), - kind: ChannelType::GuildVoice, + kind: ChannelType::GUILD_VOICE, name: String::from("voicename"), permission_overwrites: Some(vec![PermissionOverwrite { allow: Some( @@ -999,7 +999,7 @@ mod tests { ), deny: Some(Permissions::empty()), id: Id::new(2), - kind: PermissionOverwriteType::Role, + kind: PermissionOverwriteType::ROLE, }]), parent_id: None, user_limit: Some(40), diff --git a/twilight-http/src/request/guild/update_guild.rs b/twilight-http/src/request/guild/update_guild.rs index a87c0a63608..16dcc7807fe 100644 --- a/twilight-http/src/request/guild/update_guild.rs +++ b/twilight-http/src/request/guild/update_guild.rs @@ -163,19 +163,19 @@ impl<'a> UpdateGuild<'a> { /// Set the enabled features of the guild. /// - /// Attempting to add or remove the [`GuildFeature::Community`] feature requires the + /// Attempting to add or remove the [`GuildFeature::COMMUNITY`] feature requires the /// [`Permissions::ADMINISTRATOR`] permission. /// - /// Attempting to add or remove the [`GuildFeature::Discoverable`] feature requires + /// Attempting to add or remove the [`GuildFeature::DISCOVERABLE`] feature requires /// the [`Permissions::ADMINISTRATOR`] permission. Additionally the guild /// must pass all the discovery requirements. /// - /// Attempting to add or remove the [`GuildFeature::InvitesDisabled`] feature requires + /// Attempting to add or remove the [`GuildFeature::INVITES_DISABLED`] feature requires /// the [`Permissions::MANAGE_GUILD`] permission. /// - /// [`GuildFeature::Community`]: twilight_model::guild::GuildFeature::Community - /// [`GuildFeature::Discoverable`]: twilight_model::guild::GuildFeature::Discoverable - /// [`GuildFeature::InvitesDisabled`]: twilight_model::guild::GuildFeature::InvitesDisabled + /// [`GuildFeature::COMMUNITY`]: twilight_model::guild::GuildFeature::COMMUNITY + /// [`GuildFeature::DISCOVERABLE`]: twilight_model::guild::GuildFeature::DISCOVERABLE + /// [`GuildFeature::INVITES_DISABLED`]: twilight_model::guild::GuildFeature::INVITES_DISABLED /// [`Permissions::ADMINISTRATOR`]: twilight_model::guild::Permissions::ADMINISTRATOR /// [`Permissions::MANAGE_GUILD`]: twilight_model::guild::Permissions::MANAGE_GUILD pub const fn features(mut self, features: &'a [&'a str]) -> Self { diff --git a/twilight-http/src/request/scheduled_event/create_guild_scheduled_event/external.rs b/twilight-http/src/request/scheduled_event/create_guild_scheduled_event/external.rs index b3b04af1d00..434f6cc7952 100644 --- a/twilight-http/src/request/scheduled_event/create_guild_scheduled_event/external.rs +++ b/twilight-http/src/request/scheduled_event/create_guild_scheduled_event/external.rs @@ -31,7 +31,7 @@ impl<'a> CreateGuildExternalScheduledEvent<'a> { ) -> Self { Self(CreateGuildScheduledEvent { fields: CreateGuildScheduledEventFields { - entity_type: Some(EntityType::External), + entity_type: Some(EntityType::EXTERNAL), entity_metadata: Some(EntityMetadataFields { location: Some(location), }), diff --git a/twilight-http/src/request/scheduled_event/create_guild_scheduled_event/mod.rs b/twilight-http/src/request/scheduled_event/create_guild_scheduled_event/mod.rs index ba3b589ee60..5b4e1ca4dcd 100644 --- a/twilight-http/src/request/scheduled_event/create_guild_scheduled_event/mod.rs +++ b/twilight-http/src/request/scheduled_event/create_guild_scheduled_event/mod.rs @@ -75,7 +75,7 @@ struct CreateGuildScheduledEventFields<'a> { /// let garfield_start_time = Timestamp::parse("2022-01-01T14:00:00+00:00")?; /// /// client -/// .create_guild_scheduled_event(guild_id, PrivacyLevel::GuildOnly) +/// .create_guild_scheduled_event(guild_id, PrivacyLevel::GUILD_ONLY) /// .stage_instance( /// channel_id, /// "Garfield Appreciation Hour", @@ -99,7 +99,7 @@ struct CreateGuildScheduledEventFields<'a> { /// let garfield_con_end_time = Timestamp::parse("2022-01-06T17:00:00+00:00")?; /// /// client -/// .create_guild_scheduled_event(guild_id, PrivacyLevel::GuildOnly) +/// .create_guild_scheduled_event(guild_id, PrivacyLevel::GUILD_ONLY) /// .external( /// "Garfield Con 2022", /// "Baltimore Convention Center", diff --git a/twilight-http/src/request/scheduled_event/create_guild_scheduled_event/stage_instance.rs b/twilight-http/src/request/scheduled_event/create_guild_scheduled_event/stage_instance.rs index 1902493706c..7a31e3031b6 100644 --- a/twilight-http/src/request/scheduled_event/create_guild_scheduled_event/stage_instance.rs +++ b/twilight-http/src/request/scheduled_event/create_guild_scheduled_event/stage_instance.rs @@ -29,7 +29,7 @@ impl<'a> CreateGuildStageInstanceScheduledEvent<'a> { Self(CreateGuildScheduledEvent { fields: CreateGuildScheduledEventFields { channel_id: Some(channel_id), - entity_type: Some(EntityType::StageInstance), + entity_type: Some(EntityType::STAGE_INSTANCE), name: Some(name), scheduled_start_time: Some(scheduled_start_time), ..inner.fields diff --git a/twilight-http/src/request/scheduled_event/create_guild_scheduled_event/voice.rs b/twilight-http/src/request/scheduled_event/create_guild_scheduled_event/voice.rs index f776a5eebfd..13c579107dd 100644 --- a/twilight-http/src/request/scheduled_event/create_guild_scheduled_event/voice.rs +++ b/twilight-http/src/request/scheduled_event/create_guild_scheduled_event/voice.rs @@ -29,7 +29,7 @@ impl<'a> CreateGuildVoiceScheduledEvent<'a> { Self(CreateGuildScheduledEvent { fields: CreateGuildScheduledEventFields { channel_id: Some(channel_id), - entity_type: Some(EntityType::Voice), + entity_type: Some(EntityType::VOICE), name: Some(name), scheduled_start_time: Some(scheduled_start_time), ..inner.fields diff --git a/twilight-http/src/request/scheduled_event/update_guild_scheduled_event.rs b/twilight-http/src/request/scheduled_event/update_guild_scheduled_event.rs index 653d2d09cfa..f14c7f52c51 100644 --- a/twilight-http/src/request/scheduled_event/update_guild_scheduled_event.rs +++ b/twilight-http/src/request/scheduled_event/update_guild_scheduled_event.rs @@ -49,10 +49,10 @@ struct UpdateGuildScheduledEventFields<'a> { /// Update a scheduled event in a guild. /// /// This endpoint supports changing the type of event. When changing the entity -/// type to either [`EntityType::StageInstance`] or [`EntityType::Voice`], an +/// type to either [`EntityType::STAGE_INSTANCE`] or [`EntityType::VOICE`], an /// [`Id`] must be provided if it does not already exist. /// -/// When changing the entity type to [`EntityType::External`], the `channel_id` +/// When changing the entity type to [`EntityType::EXTERNAL`], the `channel_id` /// field is cleared and the [`channel_id`] method has no effect. Additionally, /// you must set a location with [`location`]. /// @@ -97,11 +97,11 @@ impl<'a> UpdateGuildScheduledEvent<'a> { /// Set the channel ID. /// - /// If `entity_type` is already [`EntityType::External`], this has no + /// If `entity_type` is already [`EntityType::EXTERNAL`], this has no /// effect. pub fn channel_id(mut self, channel_id: Id) -> Self { if let Some(entity_type) = self.fields.entity_type { - if entity_type == EntityType::External { + if entity_type == EntityType::EXTERNAL { return self; } } @@ -136,7 +136,7 @@ impl<'a> UpdateGuildScheduledEvent<'a> { /// See the struct-level documentation for information about required fields /// for each type. pub fn entity_type(mut self, entity_type: EntityType) -> Self { - if entity_type == EntityType::External { + if entity_type == EntityType::EXTERNAL { self.fields.channel_id = None; } @@ -162,9 +162,9 @@ impl<'a> UpdateGuildScheduledEvent<'a> { /// Set the location of the external scheduled event. /// - /// This only functions if the event's [`EntityType`] is [`External`]. + /// This only functions if the event's [`EntityType`] is [`EXTERNAL`]. /// - /// [`External`]: EntityType::External + /// [`EXTERNAL`]: EntityType::EXTERNAL pub const fn location(mut self, location: Option<&'a str>) -> Self { self.fields.entity_metadata = Some(EntityMetadataFields { location }); @@ -206,14 +206,14 @@ impl<'a> UpdateGuildScheduledEvent<'a> { /// Set the status of the event. /// - /// If an event is currently [`Scheduled`], it can only be set to [`Active`] - /// or [`Cancelled`]. If it is currently [`Active`], it can only be set to - /// [`Completed`]. Otherwise, the status can not be updated. + /// If an event is currently [`SCHEDULED`], it can only be set to [`ACTIVE`] + /// or [`CANCELLED`]. If it is currently [`ACTIVE`], it can only be set to + /// [`COMPLETED`]. Otherwise, the status can not be updated. /// - /// [`Active`]: Status::Active - /// [`Cancelled`]: Status::Cancelled - /// [`Completed`]: Status::Completed - /// [`Scheduled`]: Status::Scheduled + /// [`ACTIVE`]: Status::ACTIVE + /// [`CANCELLED`]: Status::CANCELLED + /// [`COMPLETED`]: Status::COMPLETED + /// [`SCHEDULED`]: Status::SCHEDULED pub const fn status(mut self, status: Status) -> Self { self.fields.status = Some(status); diff --git a/twilight-model/Cargo.toml b/twilight-model/Cargo.toml index 37bfef0c69e..779ef1b7d60 100644 --- a/twilight-model/Cargo.toml +++ b/twilight-model/Cargo.toml @@ -16,7 +16,6 @@ version = "0.15.0" bitflags = { default-features = false, version = "1" } serde = { default-features = false, features = ["derive", "std"], version = "1.0.103" } serde-value = { default-features = false, version = "0.7" } -serde_repr = { default-features = false, version = "0.1.5" } time = { default-features = false, features = ["parsing", "std"], version = "0.3" } tracing = { default-features = false, version = "0.1.16" } diff --git a/twilight-model/src/application/command/command_type.rs b/twilight-model/src/application/command/command_type.rs index 13417cf8659..1979a8a0230 100644 --- a/twilight-model/src/application/command/command_type.rs +++ b/twilight-model/src/application/command/command_type.rs @@ -1,93 +1,65 @@ use serde::{Deserialize, Serialize}; // Keep in sync with `twilight-validate::command`! -#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)] -#[non_exhaustive] -#[serde(from = "u8", into = "u8")] -pub enum CommandType { +#[derive(Clone, Copy, Deserialize, Eq, Hash, PartialEq, Serialize)] +pub struct CommandType(u8); + +impl CommandType { /// Slash command. /// /// Text-based command that appears when a user types `/`. - ChatInput, + pub const CHAT_INPUT: Self = Self::new(1); + /// UI-based command. /// /// Appears when a user right clicks or taps on a user. - User, + pub const USER: Self = Self::new(2); + /// UI-based command. /// /// Appears when a user right clicks or taps on a message. - Message, - /// Variant value is unknown to the library. - Unknown(u8), -} - -impl CommandType { - pub const fn kind(self) -> &'static str { - match self { - Self::ChatInput => "ChatInput", - Self::User => "User", - Self::Message => "Message", - Self::Unknown(_) => "Unknown", - } - } -} + pub const MESSAGE: Self = Self::new(3); -impl From for CommandType { - fn from(value: u8) -> Self { - match value { - 1 => Self::ChatInput, - 2 => Self::User, - 3 => Self::Message, - unknown => Self::Unknown(unknown), - } + /// Name of the associated constant. + /// + /// Returns `None` if the value doesn't have a defined constant. + pub const fn name(self) -> Option<&'static str> { + Some(match self { + Self::CHAT_INPUT => "CHAT_INPUT", + Self::MESSAGE => "MESSAGE", + Self::USER => "USER", + _ => return None, + }) } } -impl From for u8 { - fn from(value: CommandType) -> Self { - match value { - CommandType::ChatInput => 1, - CommandType::User => 2, - CommandType::Message => 3, - CommandType::Unknown(unknown) => unknown, - } - } -} +impl_typed!(CommandType, u8); #[cfg(test)] mod tests { use super::CommandType; - use serde::{Deserialize, Serialize}; use serde_test::Token; - use static_assertions::assert_impl_all; - use std::{fmt::Debug, hash::Hash}; - assert_impl_all!( - CommandType: Clone, - Copy, - Debug, - Deserialize<'static>, - Eq, - Hash, - PartialEq, - Serialize, - Send, - Sync - ); + const MAP: &[(CommandType, u8)] = &[ + (CommandType::CHAT_INPUT, 1), + (CommandType::USER, 2), + (CommandType::MESSAGE, 3), + ]; #[test] fn variants() { - serde_test::assert_tokens(&CommandType::ChatInput, &[Token::U8(1)]); - serde_test::assert_tokens(&CommandType::User, &[Token::U8(2)]); - serde_test::assert_tokens(&CommandType::Message, &[Token::U8(3)]); - serde_test::assert_tokens(&CommandType::Unknown(99), &[Token::U8(99)]); - } - - #[test] - fn kinds() { - assert_eq!("ChatInput", CommandType::ChatInput.kind()); - assert_eq!("User", CommandType::User.kind()); - assert_eq!("Message", CommandType::Message.kind()); - assert_eq!("Unknown", CommandType::Unknown(99).kind()); + for (kind, num) in MAP { + serde_test::assert_tokens( + kind, + &[ + Token::NewtypeStruct { + name: "CommandType", + }, + Token::U8(*num), + ], + ); + assert_eq!(*kind, CommandType::from(*num)); + assert_eq!(*num, kind.get()); + } } } diff --git a/twilight-model/src/application/command/mod.rs b/twilight-model/src/application/command/mod.rs index 056997f9162..d6c4f1e06b7 100644 --- a/twilight-model/src/application/command/mod.rs +++ b/twilight-model/src/application/command/mod.rs @@ -54,10 +54,10 @@ pub struct Command { pub dm_permission: Option, /// Description of the command. /// - /// For [`User`] and [`Message`] commands, this will be an empty string. + /// For [`USER`] and [`MESSAGE`] commands, this will be an empty string. /// - /// [`User`]: CommandType::User - /// [`Message`]: CommandType::Message + /// [`USER`]: CommandType::USER + /// [`MESSAGE`]: CommandType::MESSAGE pub description: String, /// Localization dictionary for the `description` field. /// @@ -118,7 +118,7 @@ mod tests { )])), guild_id: Some(Id::new(300)), id: Some(Id::new(200)), - kind: CommandType::ChatInput, + kind: CommandType::CHAT_INPUT, name: "test command".into(), name_localizations: Some(HashMap::from([("en-US".into(), "test command".into())])), nsfw: None, @@ -128,7 +128,7 @@ mod tests { choices: None, description: "sub command group desc".to_owned(), description_localizations: None, - kind: CommandOptionType::SubCommandGroup, + kind: CommandOptionType::SUB_COMMAND_GROUP, max_length: None, max_value: None, min_length: None, @@ -141,7 +141,7 @@ mod tests { choices: None, description: "sub command desc".to_owned(), description_localizations: None, - kind: CommandOptionType::SubCommand, + kind: CommandOptionType::SUB_COMMAND, max_length: None, max_value: None, min_length: None, @@ -155,7 +155,7 @@ mod tests { choices: None, description: "attachment desc".to_owned(), description_localizations: None, - kind: CommandOptionType::Attachment, + kind: CommandOptionType::ATTACHMENT, max_length: None, max_value: None, min_length: None, @@ -171,7 +171,7 @@ mod tests { choices: None, description: "boolean desc".to_owned(), description_localizations: None, - kind: CommandOptionType::Boolean, + kind: CommandOptionType::BOOLEAN, max_length: None, max_value: None, min_length: None, @@ -187,7 +187,7 @@ mod tests { choices: None, description: "channel desc".to_owned(), description_localizations: None, - kind: CommandOptionType::Channel, + kind: CommandOptionType::CHANNEL, max_length: None, max_value: None, min_length: None, @@ -199,11 +199,11 @@ mod tests { }, CommandOption { autocomplete: None, - channel_types: Some(Vec::from([ChannelType::GuildText])), + channel_types: Some(Vec::from([ChannelType::GUILD_TEXT])), choices: None, description: "channel desc".to_owned(), description_localizations: None, - kind: CommandOptionType::Channel, + kind: CommandOptionType::CHANNEL, max_length: None, max_value: None, min_length: None, @@ -219,7 +219,7 @@ mod tests { choices: Some(Vec::new()), description: "integer desc".to_owned(), description_localizations: None, - kind: CommandOptionType::Integer, + kind: CommandOptionType::INTEGER, max_length: None, max_value: Some(CommandOptionValue::Integer(100)), min_length: None, @@ -238,7 +238,7 @@ mod tests { "en-GB".to_owned(), "mentionable desc (but british)".to_owned(), )])), - kind: CommandOptionType::Mentionable, + kind: CommandOptionType::MENTIONABLE, max_length: None, max_value: None, min_length: None, @@ -261,7 +261,7 @@ mod tests { }])), description: "number desc".to_owned(), description_localizations: None, - kind: CommandOptionType::Number, + kind: CommandOptionType::NUMBER, max_length: None, max_value: None, min_length: None, @@ -277,7 +277,7 @@ mod tests { choices: None, description: "role desc".to_owned(), description_localizations: None, - kind: CommandOptionType::Role, + kind: CommandOptionType::ROLE, max_length: None, max_value: None, min_length: None, @@ -296,7 +296,7 @@ mod tests { choices: None, description: "string desc".to_owned(), description_localizations: None, - kind: CommandOptionType::String, + kind: CommandOptionType::STRING, max_length: Some(6000), max_value: None, min_length: Some(0), @@ -348,7 +348,10 @@ mod tests { Token::NewtypeStruct { name: "Id" }, Token::Str("200"), Token::Str("type"), - Token::U8(CommandType::ChatInput.into()), + Token::NewtypeStruct { + name: "CommandType", + }, + Token::U8(CommandType::CHAT_INPUT.get()), Token::Str("name"), Token::Str("test command"), Token::Str("name_localizations"), @@ -366,7 +369,10 @@ mod tests { Token::Str("description"), Token::Str("sub command group desc"), Token::Str("type"), - Token::U8(CommandOptionType::SubCommandGroup as u8), + Token::NewtypeStruct { + name: "CommandOptionType", + }, + Token::U8(CommandOptionType::SUB_COMMAND_GROUP.get()), Token::Str("name"), Token::Str("sub command group name"), Token::Str("options"), @@ -379,7 +385,10 @@ mod tests { Token::Str("description"), Token::Str("sub command desc"), Token::Str("type"), - Token::U8(CommandOptionType::SubCommand as u8), + Token::NewtypeStruct { + name: "CommandOptionType", + }, + Token::U8(CommandOptionType::SUB_COMMAND.get()), Token::Str("name"), Token::Str("sub command name"), Token::Str("options"), @@ -392,7 +401,10 @@ mod tests { Token::Str("description"), Token::Str("attachment desc"), Token::Str("type"), - Token::U8(CommandOptionType::Attachment as u8), + Token::NewtypeStruct { + name: "CommandOptionType", + }, + Token::U8(CommandOptionType::ATTACHMENT.get()), Token::Str("name"), Token::Str("attachment name"), Token::StructEnd, @@ -403,7 +415,10 @@ mod tests { Token::Str("description"), Token::Str("boolean desc"), Token::Str("type"), - Token::U8(CommandOptionType::Boolean as u8), + Token::NewtypeStruct { + name: "CommandOptionType", + }, + Token::U8(CommandOptionType::BOOLEAN.get()), Token::Str("name"), Token::Str("boolean name"), Token::Str("required"), @@ -421,7 +436,10 @@ mod tests { Token::Str("description"), Token::Str("channel desc"), Token::Str("type"), - Token::U8(CommandOptionType::Channel as u8), + Token::NewtypeStruct { + name: "CommandOptionType", + }, + Token::U8(CommandOptionType::CHANNEL.get()), Token::Str("name"), Token::Str("channel name"), Token::StructEnd, @@ -432,12 +450,18 @@ mod tests { Token::Str("channel_types"), Token::Some, Token::Seq { len: Some(1) }, - Token::U8(ChannelType::GuildText.into()), + Token::NewtypeStruct { + name: "ChannelType", + }, + Token::U8(ChannelType::GUILD_TEXT.get()), Token::SeqEnd, Token::Str("description"), Token::Str("channel desc"), Token::Str("type"), - Token::U8(CommandOptionType::Channel as u8), + Token::NewtypeStruct { + name: "CommandOptionType", + }, + Token::U8(CommandOptionType::CHANNEL.get()), Token::Str("name"), Token::Str("channel name"), Token::StructEnd, @@ -455,7 +479,10 @@ mod tests { Token::Str("description"), Token::Str("integer desc"), Token::Str("type"), - Token::U8(CommandOptionType::Integer as u8), + Token::NewtypeStruct { + name: "CommandOptionType", + }, + Token::U8(CommandOptionType::INTEGER.get()), Token::Str("max_value"), Token::Some, Token::I64(100), @@ -478,7 +505,10 @@ mod tests { Token::Str("mentionable desc (but british)"), Token::MapEnd, Token::Str("type"), - Token::U8(CommandOptionType::Mentionable as u8), + Token::NewtypeStruct { + name: "CommandOptionType", + }, + Token::U8(CommandOptionType::MENTIONABLE.get()), Token::Str("name"), Token::Str("mentionable name"), Token::StructEnd, @@ -511,7 +541,10 @@ mod tests { Token::Str("description"), Token::Str("number desc"), Token::Str("type"), - Token::U8(CommandOptionType::Number as u8), + Token::NewtypeStruct { + name: "CommandOptionType", + }, + Token::U8(CommandOptionType::NUMBER.get()), Token::Str("name"), Token::Str("number name"), Token::StructEnd, @@ -522,7 +555,10 @@ mod tests { Token::Str("description"), Token::Str("role desc"), Token::Str("type"), - Token::U8(CommandOptionType::Role as u8), + Token::NewtypeStruct { + name: "CommandOptionType", + }, + Token::U8(CommandOptionType::ROLE.get()), Token::Str("name"), Token::Str("role name"), Token::Str("name_localizations"), @@ -539,7 +575,10 @@ mod tests { Token::Str("description"), Token::Str("string desc"), Token::Str("type"), - Token::U8(CommandOptionType::String as u8), + Token::NewtypeStruct { + name: "CommandOptionType", + }, + Token::U8(CommandOptionType::STRING.get()), Token::Str("max_length"), Token::Some, Token::U16(6000), diff --git a/twilight-model/src/application/command/option.rs b/twilight-model/src/application/command/option.rs index 9afa739cf81..af8b3091a04 100644 --- a/twilight-model/src/application/command/option.rs +++ b/twilight-model/src/application/command/option.rs @@ -1,6 +1,5 @@ use crate::channel::ChannelType; use serde::{Deserialize, Serialize}; -use serde_repr::{Deserialize_repr, Serialize_repr}; use std::{cmp::Eq, collections::HashMap}; /// Option for a [`Command`]. @@ -20,15 +19,15 @@ use std::{cmp::Eq, collections::HashMap}; pub struct CommandOption { /// Whether the command supports autocomplete. /// - /// Applicable for options of type [`Integer`], [`Number`], and [`String`]. + /// Applicable for options of type [`INTEGER`], [`NUMBER`], and [`STRING`]. /// /// Defaults to `false`. /// /// **Note**: may not be set to `true` if `choices` are set. /// - /// [`Integer`]: CommandOptionType::Integer - /// [`Number`]: CommandOptionType::Number - /// [`String`]: CommandOptionType::String + /// [`INTEGER`]: CommandOptionType::INTEGER + /// [`NUMBER`]: CommandOptionType::NUMBER + /// [`STRING`]: CommandOptionType::STRING #[serde(skip_serializing_if = "Option::is_none")] pub autocomplete: Option, /// List of possible channel types users can select from. @@ -37,12 +36,12 @@ pub struct CommandOption { /// /// Defaults to any channel type. /// - /// [`Channel`]: CommandOptionType::Channel + /// [`CHANNEL`]: CommandOptionType::CHANNEL #[serde(skip_serializing_if = "Option::is_none")] pub channel_types: Option>, /// List of predetermined choices users can select from. /// - /// Applicable for options of type [`Integer`], [`Number`], and [`String`]. + /// Applicable for options of type [`INTEGER`], [`NUMBER`], and [`STRING`]. /// /// Defaults to no choices; users may input a value of their choice. /// @@ -50,9 +49,9 @@ pub struct CommandOption { /// /// **Note**: all choices must be of the same type. /// - /// [`Integer`]: CommandOptionType::Integer - /// [`Number`]: CommandOptionType::Number - /// [`String`]: CommandOptionType::String + /// [`INTEGER`]: CommandOptionType::INTEGER + /// [`NUMBER`]: CommandOptionType::NUMBER + /// [`STRING`]: CommandOptionType::STRING #[serde(skip_serializing_if = "Option::is_none")] pub choices: Option>, /// Description of the option. Must be 100 characters or less. @@ -77,38 +76,38 @@ pub struct CommandOption { /// /// Must be at least `1` and at most `6000`. /// - /// [`String`]: CommandOptionType::String + /// [`STRING`]: CommandOptionType::STRING #[serde(skip_serializing_if = "Option::is_none")] pub max_length: Option, /// Maximum allowed value. /// - /// Applicable for options of type [`Integer`] and [`Number`]. + /// Applicable for options of type [`INTEGER`] and [`NUMBER`]. /// /// Defaults to no maximum. /// - /// [`Integer`]: CommandOptionType::Integer - /// [`Number`]: CommandOptionType::Number + /// [`INTEGER`]: CommandOptionType::INTEGER + /// [`NUMBER`]: CommandOptionType::NUMBER #[serde(skip_serializing_if = "Option::is_none")] pub max_value: Option, /// Minimum allowed value length. /// - /// Applicable for options of type [`String`]. + /// Applicable for options of type [`STRING`]. /// /// Defaults to `0`. /// /// Must be at most `6000`. /// - /// [`String`]: CommandOptionType::String + /// [`STRING`]: CommandOptionType::STRING #[serde(skip_serializing_if = "Option::is_none")] pub min_length: Option, /// Minimum allowed value. /// - /// Applicable for options of type [`Integer`] and [`Number`]. + /// Applicable for options of type [`INTEGER`] and [`NUMBER`]. /// /// Defaults to no minimum. /// - /// [`Integer`]: CommandOptionType::Integer - /// [`Number`]: CommandOptionType::Number + /// [`INTEGER`]: CommandOptionType::INTEGER + /// [`NUMBER`]: CommandOptionType::NUMBER #[serde(skip_serializing_if = "Option::is_none")] pub min_value: Option, /// Name of the option. Must be 32 characters or less. @@ -124,29 +123,29 @@ pub struct CommandOption { pub name_localizations: Option>, /// Nested options. /// - /// Applicable for options of type [`SubCommand`] and [`SubCommandGroup`]. + /// Applicable for options of type [`SUB_COMMAND`] and [`SUB_COMMAND_GROUP`]. /// /// Defaults to no options. /// - /// **Note**: at least one option is required and [`SubCommandGroup`] may - /// only contain [`SubCommand`]s. + /// **Note**: at least one option is required and [`SUB_COMMAND_GROUP`] may + /// only contain [`SUB_COMMAND`]s. /// /// See [Discord Docs/Subcommands and Subcommand Groups]. /// /// [Discord Docs/Subcommands and Subcommand Groups]: https://discord.com/developers/docs/interactions/application-commands#subcommands-and-subcommand-groups - /// [`SubCommand`]: CommandOptionType::SubCommand - /// [`SubCommandGroup`]: CommandOptionType::SubCommandGroup + /// [`SUB_COMMAND`]: CommandOptionType::SUB_COMMAND + /// [`SUB_COMMAND_GROUP`]: CommandOptionType::SUB_COMMAND_GROUP #[serde(skip_serializing_if = "Option::is_none")] pub options: Option>, /// Whether the option is required. /// - /// Applicable for all options except those of type [`SubCommand`] and - /// [`SubCommandGroup`]. + /// Applicable for all options except those of type [`SUB_COMMAND`] and + /// [`SUB_COMMAND_GROUP`]. /// /// Defaults to `false`. /// - /// [`SubCommand`]: CommandOptionType::SubCommand - /// [`SubCommandGroup`]: CommandOptionType::SubCommandGroup + /// [`SUB_COMMAND`]: CommandOptionType::SUB_COMMAND + /// [`SUB_COMMAND_GROUP`]: CommandOptionType::SUB_COMMAND_GROUP #[serde(skip_serializing_if = "Option::is_none")] pub required: Option, } @@ -200,37 +199,88 @@ pub enum CommandOptionValue { } /// Type of a [`CommandOption`]. -#[derive(Clone, Copy, Debug, Deserialize_repr, Eq, Hash, PartialEq, Serialize_repr)] -#[non_exhaustive] -#[repr(u8)] -pub enum CommandOptionType { - SubCommand = 1, - SubCommandGroup = 2, - String = 3, - Integer = 4, - Boolean = 5, - User = 6, - Channel = 7, - Role = 8, - Mentionable = 9, - Number = 10, - Attachment = 11, -} +#[derive(Clone, Copy, Deserialize, Eq, Hash, PartialEq, Serialize)] +pub struct CommandOptionType(u8); impl CommandOptionType { - pub const fn kind(self) -> &'static str { - match self { - CommandOptionType::SubCommand => "SubCommand", - CommandOptionType::SubCommandGroup => "SubCommandGroup", - CommandOptionType::String => "String", - CommandOptionType::Integer => "Integer", - CommandOptionType::Boolean => "Boolean", - CommandOptionType::User => "User", - CommandOptionType::Channel => "Channel", - CommandOptionType::Role => "Role", - CommandOptionType::Mentionable => "Mentionable", - CommandOptionType::Number => "Number", - CommandOptionType::Attachment => "Attachment", + pub const SUB_COMMAND: Self = Self::new(1); + + pub const SUB_COMMAND_GROUP: Self = Self::new(2); + + pub const STRING: Self = Self::new(3); + + pub const INTEGER: Self = Self::new(4); + + pub const BOOLEAN: Self = Self::new(5); + + pub const USER: Self = Self::new(6); + + pub const CHANNEL: Self = Self::new(7); + + pub const ROLE: Self = Self::new(8); + + pub const MENTIONABLE: Self = Self::new(9); + + pub const NUMBER: Self = Self::new(10); + + pub const ATTACHMENT: Self = Self::new(11); + + /// Name of the associated constant. + /// + /// Returns `None` if the value doesn't have a defined constant. + pub const fn name(self) -> Option<&'static str> { + Some(match self { + Self::ATTACHMENT => "ATTACHMENT", + Self::BOOLEAN => "BOOLEAN", + Self::CHANNEL => "CHANNEL", + Self::INTEGER => "INTEGER", + Self::MENTIONABLE => "MENTIONABLE", + Self::NUMBER => "NUMBER", + Self::ROLE => "ROLE", + Self::STRING => "STRING", + Self::SUB_COMMAND => "SUB_COMMAND", + Self::SUB_COMMAND_GROUP => "SUB_COMMAND_GROUP", + Self::USER => "USER", + _ => return None, + }) + } +} + +impl_typed!(CommandOptionType, u8); + +#[cfg(test)] +mod tests { + use super::CommandOptionType; + use serde_test::Token; + + const MAP: &[(CommandOptionType, u8)] = &[ + (CommandOptionType::SUB_COMMAND, 1), + (CommandOptionType::SUB_COMMAND_GROUP, 2), + (CommandOptionType::STRING, 3), + (CommandOptionType::INTEGER, 4), + (CommandOptionType::BOOLEAN, 5), + (CommandOptionType::USER, 6), + (CommandOptionType::CHANNEL, 7), + (CommandOptionType::ROLE, 8), + (CommandOptionType::MENTIONABLE, 9), + (CommandOptionType::NUMBER, 10), + (CommandOptionType::ATTACHMENT, 11), + ]; + + #[test] + fn variants() { + for (kind, num) in MAP { + serde_test::assert_tokens( + kind, + &[ + Token::NewtypeStruct { + name: "CommandOptionType", + }, + Token::U8(*num), + ], + ); + assert_eq!(*kind, CommandOptionType::from(*num)); + assert_eq!(*num, kind.get()); } } } diff --git a/twilight-model/src/application/command/permissions.rs b/twilight-model/src/application/command/permissions.rs index c9c105cad1b..c98db7ab1cc 100644 --- a/twilight-model/src/application/command/permissions.rs +++ b/twilight-model/src/application/command/permissions.rs @@ -8,7 +8,6 @@ use crate::id::{ Id, }; use serde::{de::Deserializer, ser::Serializer, Deserialize, Serialize}; -use serde_repr::{Deserialize_repr, Serialize_repr}; /// List of [`CommandPermission`]s for a command in a guild. #[derive(Clone, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)] @@ -47,6 +46,10 @@ pub enum CommandPermissionType { Role(Id), /// Affected member. User(Id), + Unknown { + kind: CommandPermissionDataType, + id: Id, + }, } impl CommandPermissionType { @@ -56,15 +59,17 @@ impl CommandPermissionType { Self::Channel(id) => id.cast(), Self::Role(id) => id.cast(), Self::User(id) => id.cast(), + Self::Unknown { id, .. } => id, } } /// Get the associated resource type. const fn kind(self) -> CommandPermissionDataType { match self { - Self::Channel(_) => CommandPermissionDataType::Channel, - Self::Role(_) => CommandPermissionDataType::Role, - Self::User(_) => CommandPermissionDataType::User, + Self::Channel(_) => CommandPermissionDataType::CHANNEL, + Self::Role(_) => CommandPermissionDataType::ROLE, + Self::User(_) => CommandPermissionDataType::USER, + Self::Unknown { kind, .. } => kind, } } } @@ -80,15 +85,31 @@ struct CommandPermissionData { permission: bool, } -#[derive(Clone, Debug, Deserialize_repr, Eq, PartialEq, Serialize_repr)] -#[non_exhaustive] -#[repr(u8)] -enum CommandPermissionDataType { - Role = 1, - User = 2, - Channel = 3, +#[derive(Clone, Copy, Deserialize, Eq, Hash, PartialEq, Serialize)] +pub struct CommandPermissionDataType(u8); + +impl CommandPermissionDataType { + pub const ROLE: Self = Self::new(1); + + pub const USER: Self = Self::new(2); + + pub const CHANNEL: Self = Self::new(3); + + /// Name of the associated constant. + /// + /// Returns `None` if the value doesn't have a defined constant. + pub const fn name(self) -> Option<&'static str> { + Some(match self { + Self::CHANNEL => "CHANNEL", + Self::ROLE => "ROLE", + Self::USER => "USER", + _ => return None, + }) + } } +impl_typed!(CommandPermissionDataType, u8); + impl<'de> Deserialize<'de> for CommandPermission { fn deserialize>(deserializer: D) -> Result { let data = CommandPermissionData::deserialize(deserializer)?; @@ -97,9 +118,13 @@ impl<'de> Deserialize<'de> for CommandPermission { let _span_enter = span.enter(); let id = match data.kind { - CommandPermissionDataType::Role => CommandPermissionType::Role(data.id.cast()), - CommandPermissionDataType::User => CommandPermissionType::User(data.id.cast()), - CommandPermissionDataType::Channel => CommandPermissionType::Channel(data.id.cast()), + CommandPermissionDataType::ROLE => CommandPermissionType::Role(data.id.cast()), + CommandPermissionDataType::USER => CommandPermissionType::User(data.id.cast()), + CommandPermissionDataType::CHANNEL => CommandPermissionType::Channel(data.id.cast()), + other => CommandPermissionType::Unknown { + kind: other, + id: data.id, + }, }; tracing::trace!(id = %data.id, kind = ?data.kind); @@ -150,7 +175,10 @@ mod tests { Token::NewtypeStruct { name: "Id" }, Token::Str("100"), Token::Str("type"), - Token::U8(CommandPermissionDataType::Role as u8), + Token::NewtypeStruct { + name: "CommandPermissionDataType", + }, + Token::U8(CommandPermissionDataType::ROLE.get()), Token::Str("permission"), Token::Bool(true), Token::StructEnd, @@ -202,7 +230,10 @@ mod tests { Token::NewtypeStruct { name: "Id" }, Token::Str("50"), Token::Str("type"), - Token::U8(CommandPermissionDataType::Channel as u8), + Token::NewtypeStruct { + name: "CommandPermissionDataType", + }, + Token::U8(CommandPermissionDataType::CHANNEL.get()), Token::Str("permission"), Token::Bool(false), Token::StructEnd, @@ -214,7 +245,10 @@ mod tests { Token::NewtypeStruct { name: "Id" }, Token::Str("200"), Token::Str("type"), - Token::U8(CommandPermissionDataType::User as u8), + Token::NewtypeStruct { + name: "CommandPermissionDataType", + }, + Token::U8(CommandPermissionDataType::USER.get()), Token::Str("permission"), Token::Bool(true), Token::StructEnd, diff --git a/twilight-model/src/application/interaction/application_command/mod.rs b/twilight-model/src/application/interaction/application_command/mod.rs index 620610522f7..ff5fcc6dea9 100644 --- a/twilight-model/src/application/interaction/application_command/mod.rs +++ b/twilight-model/src/application/interaction/application_command/mod.rs @@ -1,6 +1,6 @@ -//! [`ApplicationCommand`] interaction. +//! [`APPLICATION_COMMAND`] interaction. //! -//! [`ApplicationCommand`]: crate::application::interaction::InteractionType::ApplicationCommand +//! [`APPLICATION_COMMAND`]: crate::application::interaction::InteractionType::APPLICATION_COMMAND mod option; mod resolved; @@ -19,13 +19,13 @@ use crate::{ }; use serde::{Deserialize, Serialize}; -/// Data received when an [`ApplicationCommand`] or [`ApplicationCommandAutocomplete`] -/// interaction is executed. +/// Data received when an [`APPLICATION_COMMAND`] or +/// [`APPLICATION_COMMAND_AUTOCOMPLETE`] interaction is executed. /// /// See [Discord Docs/Application Command Data Structure]. /// -/// [`ApplicationCommand`]: crate::application::interaction::InteractionType::ApplicationCommand -/// [`ApplicationCommandAutocomplete`]: crate::application::interaction::InteractionType::ApplicationCommandAutocomplete +/// [`APPLICATION_COMMAND`]: crate::application::interaction::InteractionType::APPLICATION_COMMAND +/// [`APPLICATION_COMMAND_AUTOCOMPLETE`]: crate::application::interaction::InteractionType::APPLICATION_COMMAND_AUTOCOMPLETE /// [Discord Docs/Application Command Data Structure]: https://discord.com/developers/docs/interactions/receiving-and-responding#interaction-object-application-command-data-structure #[derive(Clone, Debug, Deserialize, PartialEq, Serialize)] pub struct CommandData { diff --git a/twilight-model/src/application/interaction/application_command/option.rs b/twilight-model/src/application/interaction/application_command/option.rs index 0816d3fa6e4..0c5f5f59632 100644 --- a/twilight-model/src/application/interaction/application_command/option.rs +++ b/twilight-model/src/application/interaction/application_command/option.rs @@ -199,7 +199,7 @@ impl<'de> Deserialize<'de> for CommandDataOption { CommandOptionValue::Focused(val.to_string(), kind) } else { match kind { - CommandOptionType::Attachment => { + CommandOptionType::ATTACHMENT => { let val = value_opt.ok_or_else(|| DeError::missing_field("value"))?; if let ValueEnvelope::Id(id) = val { @@ -211,7 +211,7 @@ impl<'de> Deserialize<'de> for CommandDataOption { )); } } - CommandOptionType::Boolean => { + CommandOptionType::BOOLEAN => { let val = value_opt.ok_or_else(|| DeError::missing_field("value"))?; if let ValueEnvelope::Boolean(b) = val { @@ -220,7 +220,7 @@ impl<'de> Deserialize<'de> for CommandDataOption { return Err(DeError::invalid_type(val.as_unexpected(), &"boolean")); } } - CommandOptionType::Channel => { + CommandOptionType::CHANNEL => { let val = value_opt.ok_or_else(|| DeError::missing_field("value"))?; if let ValueEnvelope::Id(id) = val { @@ -232,7 +232,7 @@ impl<'de> Deserialize<'de> for CommandDataOption { )); } } - CommandOptionType::Integer => { + CommandOptionType::INTEGER => { let val = value_opt.ok_or_else(|| DeError::missing_field("value"))?; if let ValueEnvelope::Integer(i) = val { @@ -241,7 +241,7 @@ impl<'de> Deserialize<'de> for CommandDataOption { return Err(DeError::invalid_type(val.as_unexpected(), &"integer")); } } - CommandOptionType::Mentionable => { + CommandOptionType::MENTIONABLE => { let val = value_opt.ok_or_else(|| DeError::missing_field("value"))?; if let ValueEnvelope::Id(id) = val { @@ -253,7 +253,7 @@ impl<'de> Deserialize<'de> for CommandDataOption { )); } } - CommandOptionType::Number => { + CommandOptionType::NUMBER => { let val = value_opt.ok_or_else(|| DeError::missing_field("value"))?; match val { @@ -275,7 +275,7 @@ impl<'de> Deserialize<'de> for CommandDataOption { } } } - CommandOptionType::Role => { + CommandOptionType::ROLE => { let val = value_opt.ok_or_else(|| DeError::missing_field("value"))?; if let ValueEnvelope::Id(id) = val { @@ -284,7 +284,7 @@ impl<'de> Deserialize<'de> for CommandDataOption { return Err(DeError::invalid_type(val.as_unexpected(), &"role id")); } } - CommandOptionType::String => { + CommandOptionType::STRING => { let val = value_opt.ok_or_else(|| DeError::missing_field("value"))?; match val { @@ -300,11 +300,11 @@ impl<'de> Deserialize<'de> for CommandDataOption { } } } - CommandOptionType::SubCommand => CommandOptionValue::SubCommand(options), - CommandOptionType::SubCommandGroup => { + CommandOptionType::SUB_COMMAND => CommandOptionValue::SubCommand(options), + CommandOptionType::SUB_COMMAND_GROUP => { CommandOptionValue::SubCommandGroup(options) } - CommandOptionType::User => { + CommandOptionType::USER => { let val = value_opt.ok_or_else(|| DeError::missing_field("value"))?; if let ValueEnvelope::Id(id) = val { @@ -313,6 +313,12 @@ impl<'de> Deserialize<'de> for CommandDataOption { return Err(DeError::invalid_type(val.as_unexpected(), &"user id")); } } + other => { + return Err(DeError::invalid_value( + Unexpected::Unsigned(u64::from(other.get())), + &"command option type", + )); + } } }; @@ -366,18 +372,18 @@ pub enum CommandOptionValue { impl CommandOptionValue { pub const fn kind(&self) -> CommandOptionType { match self { - CommandOptionValue::Attachment(_) => CommandOptionType::Attachment, - CommandOptionValue::Boolean(_) => CommandOptionType::Boolean, - CommandOptionValue::Channel(_) => CommandOptionType::Channel, + CommandOptionValue::Attachment(_) => CommandOptionType::ATTACHMENT, + CommandOptionValue::Boolean(_) => CommandOptionType::BOOLEAN, + CommandOptionValue::Channel(_) => CommandOptionType::CHANNEL, CommandOptionValue::Focused(_, t) => *t, - CommandOptionValue::Integer(_) => CommandOptionType::Integer, - CommandOptionValue::Mentionable(_) => CommandOptionType::Mentionable, - CommandOptionValue::Number(_) => CommandOptionType::Number, - CommandOptionValue::Role(_) => CommandOptionType::Role, - CommandOptionValue::String(_) => CommandOptionType::String, - CommandOptionValue::SubCommand(_) => CommandOptionType::SubCommand, - CommandOptionValue::SubCommandGroup(_) => CommandOptionType::SubCommandGroup, - CommandOptionValue::User(_) => CommandOptionType::User, + CommandOptionValue::Integer(_) => CommandOptionType::INTEGER, + CommandOptionValue::Mentionable(_) => CommandOptionType::MENTIONABLE, + CommandOptionValue::Number(_) => CommandOptionType::NUMBER, + CommandOptionValue::Role(_) => CommandOptionType::ROLE, + CommandOptionValue::String(_) => CommandOptionType::STRING, + CommandOptionValue::SubCommand(_) => CommandOptionType::SUB_COMMAND, + CommandOptionValue::SubCommandGroup(_) => CommandOptionType::SUB_COMMAND_GROUP, + CommandOptionValue::User(_) => CommandOptionType::USER, } } } @@ -401,7 +407,7 @@ mod tests { guild_id: Some(Id::new(2)), id: Id::new(1), name: "permissions".to_owned(), - kind: CommandType::ChatInput, + kind: CommandType::CHAT_INPUT, options: Vec::new(), resolved: None, target_id: None, @@ -423,7 +429,10 @@ mod tests { Token::Str("name"), Token::Str("permissions"), Token::Str("type"), - Token::U8(CommandType::ChatInput.into()), + Token::NewtypeStruct { + name: "CommandType", + }, + Token::U8(CommandType::CHAT_INPUT.get()), Token::StructEnd, ], ) @@ -435,7 +444,7 @@ mod tests { guild_id: Some(Id::new(2)), id: Id::new(1), name: "permissions".to_owned(), - kind: CommandType::ChatInput, + kind: CommandType::CHAT_INPUT, options: Vec::from([CommandDataOption { name: "cat".to_owned(), value: CommandOptionValue::Integer(42), @@ -461,7 +470,10 @@ mod tests { Token::Str("name"), Token::Str("permissions"), Token::Str("type"), - Token::U8(CommandType::ChatInput.into()), + Token::NewtypeStruct { + name: "CommandType", + }, + Token::U8(CommandType::CHAT_INPUT.get()), Token::Str("options"), Token::Seq { len: Some(1) }, Token::Struct { @@ -471,7 +483,10 @@ mod tests { Token::Str("name"), Token::Str("cat"), Token::Str("type"), - Token::U8(CommandOptionType::Integer as u8), + Token::NewtypeStruct { + name: "CommandOptionType", + }, + Token::U8(CommandOptionType::INTEGER.get()), Token::Str("value"), Token::I64(42), Token::StructEnd, @@ -487,7 +502,7 @@ mod tests { guild_id: Some(Id::new(2)), id: Id::new(1), name: "permissions".to_owned(), - kind: CommandType::ChatInput, + kind: CommandType::CHAT_INPUT, options: Vec::from([ CommandDataOption { name: "cat".to_owned(), @@ -497,7 +512,7 @@ mod tests { name: "dog".to_owned(), value: CommandOptionValue::Focused( "Shiba".to_owned(), - CommandOptionType::String, + CommandOptionType::STRING, ), }, ]), @@ -522,7 +537,10 @@ mod tests { Token::Str("name"), Token::Str("permissions"), Token::Str("type"), - Token::U8(CommandType::ChatInput.into()), + Token::NewtypeStruct { + name: "CommandType", + }, + Token::U8(CommandType::CHAT_INPUT.get()), Token::Str("options"), Token::Seq { len: Some(2) }, Token::Struct { @@ -532,7 +550,10 @@ mod tests { Token::Str("name"), Token::Str("cat"), Token::Str("type"), - Token::U8(CommandOptionType::Integer as u8), + Token::NewtypeStruct { + name: "CommandOptionType", + }, + Token::U8(CommandOptionType::INTEGER.get()), Token::Str("value"), Token::I64(42), Token::StructEnd, @@ -546,7 +567,10 @@ mod tests { Token::Str("name"), Token::Str("dog"), Token::Str("type"), - Token::U8(CommandOptionType::String as u8), + Token::NewtypeStruct { + name: "CommandOptionType", + }, + Token::U8(CommandOptionType::STRING.get()), Token::Str("value"), Token::String("Shiba"), Token::StructEnd, @@ -562,7 +586,7 @@ mod tests { guild_id: None, id: Id::new(1), name: "photo".to_owned(), - kind: CommandType::ChatInput, + kind: CommandType::CHAT_INPUT, options: Vec::from([CommandDataOption { name: "cat".to_owned(), value: CommandOptionValue::SubCommand(Vec::new()), @@ -584,7 +608,10 @@ mod tests { Token::Str("name"), Token::Str("photo"), Token::Str("type"), - Token::U8(CommandType::ChatInput.into()), + Token::NewtypeStruct { + name: "CommandType", + }, + Token::U8(CommandType::CHAT_INPUT.get()), Token::Str("options"), Token::Seq { len: Some(1) }, Token::Struct { @@ -594,7 +621,10 @@ mod tests { Token::Str("name"), Token::Str("cat"), Token::Str("type"), - Token::U8(CommandOptionType::SubCommand as u8), + Token::NewtypeStruct { + name: "CommandOptionType", + }, + Token::U8(CommandOptionType::SUB_COMMAND.get()), Token::StructEnd, Token::SeqEnd, Token::StructEnd, @@ -619,7 +649,10 @@ mod tests { Token::Str("name"), Token::Str("opt"), Token::Str("type"), - Token::U8(CommandOptionType::Number as u8), + Token::NewtypeStruct { + name: "CommandOptionType", + }, + Token::U8(CommandOptionType::NUMBER.get()), Token::Str("value"), Token::I64(5), Token::StructEnd, @@ -633,7 +666,7 @@ mod tests { name: "opt".to_string(), value: CommandOptionValue::Focused( "not a number".to_owned(), - CommandOptionType::Number, + CommandOptionType::NUMBER, ), }; @@ -650,7 +683,10 @@ mod tests { Token::Str("name"), Token::Str("opt"), Token::Str("type"), - Token::U8(CommandOptionType::Number as u8), + Token::NewtypeStruct { + name: "CommandOptionType", + }, + Token::U8(CommandOptionType::NUMBER.get()), Token::Str("value"), Token::String("not a number"), Token::StructEnd, @@ -662,7 +698,7 @@ mod tests { fn autocomplete_number() { let value = CommandDataOption { name: "opt".to_string(), - value: CommandOptionValue::Focused("1".to_owned(), CommandOptionType::Number), + value: CommandOptionValue::Focused("1".to_owned(), CommandOptionType::NUMBER), }; serde_test::assert_de_tokens( @@ -678,7 +714,10 @@ mod tests { Token::Str("name"), Token::Str("opt"), Token::Str("type"), - Token::U8(CommandOptionType::Number as u8), + Token::NewtypeStruct { + name: "CommandOptionType", + }, + Token::U8(CommandOptionType::NUMBER.get()), Token::Str("value"), Token::String("1"), Token::StructEnd, diff --git a/twilight-model/src/application/interaction/application_command/resolved.rs b/twilight-model/src/application/interaction/application_command/resolved.rs index 59e3390e354..dad94755104 100644 --- a/twilight-model/src/application/interaction/application_command/resolved.rs +++ b/twilight-model/src/application/interaction/application_command/resolved.rs @@ -143,7 +143,7 @@ mod tests { Id::new(100), InteractionChannel { id: Id::new(100), - kind: ChannelType::GuildText, + kind: ChannelType::GUILD_TEXT, name: "channel name".into(), parent_id: None, permissions: Permissions::empty(), @@ -199,7 +199,7 @@ mod tests { guild_id: Some(Id::new(1)), id: Id::new(4), interaction: None, - kind: MessageType::Regular, + kind: MessageType::REGULAR, member: Some(PartialMember { avatar: None, communication_disabled_until: None, @@ -222,7 +222,7 @@ mod tests { reference: None, role_subscription_data: None, sticker_items: vec![MessageSticker { - format_type: StickerFormatType::Png, + format_type: StickerFormatType::PNG, id: Id::new(1), name: "sticker name".to_owned(), }], @@ -265,7 +265,7 @@ mod tests { locale: Some("en-us".to_owned()), mfa_enabled: Some(true), name: "test".to_owned(), - premium_type: Some(PremiumType::Nitro), + premium_type: Some(PremiumType::NITRO), public_flags: Some( UserFlags::PREMIUM_EARLY_SUPPORTER | UserFlags::VERIFIED_DEVELOPER, ), @@ -327,6 +327,9 @@ mod tests { Token::NewtypeStruct { name: "Id" }, Token::Str("100"), Token::Str("type"), + Token::NewtypeStruct { + name: "ChannelType", + }, Token::U8(0), Token::Str("name"), Token::Str("channel name"), @@ -413,6 +416,9 @@ mod tests { Token::NewtypeStruct { name: "Id" }, Token::Str("4"), Token::Str("type"), + Token::NewtypeStruct { + name: "MessageType", + }, Token::U8(0), Token::Str("member"), Token::Some, @@ -456,6 +462,9 @@ mod tests { len: 3, }, Token::Str("format_type"), + Token::NewtypeStruct { + name: "StickerFormatType", + }, Token::U8(1), Token::Str("id"), Token::NewtypeStruct { name: "Id" }, @@ -535,6 +544,9 @@ mod tests { Token::Str("test"), Token::Str("premium_type"), Token::Some, + Token::NewtypeStruct { + name: "PremiumType", + }, Token::U8(2), Token::Str("public_flags"), Token::Some, diff --git a/twilight-model/src/application/interaction/interaction_type.rs b/twilight-model/src/application/interaction/interaction_type.rs index d981526cffd..5dd3c081b82 100644 --- a/twilight-model/src/application/interaction/interaction_type.rs +++ b/twilight-model/src/application/interaction/interaction_type.rs @@ -1,131 +1,79 @@ -use serde_repr::{Deserialize_repr, Serialize_repr}; -use std::fmt::{Display, Formatter, Result as FmtResult}; +use serde::{Deserialize, Serialize}; /// Type of interaction. /// /// See [Discord Docs/Interaction Object]. /// /// [Discord Docs/Interaction Object]: https://discord.com/developers/docs/interactions/receiving-and-responding#interaction-object-interaction-type -#[derive(Clone, Copy, Debug, Deserialize_repr, Eq, Hash, PartialEq, Serialize_repr)] -#[non_exhaustive] -#[repr(u8)] -pub enum InteractionType { +#[derive(Clone, Copy, Deserialize, Eq, Hash, PartialEq, Serialize)] +pub struct InteractionType(u8); + +impl InteractionType { /// Interaction involves a ping (webhook-based interactions). /// /// See [Discord Docs/Receiving an Interaction]. /// /// [Discord Docs/Receiving an Interaction]: https://discord.com/developers/docs/interactions/receiving-and-responding#receiving-an-interaction - Ping = 1, + pub const PING: Self = Self::new(1); + /// Interaction involves an application command. - ApplicationCommand = 2, + pub const APPLICATION_COMMAND: Self = Self::new(2); + /// Interaction involves a message [`Component`]. /// /// [`Component`]: crate::channel::message::Component - MessageComponent = 3, - /// Interaction involves an autocomplete request. - ApplicationCommandAutocomplete = 4, - /// Interaction involves a modal submit. - ModalSubmit = 5, -} + pub const MESSAGE_COMPONENT: Self = Self::new(3); -impl InteractionType { - pub const fn kind(self) -> &'static str { - match self { - Self::Ping => "Ping", - Self::ApplicationCommand => "ApplicationCommand", - Self::MessageComponent => "MessageComponent", - Self::ApplicationCommandAutocomplete => "ApplicationCommandAutocomplete", - Self::ModalSubmit => "ModalSubmit", - } - } -} - -#[derive(Debug)] -pub struct UnknownInteractionTypeError { - value: u8, -} + /// Interaction involves an autocomplete request. + pub const APPLICATION_COMMAND_AUTOCOMPLETE: Self = Self::new(4); -impl Display for UnknownInteractionTypeError { - fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult { - f.write_str("unknown interaction type: ")?; + /// Interaction involves a modal submit. + pub const MODAL_SUBMIT: Self = Self::new(5); - Display::fmt(&self.value, f) + /// Name of the associated constant. + /// + /// Returns `None` if the value doesn't have a defined constant. + pub const fn name(self) -> Option<&'static str> { + Some(match self { + Self::APPLICATION_COMMAND => "APPLICATION_COMMAND", + Self::APPLICATION_COMMAND_AUTOCOMPLETE => "APPLICATION_COMMAND_AUTOCOMPLETE", + Self::MESSAGE_COMPONENT => "MESSAGE_COMPONENT", + Self::MODAL_SUBMIT => "MODAL_SUBMIT", + Self::PING => "PING", + _ => return None, + }) } } -impl TryFrom for InteractionType { - type Error = UnknownInteractionTypeError; - - fn try_from(i: u8) -> Result { - match i { - 1 => Ok(Self::Ping), - 2 => Ok(Self::ApplicationCommand), - 3 => Ok(Self::MessageComponent), - 4 => Ok(Self::ApplicationCommandAutocomplete), - 5 => Ok(Self::ModalSubmit), - other => Err(UnknownInteractionTypeError { value: other }), - } - } -} +impl_typed!(InteractionType, u8); #[cfg(test)] mod tests { - use super::{InteractionType, UnknownInteractionTypeError}; - use serde::{Deserialize, Serialize}; - use static_assertions::{assert_impl_all, const_assert_eq}; - use std::{fmt::Debug, hash::Hash}; - - assert_impl_all!( - InteractionType: Clone, - Copy, - Debug, - Deserialize<'static>, - Eq, - Hash, - PartialEq, - Serialize, - Send, - Sync - ); - const_assert_eq!(1, InteractionType::Ping as u8); - const_assert_eq!(2, InteractionType::ApplicationCommand as u8); - const_assert_eq!(3, InteractionType::MessageComponent as u8); - const_assert_eq!(4, InteractionType::ApplicationCommandAutocomplete as u8); - const_assert_eq!(5, InteractionType::ModalSubmit as u8); + use super::InteractionType; + use serde_test::Token; - #[test] - fn kind() { - assert_eq!("Ping", InteractionType::Ping.kind()); - assert_eq!( - "ApplicationCommand", - InteractionType::ApplicationCommand.kind() - ); - assert_eq!("MessageComponent", InteractionType::MessageComponent.kind()); - assert_eq!( - "ApplicationCommandAutocomplete", - InteractionType::ApplicationCommandAutocomplete.kind() - ); - assert_eq!("ModalSubmit", InteractionType::ModalSubmit.kind()); - } + const MAP: &[(InteractionType, u8)] = &[ + (InteractionType::PING, 1), + (InteractionType::APPLICATION_COMMAND, 2), + (InteractionType::MESSAGE_COMPONENT, 3), + (InteractionType::APPLICATION_COMMAND_AUTOCOMPLETE, 4), + (InteractionType::MODAL_SUBMIT, 5), + ]; #[test] - fn try_from() -> Result<(), UnknownInteractionTypeError> { - assert_eq!(InteractionType::Ping, InteractionType::try_from(1)?); - assert_eq!( - InteractionType::ApplicationCommand, - InteractionType::try_from(2)? - ); - assert_eq!( - InteractionType::MessageComponent, - InteractionType::try_from(3)? - ); - assert_eq!( - InteractionType::ApplicationCommandAutocomplete, - InteractionType::try_from(4)?, - ); - assert_eq!(InteractionType::ModalSubmit, InteractionType::try_from(5)?); - assert!(InteractionType::try_from(u8::MAX).is_err()); - - Ok(()) + fn variants() { + for (kind, num) in MAP { + serde_test::assert_tokens( + kind, + &[ + Token::NewtypeStruct { + name: "InteractionType", + }, + Token::U8(*num), + ], + ); + assert_eq!(*kind, InteractionType::from(*num)); + assert_eq!(*num, kind.get()); + } } } diff --git a/twilight-model/src/application/interaction/message_component.rs b/twilight-model/src/application/interaction/message_component.rs index 24427251e18..c97a780741a 100644 --- a/twilight-model/src/application/interaction/message_component.rs +++ b/twilight-model/src/application/interaction/message_component.rs @@ -1,15 +1,15 @@ -//! [`MessageComponent`] interaction. +//! [`MESSAGE_COMPONENT`] interaction. //! -//! [`MessageComponent`]: crate::application::interaction::InteractionType::MessageComponent +//! [`MESSAGE_COMPONENT`]: crate::application::interaction::InteractionType::MESSAGE_COMPONENT use crate::channel::message::component::ComponentType; use serde::{Deserialize, Serialize}; -/// Data received when an [`MessageComponent`] interaction is executed. +/// Data received when an [`MESSAGE_COMPONENT`] interaction is executed. /// /// See [Discord Docs/Message Component Data Structure]. /// -/// [`MessageComponent`]: crate::application::interaction::InteractionType::MessageComponent +/// [`MESSAGE_COMPONENT`]: crate::application::interaction::InteractionType::MESSAGE_COMPONENT /// [Discord Docs/Message Component Data Structure]: https://discord.com/developers/docs/interactions/receiving-and-responding#interaction-object-message-component-data-structure #[derive(Clone, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)] pub struct MessageComponentInteractionData { @@ -23,9 +23,9 @@ pub struct MessageComponentInteractionData { pub component_type: ComponentType, /// Values selected by the user. /// - /// Only used for [`SelectMenu`] components. + /// Only used for [`SELECT_MENU`] components. /// - /// [`SelectMenu`]: ComponentType::SelectMenu + /// [`SELECT_MENU`]: ComponentType::SELECT_MENU #[serde(default)] pub values: Vec, } @@ -60,7 +60,7 @@ mod tests { fn message_component_interaction_data() { let value = MessageComponentInteractionData { custom_id: "test".to_owned(), - component_type: ComponentType::Button, + component_type: ComponentType::BUTTON, values: Vec::from(["1".to_owned(), "2".to_owned()]), }; @@ -74,7 +74,10 @@ mod tests { Token::String("custom_id"), Token::String("test"), Token::String("component_type"), - Token::U8(ComponentType::Button.into()), + Token::NewtypeStruct { + name: "ComponentType", + }, + Token::U8(ComponentType::BUTTON.get()), Token::String("values"), Token::Seq { len: Some(2) }, Token::String("1"), diff --git a/twilight-model/src/application/interaction/mod.rs b/twilight-model/src/application/interaction/mod.rs index 0fcc62a7c26..3cd0da16a73 100644 --- a/twilight-model/src/application/interaction/mod.rs +++ b/twilight-model/src/application/interaction/mod.rs @@ -48,21 +48,21 @@ pub struct Interaction { pub application_id: Id, /// ID of the channel the interaction was invoked in. /// - /// Present on all interactions types, except [`Ping`]. + /// Present on all interactions types, except [`PING`]. /// - /// [`Ping`]: InteractionType::Ping + /// [`PING`]: InteractionType::PING #[serde(skip_serializing_if = "Option::is_none")] pub channel_id: Option>, /// Data from the interaction. /// - /// This field present on [`ApplicationCommand`], [`MessageComponent`], - /// [`ApplicationCommandAutocomplete`] and [`ModalSubmit`] interactions. + /// This field present on [`APPLICATION_COMMAND`], [`MESSAGE_COMPONENT`], + /// [`APPLICATION_COMMAND_AUTOCOMPLETE`] and [`MODAL_SUBMIT`] interactions. /// The inner enum variant matches the interaction type. /// - /// [`ApplicationCommand`]: InteractionType::ApplicationCommand - /// [`MessageComponent`]: InteractionType::MessageComponent - /// [`ApplicationCommandAutocomplete`]: InteractionType::ApplicationCommandAutocomplete - /// [`ModalSubmit`]: InteractionType::ModalSubmit + /// [`APPLICATION_COMMAND`]: InteractionType::APPLICATION_COMMAND + /// [`MESSAGE_COMPONENT`]: InteractionType::MESSAGE_COMPONENT + /// [`APPLICATION_COMMAND_AUTOCOMPLETE`]: InteractionType::APPLICATION_COMMAND_AUTOCOMPLETE + /// [`MODAL_SUBMIT`]: InteractionType::MODAL_SUBMIT #[serde(skip_serializing_if = "Option::is_none")] pub data: Option, /// ID of the guild the interaction was invoked in. @@ -80,9 +80,9 @@ pub struct Interaction { pub kind: InteractionType, /// Selected language of the user who invoked the interaction. /// - /// Present on all interactions types, except [`Ping`]. + /// Present on all interactions types, except [`PING`]. /// - /// [`Ping`]: InteractionType::Ping + /// [`PING`]: InteractionType::PING #[serde(skip_serializing_if = "Option::is_none")] pub locale: Option, /// Member that invoked the interaction. @@ -92,9 +92,9 @@ pub struct Interaction { pub member: Option, /// Message attached to the interaction. /// - /// Present on [`MessageComponent`] interactions. + /// Present on [`MESSAGE_COMPONENT`] interactions. /// - /// [`MessageComponent`]: InteractionType::MessageComponent + /// [`MESSAGE_COMPONENT`]: InteractionType::MESSAGE_COMPONENT #[serde(skip_serializing_if = "Option::is_none")] pub message: Option, /// Token for responding to the interaction. @@ -337,8 +337,7 @@ impl<'de> Visitor<'de> for InteractionVisitor { ); let data = match kind { - InteractionType::Ping => None, - InteractionType::ApplicationCommand => { + InteractionType::APPLICATION_COMMAND => { let data = data .ok_or_else(|| DeError::missing_field("data"))? .deserialize_into() @@ -346,7 +345,7 @@ impl<'de> Visitor<'de> for InteractionVisitor { Some(InteractionData::ApplicationCommand(data)) } - InteractionType::MessageComponent => { + InteractionType::MESSAGE_COMPONENT => { let data = data .ok_or_else(|| DeError::missing_field("data"))? .deserialize_into() @@ -354,7 +353,7 @@ impl<'de> Visitor<'de> for InteractionVisitor { Some(InteractionData::MessageComponent(data)) } - InteractionType::ApplicationCommandAutocomplete => { + InteractionType::APPLICATION_COMMAND_AUTOCOMPLETE => { let data = data .ok_or_else(|| DeError::missing_field("data"))? .deserialize_into() @@ -362,7 +361,7 @@ impl<'de> Visitor<'de> for InteractionVisitor { Some(InteractionData::ApplicationCommand(data)) } - InteractionType::ModalSubmit => { + InteractionType::MODAL_SUBMIT => { let data = data .ok_or_else(|| DeError::missing_field("data"))? .deserialize_into() @@ -370,6 +369,7 @@ impl<'de> Visitor<'de> for InteractionVisitor { Some(InteractionData::ModalSubmit(data)) } + _ => None, }; Ok(Self::Value { @@ -395,19 +395,19 @@ impl<'de> Visitor<'de> for InteractionVisitor { #[non_exhaustive] #[serde(untagged)] pub enum InteractionData { - /// Data received for the [`ApplicationCommand`] and [`ApplicationCommandAutocomplete`] - /// interaction types. + /// Data received for the [`APPLICATION_COMMAND`] and + /// [`APPLICATION_COMMAND_AUTOCOMPLETE`] interaction types. /// - /// [`ApplicationCommand`]: InteractionType::ApplicationCommand - /// [`ApplicationCommandAutocomplete`]: InteractionType::ApplicationCommandAutocomplete + /// [`APPLICATION_COMMAND`]: InteractionType::APPLICATION_COMMAND + /// [`APPLICATION_COMMAND_AUTOCOMPLETE`]: InteractionType::APPLICATION_COMMAND_AUTOCOMPLETE ApplicationCommand(Box), - /// Data received for the [`MessageComponent`] interaction type. + /// Data received for the [`MESSAGE_COMPONENT`] interaction type. /// - /// [`MessageComponent`]: InteractionType::MessageComponent + /// [`MESSAGE_COMPONENT`]: InteractionType::MESSAGE_COMPONENT MessageComponent(MessageComponentInteractionData), - /// Data received for the [`ModalSubmit`] interaction type. + /// Data received for the [`MODAL_SUBMIT`] interaction type. /// - /// [`ModalSubmit`]: InteractionType::ModalSubmit + /// [`MODAL_SUBMIT`]: InteractionType::MODAL_SUBMIT ModalSubmit(ModalInteractionData), } @@ -445,7 +445,7 @@ mod tests { guild_id: None, id: Id::new(300), name: "command name".into(), - kind: CommandType::ChatInput, + kind: CommandType::CHAT_INPUT, options: Vec::from([CommandDataOption { name: "member".into(), value: CommandOptionValue::User(Id::new(600)), @@ -497,7 +497,7 @@ mod tests { guild_id: Some(Id::new(400)), guild_locale: Some("de".to_owned()), id: Id::new(500), - kind: InteractionType::ApplicationCommand, + kind: InteractionType::APPLICATION_COMMAND, locale: Some("en-GB".to_owned()), member: Some(PartialMember { avatar: None, @@ -562,6 +562,9 @@ mod tests { Token::Str("name"), Token::Str("command name"), Token::Str("type"), + Token::NewtypeStruct { + name: "CommandType", + }, Token::U8(1), Token::Str("options"), Token::Seq { len: Some(1) }, @@ -572,7 +575,10 @@ mod tests { Token::Str("name"), Token::Str("member"), Token::Str("type"), - Token::U8(CommandOptionType::User as u8), + Token::NewtypeStruct { + name: "CommandOptionType", + }, + Token::U8(CommandOptionType::USER.get()), Token::Str("value"), Token::NewtypeStruct { name: "Id" }, Token::Str("600"), @@ -649,7 +655,10 @@ mod tests { Token::NewtypeStruct { name: "Id" }, Token::Str("500"), Token::Str("type"), - Token::U8(InteractionType::ApplicationCommand as u8), + Token::NewtypeStruct { + name: "InteractionType", + }, + Token::U8(InteractionType::APPLICATION_COMMAND.get()), Token::Str("locale"), Token::Some, Token::Str("en-GB"), diff --git a/twilight-model/src/application/interaction/modal.rs b/twilight-model/src/application/interaction/modal.rs index 376621ce3e5..4e6c93bda6c 100644 --- a/twilight-model/src/application/interaction/modal.rs +++ b/twilight-model/src/application/interaction/modal.rs @@ -1,16 +1,16 @@ -//! [`ModalSubmit`] interaction. +//! [`MODAL_SUBMIT`] interaction. //! //! -//! [`ModalSubmit`]: crate::application::interaction::InteractionType::ModalSubmit +//! [`MODAL_SUBMIT`]: crate::application::interaction::InteractionType::MODAL_SUBMIT use crate::channel::message::component::ComponentType; use serde::{ser::SerializeStruct, Deserialize, Serialize, Serializer}; -/// Data received when an [`ModalSubmit`] interaction is executed. +/// Data received when an [`MODAL_SUBMIT`] interaction is executed. /// /// See [Discord Docs/Modal Submit Data Structure]. /// -/// [`ModalSubmit`]: crate::application::interaction::InteractionType::ModalSubmit +/// [`MODAL_SUBMIT`]: crate::application::interaction::InteractionType::MODAL_SUBMIT /// [Discord Docs/Modal Submit Data Structure]: https://discord.com/developers/docs/interactions/receiving-and-responding#interaction-object-modal-submit-data-structure #[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)] pub struct ModalInteractionData { @@ -40,7 +40,7 @@ impl Serialize for ModalInteractionDataActionRow { fn serialize(&self, serializer: S) -> Result { let mut state = serializer.serialize_struct("ModalInteractionDataActionRow", 2)?; - state.serialize_field("type", &ComponentType::ActionRow)?; + state.serialize_field("type", &ComponentType::ACTION_ROW)?; state.serialize_field("components", &self.components)?; state.end() @@ -117,7 +117,7 @@ mod tests { components: Vec::from([ModalInteractionDataActionRow { components: Vec::from([ModalInteractionDataComponent { custom_id: "the-data-id".to_owned(), - kind: ComponentType::TextInput, + kind: ComponentType::TEXT_INPUT, value: Some("input value".into()), }]), }]), @@ -137,7 +137,10 @@ mod tests { len: 2, }, Token::String("type"), - Token::U8(ComponentType::ActionRow.into()), + Token::NewtypeStruct { + name: "ComponentType", + }, + Token::U8(ComponentType::ACTION_ROW.get()), Token::String("components"), Token::Seq { len: Some(1) }, Token::Struct { @@ -147,7 +150,10 @@ mod tests { Token::String("custom_id"), Token::String("the-data-id"), Token::String("type"), - Token::U8(ComponentType::TextInput.into()), + Token::NewtypeStruct { + name: "ComponentType", + }, + Token::U8(ComponentType::TEXT_INPUT.get()), Token::String("value"), Token::Some, Token::String("input value"), diff --git a/twilight-model/src/channel/channel_mention.rs b/twilight-model/src/channel/channel_mention.rs index 06463420f50..03e10cebd7c 100644 --- a/twilight-model/src/channel/channel_mention.rs +++ b/twilight-model/src/channel/channel_mention.rs @@ -27,7 +27,7 @@ mod tests { let value = ChannelMention { guild_id: Id::new(1), id: Id::new(2), - kind: ChannelType::GuildText, + kind: ChannelType::GUILD_TEXT, name: "channel".to_owned(), }; @@ -45,6 +45,9 @@ mod tests { Token::NewtypeStruct { name: "Id" }, Token::Str("2"), Token::Str("type"), + Token::NewtypeStruct { + name: "ChannelType", + }, Token::U8(0), Token::Str("name"), Token::Str("channel"), diff --git a/twilight-model/src/channel/channel_type.rs b/twilight-model/src/channel/channel_type.rs index a77aab2810c..bd1a36b4f1d 100644 --- a/twilight-model/src/channel/channel_type.rs +++ b/twilight-model/src/channel/channel_type.rs @@ -1,94 +1,83 @@ use serde::{Deserialize, Serialize}; -#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)] -#[non_exhaustive] -#[serde(from = "u8", into = "u8")] -pub enum ChannelType { - GuildText, - Private, - GuildVoice, - Group, - GuildCategory, - GuildAnnouncement, - AnnouncementThread, - PublicThread, - PrivateThread, - GuildStageVoice, +#[derive(Clone, Copy, Deserialize, Eq, Hash, PartialEq, Serialize)] +pub struct ChannelType(u8); + +impl ChannelType { + pub const GUILD_TEXT: Self = Self::new(0); + + pub const PRIVATE: Self = Self::new(1); + + pub const GUILD_VOICE: Self = Self::new(2); + + pub const GROUP: Self = Self::new(3); + + pub const GUILD_CATEGORY: Self = Self::new(4); + + pub const GUILD_ANNOUNCEMENT: Self = Self::new(5); + + pub const ANNOUNCEMENT_THREAD: Self = Self::new(10); + + pub const PUBLIC_THREAD: Self = Self::new(11); + + pub const PRIVATE_THREAD: Self = Self::new(12); + + pub const GUILD_STAGE_VOICE: Self = Self::new(13); + /// Channel in a [hub] containing the listed servers. /// /// [hub]: https://support.discord.com/hc/en-us/articles/4406046651927-Discord-Student-Hubs-FAQ - GuildDirectory, - /// Channel that can only contain threads. - GuildForum, - Unknown(u8), -} + pub const GUILD_DIRECTORY: Self = Self::new(14); -impl From for ChannelType { - fn from(value: u8) -> Self { - match value { - 0 => ChannelType::GuildText, - 1 => ChannelType::Private, - 2 => ChannelType::GuildVoice, - 3 => ChannelType::Group, - 4 => ChannelType::GuildCategory, - 5 => ChannelType::GuildAnnouncement, - 10 => ChannelType::AnnouncementThread, - 11 => ChannelType::PublicThread, - 12 => ChannelType::PrivateThread, - 13 => ChannelType::GuildStageVoice, - 14 => ChannelType::GuildDirectory, - 15 => ChannelType::GuildForum, - unknown => ChannelType::Unknown(unknown), - } - } -} + /// Channel that can only contain threads. + pub const GUILD_FORUM: Self = Self::new(15); -impl From for u8 { - fn from(value: ChannelType) -> Self { - match value { - ChannelType::GuildText => 0, - ChannelType::Private => 1, - ChannelType::GuildVoice => 2, - ChannelType::Group => 3, - ChannelType::GuildCategory => 4, - ChannelType::GuildAnnouncement => 5, - ChannelType::AnnouncementThread => 10, - ChannelType::PublicThread => 11, - ChannelType::PrivateThread => 12, - ChannelType::GuildStageVoice => 13, - ChannelType::GuildDirectory => 14, - ChannelType::GuildForum => 15, - ChannelType::Unknown(unknown) => unknown, - } + /// Name of the associated constant. + /// + /// Returns `None` if the value doesn't have a defined constant. + pub const fn name(self) -> Option<&'static str> { + Some(match self { + Self::GUILD_TEXT => "GUILD_TEXT", + Self::PRIVATE => "PRIVATE", + Self::GUILD_VOICE => "GUILD_VOICE", + Self::GROUP => "GROUP", + Self::GUILD_CATEGORY => "GUILD_CATEGORY", + Self::GUILD_ANNOUNCEMENT => "GUILD_ANNOUNCEMENT", + Self::ANNOUNCEMENT_THREAD => "ANNOUNCEMENT_THREAD", + Self::PUBLIC_THREAD => "PUBLIC_THREAD", + Self::PRIVATE_THREAD => "PRIVATE_THREAD", + Self::GUILD_STAGE_VOICE => "GUILD_STAGE_VOICE", + Self::GUILD_DIRECTORY => "GUILD_DIRECTORY", + Self::GUILD_FORUM => "GUILD_FORUM", + _ => return None, + }) } -} -impl ChannelType { /// Whether the channel type is that of a guild. /// /// The following channel types are considered guild channel types: /// - /// - [`AnnouncementThread`][`Self::AnnouncementThread`] - /// - [`GuildAnnouncement`][`Self::GuildAnnouncement`] - /// - [`GuildCategory`][`Self::GuildCategory`] - /// - [`GuildDirectory`][`Self::GuildDirectory`] - /// - [`GuildStageVoice`][`Self::GuildStageVoice`] - /// - [`GuildText`][`Self::GuildText`] - /// - [`GuildVoice`][`Self::GuildVoice`] - /// - [`PublicThread`][`Self::PublicThread`] - /// - [`PrivateThread`][`Self::PrivateThread`] + /// - [`ANNOUNCEMENT_THREAD`][`Self::ANNOUNCEMENT_THREAD`] + /// - [`GUILD_ANNOUNCEMENT`][`Self::GUILD_ANNOUNCEMENT`] + /// - [`GUILD_CATEGORY`][`Self::GUILD_CATEGORY`] + /// - [`GUILD_DIRECTORY`][`Self::GUILD_DIRECTORY`] + /// - [`GUILD_STAGE_VOICE`][`Self::GUILD_STAGE_VOICE`] + /// - [`GUILD_TEXT`][`Self::GUILD_TEXT`] + /// - [`GUILD_VOICE`][`Self::GUILD_VOICE`] + /// - [`PUBLIC_THREAD`][`Self::PUBLIC_THREAD`] + /// - [`PRIVATE_THREAD`][`Self::PRIVATE_THREAD`] pub const fn is_guild(self) -> bool { matches!( self, - Self::GuildCategory - | Self::GuildDirectory - | Self::GuildAnnouncement - | Self::AnnouncementThread - | Self::PublicThread - | Self::PrivateThread - | Self::GuildStageVoice - | Self::GuildText - | Self::GuildVoice + Self::GUILD_CATEGORY + | Self::GUILD_DIRECTORY + | Self::GUILD_ANNOUNCEMENT + | Self::ANNOUNCEMENT_THREAD + | Self::PUBLIC_THREAD + | Self::PRIVATE_THREAD + | Self::GUILD_STAGE_VOICE + | Self::GUILD_TEXT + | Self::GUILD_VOICE ) } @@ -96,85 +85,69 @@ impl ChannelType { /// /// The following channel types are considered guild channel types: /// - /// - [`AnnouncementThread`][`Self::AnnouncementThread`] - /// - [`PrivateThread`][`Self::PrivateThread`] - /// - [`PublicThread`][`Self::PublicThread`] + /// - [`ANNOUNCEMENT_THREAD`][`Self::ANNOUNCEMENT_THREAD`] + /// - [`PRIVATE_THREAD`][`Self::PRIVATE_THREAD`] + /// - [`PUBLIC_THREAD`][`Self::PUBLIC_THREAD`] pub const fn is_thread(self) -> bool { matches!( self, - Self::AnnouncementThread | Self::PublicThread | Self::PrivateThread + Self::ANNOUNCEMENT_THREAD | Self::PUBLIC_THREAD | Self::PRIVATE_THREAD ) } - - /// Name of the variant as a string slice. - pub const fn name(self) -> &'static str { - match self { - Self::AnnouncementThread => "AnnouncementThread", - Self::Group => "Group", - Self::GuildCategory => "GuildCategory", - Self::GuildDirectory => "GuildDirectory", - Self::GuildForum => "GuildForum", - Self::GuildAnnouncement => "GuildAnnouncement", - Self::GuildStageVoice => "GuildStageVoice", - Self::GuildText => "GuildText", - Self::GuildVoice => "GuildVoice", - Self::Private => "Private", - Self::PrivateThread => "PrivateThread", - Self::PublicThread => "PublicThread", - Self::Unknown(_) => "Unknown", - } - } } +impl_typed!(ChannelType, u8); + #[cfg(test)] mod tests { use super::ChannelType; use serde_test::Token; use static_assertions::const_assert; - const_assert!(ChannelType::GuildCategory.is_guild()); - const_assert!(ChannelType::GuildDirectory.is_guild()); - const_assert!(ChannelType::GuildAnnouncement.is_guild()); - const_assert!(ChannelType::AnnouncementThread.is_guild()); - const_assert!(ChannelType::PublicThread.is_guild()); - const_assert!(ChannelType::PrivateThread.is_guild()); - const_assert!(ChannelType::GuildStageVoice.is_guild()); - const_assert!(ChannelType::GuildText.is_guild()); - const_assert!(ChannelType::GuildVoice.is_guild()); + const_assert!(ChannelType::GUILD_CATEGORY.is_guild()); + const_assert!(ChannelType::GUILD_DIRECTORY.is_guild()); + const_assert!(ChannelType::GUILD_ANNOUNCEMENT.is_guild()); + const_assert!(ChannelType::ANNOUNCEMENT_THREAD.is_guild()); + const_assert!(ChannelType::PUBLIC_THREAD.is_guild()); + const_assert!(ChannelType::PRIVATE_THREAD.is_guild()); + const_assert!(ChannelType::GUILD_STAGE_VOICE.is_guild()); + const_assert!(ChannelType::GUILD_TEXT.is_guild()); + const_assert!(ChannelType::GUILD_VOICE.is_guild()); - const_assert!(ChannelType::AnnouncementThread.is_thread()); - const_assert!(ChannelType::PublicThread.is_thread()); - const_assert!(ChannelType::PrivateThread.is_thread()); + const_assert!(ChannelType::ANNOUNCEMENT_THREAD.is_thread()); + const_assert!(ChannelType::PUBLIC_THREAD.is_thread()); + const_assert!(ChannelType::PRIVATE_THREAD.is_thread()); - #[test] - fn variants() { - serde_test::assert_tokens(&ChannelType::GuildText, &[Token::U8(0)]); - serde_test::assert_tokens(&ChannelType::Private, &[Token::U8(1)]); - serde_test::assert_tokens(&ChannelType::GuildVoice, &[Token::U8(2)]); - serde_test::assert_tokens(&ChannelType::Group, &[Token::U8(3)]); - serde_test::assert_tokens(&ChannelType::GuildCategory, &[Token::U8(4)]); - serde_test::assert_tokens(&ChannelType::GuildAnnouncement, &[Token::U8(5)]); - serde_test::assert_tokens(&ChannelType::AnnouncementThread, &[Token::U8(10)]); - serde_test::assert_tokens(&ChannelType::PublicThread, &[Token::U8(11)]); - serde_test::assert_tokens(&ChannelType::PrivateThread, &[Token::U8(12)]); - serde_test::assert_tokens(&ChannelType::GuildStageVoice, &[Token::U8(13)]); - serde_test::assert_tokens(&ChannelType::GuildDirectory, &[Token::U8(14)]); - serde_test::assert_tokens(&ChannelType::Unknown(99), &[Token::U8(99)]); - } + const MAP: &[(ChannelType, u8, &str)] = &[ + (ChannelType::GUILD_TEXT, 0, "GUILD_TEXT"), + (ChannelType::PRIVATE, 1, "PRIVATE"), + (ChannelType::GUILD_VOICE, 2, "GUILD_VOICE"), + (ChannelType::GROUP, 3, "GROUP"), + (ChannelType::GUILD_CATEGORY, 4, "GUILD_CATEGORY"), + (ChannelType::GUILD_ANNOUNCEMENT, 5, "GUILD_ANNOUNCEMENT"), + (ChannelType::ANNOUNCEMENT_THREAD, 10, "ANNOUNCEMENT_THREAD"), + (ChannelType::PUBLIC_THREAD, 11, "PUBLIC_THREAD"), + (ChannelType::PRIVATE_THREAD, 12, "PRIVATE_THREAD"), + (ChannelType::GUILD_STAGE_VOICE, 13, "GUILD_STAGE_VOICE"), + (ChannelType::GUILD_DIRECTORY, 14, "GUILD_DIRECTORY"), + (ChannelType::GUILD_FORUM, 15, "GUILD_FORUM"), + ]; #[test] - fn names() { - assert_eq!("AnnouncementThread", ChannelType::AnnouncementThread.name()); - assert_eq!("Group", ChannelType::Group.name()); - assert_eq!("GuildCategory", ChannelType::GuildCategory.name()); - assert_eq!("GuildDirectory", ChannelType::GuildDirectory.name()); - assert_eq!("GuildAnnouncement", ChannelType::GuildAnnouncement.name()); - assert_eq!("GuildStageVoice", ChannelType::GuildStageVoice.name()); - assert_eq!("GuildText", ChannelType::GuildText.name()); - assert_eq!("GuildVoice", ChannelType::GuildVoice.name()); - assert_eq!("Private", ChannelType::Private.name()); - assert_eq!("PrivateThread", ChannelType::PrivateThread.name()); - assert_eq!("PublicThread", ChannelType::PublicThread.name()); - assert_eq!("Unknown", ChannelType::Unknown(99).name()); + fn variants() { + for (kind, num, name) in MAP { + serde_test::assert_tokens( + kind, + &[ + Token::NewtypeStruct { + name: "ChannelType", + }, + Token::U8(*num), + ], + ); + assert_eq!(*kind, ChannelType::from(*num)); + assert_eq!(*num, kind.get()); + assert_eq!(kind.name(), Some(*name)); + } } } diff --git a/twilight-model/src/channel/forum.rs b/twilight-model/src/channel/forum.rs index 87bcfd3f96d..6c95475f91d 100644 --- a/twilight-model/src/channel/forum.rs +++ b/twilight-model/src/channel/forum.rs @@ -22,106 +22,66 @@ pub struct DefaultReaction { /// Layout of a [channel] that is a [forum]. /// /// [channel]: super::Channel -/// [forum]: super::ChannelType::GuildForum -#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)] -#[non_exhaustive] -#[serde(from = "u8", into = "u8")] -pub enum ForumLayout { +/// [forum]: super::ChannelType::GUILD_FORUM +#[derive(Clone, Copy, Deserialize, Eq, Hash, PartialEq, Serialize)] +pub struct ForumLayout(u8); + +impl ForumLayout { /// Display posts as a collection of tiles. - GalleryView, + pub const GALLERY_VIEW: Self = Self::new(2); + /// Display posts as a list. - ListView, - /// No default has been set for the forum channel. - NotSet, - /// Variant value is unknown to the library. - Unknown(u8), -} + pub const LIST_VIEW: Self = Self::new(1); -impl ForumLayout { - pub const fn name(self) -> &'static str { - match self { - Self::ListView => "ListView", - Self::NotSet => "NotSet", - Self::GalleryView => "GalleryView", - Self::Unknown(_) => "Unknown", - } - } -} + /// No default has been set for the forum channel. + pub const NOT_SET: Self = Self::new(0); -impl From for ForumLayout { - fn from(value: u8) -> Self { - match value { - 0 => Self::NotSet, - 1 => Self::ListView, - 2 => Self::GalleryView, - unknown => Self::Unknown(unknown), - } + /// Name of the associated constant. + /// + /// Returns `None` if the value doesn't have a defined constant. + pub const fn name(self) -> Option<&'static str> { + Some(match self { + Self::GALLERY_VIEW => "GALLERY_VIEW", + Self::LIST_VIEW => "LIST_VIEW", + Self::NOT_SET => "NOT_SET", + _ => return None, + }) } } -impl From for u8 { - fn from(value: ForumLayout) -> Self { - match value { - ForumLayout::NotSet => 0, - ForumLayout::ListView => 1, - ForumLayout::GalleryView => 2, - ForumLayout::Unknown(unknown) => unknown, - } - } -} +impl_typed!(ForumLayout, u8); /// Layout of a [channel] that is a [forum]. /// /// [channel]: super::Channel -/// [forum]: super::ChannelType::GuildForum -#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)] -#[non_exhaustive] -#[serde(from = "u8", into = "u8")] -pub enum ForumSortOrder { - /// Sort forum posts by creation time (from most recent to oldest). - CreationDate, - /// Sort forum posts by activity. - LatestActivity, - /// Variant value is unknown to the library. - Unknown(u8), -} +/// [forum]: super::ChannelType::GUILD_FORUM +#[derive(Clone, Copy, Deserialize, Eq, Hash, PartialEq, Serialize)] +pub struct ForumSortOrder(u8); impl ForumSortOrder { - pub const fn name(self) -> &'static str { - match self { - Self::CreationDate => "CreationDate", - Self::LatestActivity => "LatestActivity", - Self::Unknown(_) => "Unknown", - } - } -} + /// Sort forum posts by activity. + pub const LATEST_ACTIVITY: Self = Self::new(0); -impl From for ForumSortOrder { - fn from(value: u8) -> Self { - match value { - 0 => Self::LatestActivity, - 1 => Self::CreationDate, - unknown => Self::Unknown(unknown), - } - } -} + /// Sort forum posts by creation time (from most recent to oldest). + pub const CREATION_DATE: Self = Self::new(1); -impl From for u8 { - fn from(value: ForumSortOrder) -> Self { - match value { - ForumSortOrder::LatestActivity => 0, - ForumSortOrder::CreationDate => 1, - ForumSortOrder::Unknown(unknown) => unknown, - } + pub const fn name(self) -> Option<&'static str> { + Some(match self { + Self::CREATION_DATE => "CREATION_DATE", + Self::LATEST_ACTIVITY => "LATEST_ACTIVITY", + _ => return None, + }) } } -/// Tag that is able to be applied to a thread in a [`GuildForum`] [`Channel`]. +impl_typed!(ForumSortOrder, u8); + +/// Tag that is able to be applied to a thread in a [`GUILD_FORUM`] [`Channel`]. /// /// May at most contain one of `emoji_id` and `emoji_name`. /// /// [`Channel`]: super::Channel -/// [`GuildForum`]: super::ChannelType::GuildForum +/// [`GUILD_FORUM`]: super::ChannelType::GUILD_FORUM #[derive(Clone, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)] pub struct ForumTag { /// ID of custom guild emoji. @@ -215,33 +175,47 @@ mod tests { #[test] fn forum_layout() { const MAP: &[(ForumLayout, u8, &str)] = &[ - (ForumLayout::NotSet, 0, "NotSet"), - (ForumLayout::ListView, 1, "ListView"), - (ForumLayout::GalleryView, 2, "GalleryView"), - (ForumLayout::Unknown(3), 3, "Unknown"), + (ForumLayout::NOT_SET, 0, "NOT_SET"), + (ForumLayout::LIST_VIEW, 1, "LIST_VIEW"), + (ForumLayout::GALLERY_VIEW, 2, "GALLERY_VIEW"), ]; for (layout, number, name) in MAP { - assert_eq!(layout.name(), *name); assert_eq!(u8::from(*layout), *number); assert_eq!(ForumLayout::from(*number), *layout); - assert_tokens(layout, &[Token::U8(*number)]); + assert_tokens( + layout, + &[ + Token::NewtypeStruct { + name: "ForumLayout", + }, + Token::U8(*number), + ], + ); + assert_eq!(layout.name(), Some(*name)); } } #[test] fn forum_sort_order() { const MAP: &[(ForumSortOrder, u8, &str)] = &[ - (ForumSortOrder::LatestActivity, 0, "LatestActivity"), - (ForumSortOrder::CreationDate, 1, "CreationDate"), - (ForumSortOrder::Unknown(100), 100, "Unknown"), + (ForumSortOrder::LATEST_ACTIVITY, 0, "LATEST_ACTIVITY"), + (ForumSortOrder::CREATION_DATE, 1, "CREATION_DATE"), ]; for (layout, number, name) in MAP { - assert_eq!(layout.name(), *name); + assert_eq!(layout.name(), Some(*name)); assert_eq!(u8::from(*layout), *number); assert_eq!(ForumSortOrder::from(*number), *layout); - assert_tokens(layout, &[Token::U8(*number)]); + assert_tokens( + layout, + &[ + Token::NewtypeStruct { + name: "ForumSortOrder", + }, + Token::U8(*number), + ], + ); } } diff --git a/twilight-model/src/channel/message/activity.rs b/twilight-model/src/channel/message/activity.rs index f78e2a27833..3166fc0d44c 100644 --- a/twilight-model/src/channel/message/activity.rs +++ b/twilight-model/src/channel/message/activity.rs @@ -12,45 +12,38 @@ pub struct MessageActivity { } /// Activity of this message. -#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)] -#[non_exhaustive] -#[serde(from = "u8", into = "u8")] -pub enum MessageActivityType { +#[derive(Clone, Copy, Deserialize, Eq, Hash, PartialEq, Serialize)] + +pub struct MessageActivityType(u8); + +impl MessageActivityType { /// Join the the party. - Join, + pub const JOIN: Self = Self::new(1); + /// Spectate on or with the party. - Spectate, + pub const SPECTATE: Self = Self::new(2); + /// Listen to or with the party. - Listen, + pub const LISTEN: Self = Self::new(3); + /// Request to join the party. - JoinRequest, - /// Variant value is unknown to the library. - Unknown(u8), -} + pub const JOIN_REQUEST: Self = Self::new(5); -impl From for MessageActivityType { - fn from(value: u8) -> Self { - match value { - 1 => Self::Join, - 2 => Self::Spectate, - 3 => Self::Listen, - 5 => Self::JoinRequest, - unknown => Self::Unknown(unknown), - } + /// Name of the associated constant. + /// + /// Returns `None` if the value doesn't have a defined constant. + pub const fn name(self) -> Option<&'static str> { + Some(match self { + Self::JOIN => "JOIN", + Self::SPECTATE => "SPECTATE", + Self::LISTEN => "LISTEN", + Self::JOIN_REQUEST => "JOIN_REQUEST", + _ => return None, + }) } } -impl From for u8 { - fn from(value: MessageActivityType) -> Self { - match value { - MessageActivityType::Join => 1, - MessageActivityType::Spectate => 2, - MessageActivityType::Listen => 3, - MessageActivityType::JoinRequest => 5, - MessageActivityType::Unknown(unknown) => unknown, - } - } -} +impl_typed!(MessageActivityType, u8); #[cfg(test)] mod tests { @@ -60,7 +53,7 @@ mod tests { #[test] fn message_activity() { let value = MessageActivity { - kind: MessageActivityType::Join, + kind: MessageActivityType::JOIN, party_id: None, }; @@ -72,6 +65,9 @@ mod tests { len: 1, }, Token::Str("type"), + Token::NewtypeStruct { + name: "MessageActivityType", + }, Token::U8(1), Token::StructEnd, ], @@ -81,7 +77,7 @@ mod tests { #[test] fn message_activity_complete() { let value = MessageActivity { - kind: MessageActivityType::Join, + kind: MessageActivityType::JOIN, party_id: Some("test".to_owned()), }; @@ -93,6 +89,9 @@ mod tests { len: 2, }, Token::Str("type"), + Token::NewtypeStruct { + name: "MessageActivityType", + }, Token::U8(1), Token::Str("party_id"), Token::Some, @@ -104,10 +103,50 @@ mod tests { #[test] fn variants() { - serde_test::assert_tokens(&MessageActivityType::Join, &[Token::U8(1)]); - serde_test::assert_tokens(&MessageActivityType::Spectate, &[Token::U8(2)]); - serde_test::assert_tokens(&MessageActivityType::Listen, &[Token::U8(3)]); - serde_test::assert_tokens(&MessageActivityType::JoinRequest, &[Token::U8(5)]); - serde_test::assert_tokens(&MessageActivityType::Unknown(99), &[Token::U8(99)]); + serde_test::assert_tokens( + &MessageActivityType::JOIN, + &[ + Token::NewtypeStruct { + name: "MessageActivityType", + }, + Token::U8(1), + ], + ); + serde_test::assert_tokens( + &MessageActivityType::SPECTATE, + &[ + Token::NewtypeStruct { + name: "MessageActivityType", + }, + Token::U8(2), + ], + ); + serde_test::assert_tokens( + &MessageActivityType::LISTEN, + &[ + Token::NewtypeStruct { + name: "MessageActivityType", + }, + Token::U8(3), + ], + ); + serde_test::assert_tokens( + &MessageActivityType::JOIN_REQUEST, + &[ + Token::NewtypeStruct { + name: "MessageActivityType", + }, + Token::U8(5), + ], + ); + serde_test::assert_tokens( + &MessageActivityType::new(99), + &[ + Token::NewtypeStruct { + name: "MessageActivityType", + }, + Token::U8(99), + ], + ); } } diff --git a/twilight-model/src/channel/message/allowed_mentions.rs b/twilight-model/src/channel/message/allowed_mentions.rs index ffd062f4ab8..8d9ee4b0765 100644 --- a/twilight-model/src/channel/message/allowed_mentions.rs +++ b/twilight-model/src/channel/message/allowed_mentions.rs @@ -3,7 +3,7 @@ use crate::{ marker::{RoleMarker, UserMarker}, Id, }, - util::is_false, + util::{is_false, known_string::KnownString}, }; use serde::{Deserialize, Serialize}; @@ -23,7 +23,7 @@ use serde::{Deserialize, Serialize}; pub struct AllowedMentions { /// List of allowed mention types. /// - /// [`MentionType::Roles`] and [`MentionType::Users`] allows all roles and users to be + /// [`MentionType::ROLES`] and [`MentionType::USERS`] allows all roles and users to be /// mentioned; they are mutually exclusive with the [`roles`] and [`users`] fields. /// /// [`roles`]: Self::roles @@ -44,18 +44,34 @@ pub struct AllowedMentions { } /// Allowed mention type. -#[derive(Clone, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)] -#[non_exhaustive] -#[serde(rename_all = "lowercase")] -pub enum MentionType { +#[derive(Clone, Copy, Deserialize, Eq, Hash, PartialEq, Serialize)] +pub struct MentionType(KnownString<16>); + +impl MentionType { /// `@everyone` and `@here` mentions. - Everyone, + pub const EVERYONE: Self = Self::from_bytes(b"everyone"); + /// Role mentions. - Roles, + pub const ROLES: Self = Self::from_bytes(b"roles"); + /// User mentions. - Users, + pub const USERS: Self = Self::from_bytes(b"users"); + + /// Name of the associated constant. + /// + /// Returns `None` if the value doesn't have a defined constant. + pub const fn name(self) -> Option<&'static str> { + Some(match self { + Self::EVERYONE => "EVERYONE", + Self::ROLES => "ROLES", + Self::USERS => "USERS", + _ => return None, + }) + } } +impl_typed!(MentionType, String); + #[cfg(test)] mod tests { use super::{AllowedMentions, MentionType}; @@ -89,7 +105,7 @@ mod tests { #[test] fn full() { let value = AllowedMentions { - parse: Vec::from([MentionType::Everyone]), + parse: Vec::from([MentionType::EVERYONE]), users: Vec::from([Id::new(100)]), roles: Vec::from([Id::new(200)]), replied_user: true, @@ -104,10 +120,10 @@ mod tests { }, Token::Str("parse"), Token::Seq { len: Some(1) }, - Token::UnitVariant { + Token::NewtypeStruct { name: "MentionType", - variant: "everyone", }, + Token::Str("everyone"), Token::SeqEnd, Token::Str("replied_user"), Token::Bool(true), diff --git a/twilight-model/src/channel/message/component/button.rs b/twilight-model/src/channel/message/component/button.rs index efb24707498..8d809433c3a 100644 --- a/twilight-model/src/channel/message/component/button.rs +++ b/twilight-model/src/channel/message/component/button.rs @@ -10,10 +10,10 @@ pub struct Button { /// /// This field is required when using the following [`ButtonStyle`]s: /// - /// - [`ButtonStyle::Danger`] - /// - [`ButtonStyle::Primary`] - /// - [`ButtonStyle::Secondary`] - /// - [`ButtonStyle::Success`] + /// - [`ButtonStyle::DANGER`] + /// - [`ButtonStyle::PRIMARY`] + /// - [`ButtonStyle::SECONDARY`] + /// - [`ButtonStyle::SUCCESS`] pub custom_id: Option, /// Whether the button is disabled. /// @@ -25,74 +25,65 @@ pub struct Button { pub label: Option, /// Style variant of the button. pub style: ButtonStyle, - /// URL for buttons of a [`ButtonStyle::Link`] style. + /// URL for buttons of a [`ButtonStyle::LINK`] style. pub url: Option, } /// Style of a [`Button`]. // Keep in sync with `twilight-validate::component`! -#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)] -#[non_exhaustive] -#[serde(from = "u8", into = "u8")] -pub enum ButtonStyle { +#[derive(Clone, Copy, Deserialize, Eq, Hash, PartialEq, Serialize)] +pub struct ButtonStyle(u8); + +impl ButtonStyle { /// Button indicates a primary action. /// /// Selecting this button style requires specifying the /// [`Button::custom_id`] field. - Primary, + pub const PRIMARY: Self = Self::new(1); + /// Button indicates a secondary action. /// /// Selecting this button style requires specifying the /// [`Button::custom_id`] field. - Secondary, + pub const SECONDARY: Self = Self::new(2); + /// Button indicates a successful action. /// /// Selecting this button style requires specifying the /// [`Button::custom_id`] field. - Success, + pub const SUCCESS: Self = Self::new(3); + /// Button indicates a dangerous action. /// /// Selecting this button style requires specifying the /// [`Button::custom_id`] field. - Danger, + pub const DANGER: Self = Self::new(4); + /// Button indicates an action with a link. /// /// Selecting this button style requires specifying the [`Button::url`] /// field. - Link, - /// Variant value is unknown to the library. - Unknown(u8), -} + pub const LINK: Self = Self::new(5); -impl From for ButtonStyle { - fn from(value: u8) -> Self { - match value { - 1 => ButtonStyle::Primary, - 2 => ButtonStyle::Secondary, - 3 => ButtonStyle::Success, - 4 => ButtonStyle::Danger, - 5 => ButtonStyle::Link, - unknown => ButtonStyle::Unknown(unknown), - } + /// Name of the associated constant. + /// + /// Returns `None` if the value doesn't have a defined constant. + pub const fn name(self) -> Option<&'static str> { + Some(match self { + Self::DANGER => "DANGER", + Self::LINK => "LINK", + Self::PRIMARY => "PRIMARY", + Self::SECONDARY => "SECONDARY", + Self::SUCCESS => "SUCCESS", + _ => return None, + }) } } -impl From for u8 { - fn from(value: ButtonStyle) -> Self { - match value { - ButtonStyle::Primary => 1, - ButtonStyle::Secondary => 2, - ButtonStyle::Success => 3, - ButtonStyle::Danger => 4, - ButtonStyle::Link => 5, - ButtonStyle::Unknown(unknown) => unknown, - } - } -} +impl_typed!(ButtonStyle, u8); #[cfg(test)] mod tests { - use super::*; use serde::{Deserialize, Serialize}; use serde_test::Token; @@ -116,12 +107,27 @@ mod tests { ); #[test] - fn button_style() { - serde_test::assert_tokens(&ButtonStyle::Primary, &[Token::U8(1)]); - serde_test::assert_tokens(&ButtonStyle::Secondary, &[Token::U8(2)]); - serde_test::assert_tokens(&ButtonStyle::Success, &[Token::U8(3)]); - serde_test::assert_tokens(&ButtonStyle::Danger, &[Token::U8(4)]); - serde_test::assert_tokens(&ButtonStyle::Link, &[Token::U8(5)]); - serde_test::assert_tokens(&ButtonStyle::Unknown(99), &[Token::U8(99)]); + fn button_style_variants() { + const MAP: &[(ButtonStyle, u8)] = &[ + (ButtonStyle::PRIMARY, 1), + (ButtonStyle::SECONDARY, 2), + (ButtonStyle::SUCCESS, 3), + (ButtonStyle::DANGER, 4), + (ButtonStyle::LINK, 5), + ]; + + for (kind, num) in MAP { + serde_test::assert_tokens( + kind, + &[ + Token::NewtypeStruct { + name: "ButtonStyle", + }, + Token::U8(*num), + ], + ); + assert_eq!(*kind, ButtonStyle::from(*num)); + assert_eq!(*num, kind.get()); + } } } diff --git a/twilight-model/src/channel/message/component/kind.rs b/twilight-model/src/channel/message/component/kind.rs index 4d1b2852aac..b057330b822 100644 --- a/twilight-model/src/channel/message/component/kind.rs +++ b/twilight-model/src/channel/message/component/kind.rs @@ -1,126 +1,86 @@ use serde::{Deserialize, Serialize}; -use std::fmt::{Display, Formatter, Result as FmtResult}; /// Type of [`Component`]. /// /// [`Component`]: super::Component -#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)] -#[non_exhaustive] -#[serde(from = "u8", into = "u8")] -pub enum ComponentType { +#[derive(Clone, Copy, Deserialize, Eq, Hash, PartialEq, Serialize)] +pub struct ComponentType(u8); + +impl ComponentType { /// Component is an [`ActionRow`]. /// /// [`ActionRow`]: super::ActionRow - ActionRow, + pub const ACTION_ROW: Self = Self::new(1); + /// Component is an [`Button`]. /// /// [`Button`]: super::Button - Button, + pub const BUTTON: Self = Self::new(2); + /// Component is an [`SelectMenu`]. /// /// [`SelectMenu`]: super::SelectMenu - SelectMenu, + pub const SELECT_MENU: Self = Self::new(3); + /// Component is an [`TextInput`]. /// /// [`TextInput`]: super::TextInput - TextInput, - /// Variant value is unknown to the library. - Unknown(u8), -} - -impl From for ComponentType { - fn from(value: u8) -> Self { - match value { - 1 => ComponentType::ActionRow, - 2 => ComponentType::Button, - 3 => ComponentType::SelectMenu, - 4 => ComponentType::TextInput, - unknown => ComponentType::Unknown(unknown), - } - } -} - -impl From for u8 { - fn from(value: ComponentType) -> Self { - match value { - ComponentType::ActionRow => 1, - ComponentType::Button => 2, - ComponentType::SelectMenu => 3, - ComponentType::TextInput => 4, - ComponentType::Unknown(unknown) => unknown, - } - } -} + pub const TEXT_INPUT: Self = Self::new(4); -impl ComponentType { - /// Name of the component type. + /// Name of the associated constant. /// - /// Variants have a name equivalent to the variant name itself. + /// Returns `None` if the value doesn't have a defined constant. /// /// # Examples /// - /// Check the [`ActionRow`] variant's name: + /// Check the [`ACTION_ROW`] constant's name: /// /// ``` /// use twilight_model::channel::message::component::ComponentType; /// - /// assert_eq!("ActionRow", ComponentType::ActionRow.name()); + /// assert_eq!(Some("ACTION_ROW"), ComponentType::ACTION_ROW.name()); /// ``` /// - /// [`ActionRow`]: Self::ActionRow - pub const fn name(self) -> &'static str { - match self { - Self::ActionRow => "ActionRow", - Self::Button => "Button", - Self::SelectMenu => "SelectMenu", - Self::TextInput => "TextInput", - Self::Unknown(_) => "Unknown", - } + /// [`ACTION_ROW`]: Self::ACTION_ROW + pub const fn name(self) -> Option<&'static str> { + Some(match self { + Self::ACTION_ROW => "ACTION_ROW", + Self::BUTTON => "BUTTON", + Self::SELECT_MENU => "SELECT_MENU", + Self::TEXT_INPUT => "TEXT_INPUT", + _ => return None, + }) } } -impl Display for ComponentType { - fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult { - f.write_str(self.name()) - } -} +impl_typed!(ComponentType, u8); #[cfg(test)] mod tests { - use super::*; - use serde::{Deserialize, Serialize}; + use super::ComponentType; use serde_test::Token; - use static_assertions::assert_impl_all; - use std::{fmt::Debug, hash::Hash}; - assert_impl_all!( - ComponentType: Clone, - Copy, - Debug, - Deserialize<'static>, - Eq, - Hash, - PartialEq, - Send, - Serialize, - Sync - ); + const MAP: &[(ComponentType, u8)] = &[ + (ComponentType::ACTION_ROW, 1), + (ComponentType::BUTTON, 2), + (ComponentType::SELECT_MENU, 3), + (ComponentType::TEXT_INPUT, 4), + ]; #[test] fn variants() { - serde_test::assert_tokens(&ComponentType::ActionRow, &[Token::U8(1)]); - serde_test::assert_tokens(&ComponentType::Button, &[Token::U8(2)]); - serde_test::assert_tokens(&ComponentType::SelectMenu, &[Token::U8(3)]); - serde_test::assert_tokens(&ComponentType::TextInput, &[Token::U8(4)]); - serde_test::assert_tokens(&ComponentType::Unknown(99), &[Token::U8(99)]); - } - - #[test] - fn names() { - assert_eq!("ActionRow", ComponentType::ActionRow.name()); - assert_eq!("Button", ComponentType::Button.name()); - assert_eq!("SelectMenu", ComponentType::SelectMenu.name()); - assert_eq!("TextInput", ComponentType::TextInput.name()); - assert_eq!("Unknown", ComponentType::Unknown(99).name()); + for (kind, num) in MAP { + serde_test::assert_tokens( + kind, + &[ + Token::NewtypeStruct { + name: "ComponentType", + }, + Token::U8(*num), + ], + ); + assert_eq!(*kind, ComponentType::from(*num)); + assert_eq!(*num, kind.get()); + } } } diff --git a/twilight-model/src/channel/message/component/mod.rs b/twilight-model/src/channel/message/component/mod.rs index c97b6455347..7a7dca5fdfd 100644 --- a/twilight-model/src/channel/message/component/mod.rs +++ b/twilight-model/src/channel/message/component/mod.rs @@ -45,7 +45,7 @@ use std::fmt::{Formatter, Result as FmtResult}; /// disabled: false, /// emoji: None, /// label: Some("Click me!".to_owned()), -/// style: ButtonStyle::Primary, +/// style: ButtonStyle::PRIMARY, /// url: None, /// })]), /// }); @@ -118,7 +118,7 @@ pub enum Component { /// Pop-up item that renders on modals. TextInput(TextInput), /// Variant value is unknown to the library. - Unknown(u8), + Unknown(ComponentType), } impl Component { @@ -134,19 +134,19 @@ impl Component { /// disabled: false, /// emoji: None, /// label: Some("ping".to_owned()), - /// style: ButtonStyle::Primary, + /// style: ButtonStyle::PRIMARY, /// url: None, /// }); /// - /// assert_eq!(ComponentType::Button, component.kind()); + /// assert_eq!(ComponentType::BUTTON, component.kind()); /// ``` pub const fn kind(&self) -> ComponentType { match self { - Self::ActionRow(_) => ComponentType::ActionRow, - Self::Button(_) => ComponentType::Button, - Self::SelectMenu(_) => ComponentType::SelectMenu, - Self::TextInput(_) => ComponentType::TextInput, - Component::Unknown(unknown) => ComponentType::Unknown(*unknown), + Self::ActionRow(_) => ComponentType::ACTION_ROW, + Self::Button(_) => ComponentType::BUTTON, + Self::SelectMenu(_) => ComponentType::SELECT_MENU, + Self::TextInput(_) => ComponentType::TEXT_INPUT, + Self::Unknown(component_type) => *component_type, } } } @@ -399,7 +399,7 @@ impl<'de> Visitor<'de> for ComponentVisitor { Ok(match kind { // Required fields: // - components - ComponentType::ActionRow => { + ComponentType::ACTION_ROW => { let components = components.ok_or_else(|| DeError::missing_field("components"))?; Self::Value::ActionRow(ActionRow { components }) @@ -413,7 +413,7 @@ impl<'de> Visitor<'de> for ComponentVisitor { // - emoji // - label // - url - ComponentType::Button => { + ComponentType::BUTTON => { let style = style .ok_or_else(|| DeError::missing_field("style"))? .deserialize_into() @@ -443,7 +443,7 @@ impl<'de> Visitor<'de> for ComponentVisitor { // - max_values // - min_values // - placeholder - ComponentType::SelectMenu => { + ComponentType::SELECT_MENU => { let custom_id = custom_id .flatten() .ok_or_else(|| DeError::missing_field("custom_id"))? @@ -472,7 +472,7 @@ impl<'de> Visitor<'de> for ComponentVisitor { // - placeholder // - required // - value - ComponentType::TextInput => { + ComponentType::TEXT_INPUT => { let custom_id = custom_id .flatten() .ok_or_else(|| DeError::missing_field("custom_id"))? @@ -499,7 +499,7 @@ impl<'de> Visitor<'de> for ComponentVisitor { value: value.unwrap_or_default(), }) } - ComponentType::Unknown(unknown) => Self::Value::Unknown(unknown), + other => Self::Value::Unknown(other), }) } } @@ -572,12 +572,12 @@ impl Serialize for Component { match self { Component::ActionRow(action_row) => { - state.serialize_field("type", &ComponentType::ActionRow)?; + state.serialize_field("type", &ComponentType::ACTION_ROW)?; state.serialize_field("components", &action_row.components)?; } Component::Button(button) => { - state.serialize_field("type", &ComponentType::Button)?; + state.serialize_field("type", &ComponentType::BUTTON)?; if button.custom_id.is_some() { state.serialize_field("custom_id", &button.custom_id)?; @@ -602,7 +602,7 @@ impl Serialize for Component { } } Component::SelectMenu(select_menu) => { - state.serialize_field("type", &ComponentType::SelectMenu)?; + state.serialize_field("type", &ComponentType::SELECT_MENU)?; // Due to `custom_id` being required in some variants and // optional in others, serialize as an Option. @@ -625,7 +625,7 @@ impl Serialize for Component { } } Component::TextInput(text_input) => { - state.serialize_field("type", &ComponentType::TextInput)?; + state.serialize_field("type", &ComponentType::TEXT_INPUT)?; // Due to `custom_id` and `label` being required in some // variants and optional in others, serialize as an Option. @@ -658,7 +658,7 @@ impl Serialize for Component { // deserialize. But it is all that can be done to avoid losing // incoming messages at this time. Component::Unknown(unknown) => { - state.serialize_field("type", &ComponentType::Unknown(*unknown))?; + state.serialize_field("type", unknown)?; } } @@ -692,7 +692,7 @@ mod tests { disabled: true, emoji: None, label: Some("test label".into()), - style: ButtonStyle::Primary, + style: ButtonStyle::PRIMARY, url: None, }), Component::SelectMenu(SelectMenu { @@ -720,7 +720,10 @@ mod tests { len: 2, }, Token::Str("type"), - Token::U8(ComponentType::ActionRow.into()), + Token::NewtypeStruct { + name: "ComponentType", + }, + Token::U8(ComponentType::ACTION_ROW.get()), Token::Str("components"), Token::Seq { len: Some(2) }, Token::Struct { @@ -728,7 +731,10 @@ mod tests { len: 5, }, Token::Str("type"), - Token::U8(ComponentType::Button.into()), + Token::NewtypeStruct { + name: "ComponentType", + }, + Token::U8(ComponentType::BUTTON.get()), Token::Str("custom_id"), Token::Some, Token::Str("test custom id"), @@ -738,14 +744,20 @@ mod tests { Token::Some, Token::Str("test label"), Token::Str("style"), - Token::U8(ButtonStyle::Primary.into()), + Token::NewtypeStruct { + name: "ButtonStyle", + }, + Token::U8(ButtonStyle::PRIMARY.get()), Token::StructEnd, Token::Struct { name: "Component", len: 6, }, Token::Str("type"), - Token::U8(ComponentType::SelectMenu.into()), + Token::NewtypeStruct { + name: "ComponentType", + }, + Token::U8(ComponentType::SELECT_MENU.get()), Token::Str("custom_id"), Token::Some, Token::Str("test custom id 2"), @@ -791,7 +803,7 @@ mod tests { custom_id: Some("button-1".to_owned()), disabled: false, emoji: None, - style: ButtonStyle::Primary, + style: ButtonStyle::PRIMARY, label: Some("Button".to_owned()), url: None, })]), @@ -805,7 +817,10 @@ mod tests { len: 2, }, Token::String("type"), - Token::U8(ComponentType::ActionRow.into()), + Token::NewtypeStruct { + name: "ComponentType", + }, + Token::U8(ComponentType::ACTION_ROW.get()), Token::String("components"), Token::Seq { len: Some(1) }, Token::Struct { @@ -813,6 +828,9 @@ mod tests { len: 4, }, Token::String("type"), + Token::NewtypeStruct { + name: "ComponentType", + }, Token::U8(2), Token::String("custom_id"), Token::Some, @@ -821,6 +839,9 @@ mod tests { Token::Some, Token::String("Button"), Token::String("style"), + Token::NewtypeStruct { + name: "ButtonStyle", + }, Token::U8(1), Token::StructEnd, Token::SeqEnd, @@ -843,7 +864,7 @@ mod tests { name: FLAG.to_owned(), }), label: Some("Test".to_owned()), - style: ButtonStyle::Link, + style: ButtonStyle::LINK, url: Some("https://twilight.rs".to_owned()), }); @@ -855,7 +876,10 @@ mod tests { len: 6, }, Token::String("type"), - Token::U8(ComponentType::Button.into()), + Token::NewtypeStruct { + name: "ComponentType", + }, + Token::U8(ComponentType::BUTTON.get()), Token::String("custom_id"), Token::Some, Token::String("test"), @@ -872,7 +896,10 @@ mod tests { Token::Some, Token::String("Test"), Token::String("style"), - Token::U8(ButtonStyle::Link.into()), + Token::NewtypeStruct { + name: "ButtonStyle", + }, + Token::U8(ButtonStyle::LINK.get()), Token::String("url"), Token::Some, Token::String("https://twilight.rs"), @@ -890,7 +917,7 @@ mod tests { min_length: Some(1), placeholder: Some("Taking this place".to_owned()), required: Some(true), - style: TextInputStyle::Short, + style: TextInputStyle::SHORT, value: Some("Hello World!".to_owned()), }); @@ -902,7 +929,10 @@ mod tests { len: 9, }, Token::String("type"), - Token::U8(ComponentType::TextInput.into()), + Token::NewtypeStruct { + name: "ComponentType", + }, + Token::U8(ComponentType::TEXT_INPUT.get()), Token::String("custom_id"), Token::Some, Token::String("test"), @@ -922,7 +952,10 @@ mod tests { Token::Some, Token::Bool(true), Token::String("style"), - Token::U8(TextInputStyle::Short as u8), + Token::NewtypeStruct { + name: "TextInputStyle", + }, + Token::U8(TextInputStyle::SHORT.get()), Token::String("value"), Token::Some, Token::String("Hello World!"), diff --git a/twilight-model/src/channel/message/component/text_input.rs b/twilight-model/src/channel/message/component/text_input.rs index d8a7fcfc862..94aea992207 100644 --- a/twilight-model/src/channel/message/component/text_input.rs +++ b/twilight-model/src/channel/message/component/text_input.rs @@ -1,4 +1,4 @@ -use serde_repr::{Deserialize_repr, Serialize_repr}; +use serde::{Deserialize, Serialize}; /// Pop-up [`Component`] that renders on modals. /// @@ -28,22 +28,36 @@ pub struct TextInput { } /// Style of an [`TextInput`]. -#[derive(Clone, Copy, Debug, Deserialize_repr, Eq, Hash, PartialEq, Serialize_repr)] -#[non_exhaustive] -#[repr(u8)] -pub enum TextInputStyle { +#[derive(Clone, Copy, Deserialize, Eq, Hash, PartialEq, Serialize)] +pub struct TextInputStyle(u8); + +impl TextInputStyle { /// Intended for short single-line text. - Short = 1, + pub const SHORT: Self = Self::new(1); + /// Intended for much longer inputs. - Paragraph = 2, + pub const PARAGRAPH: Self = Self::new(2); + + /// Name of the associated constant. + /// + /// Returns `None` if the value doesn't have a defined constant. + pub const fn name(self) -> Option<&'static str> { + Some(match self { + Self::PARAGRAPH => "PARAGRAPH", + Self::SHORT => "SHORT", + _ => return None, + }) + } } +impl_typed!(TextInputStyle, u8); + #[cfg(test)] mod tests { use super::*; use serde::{Deserialize, Serialize}; use serde_test::Token; - use static_assertions::{assert_fields, assert_impl_all, const_assert_eq}; + use static_assertions::{assert_fields, assert_impl_all}; use std::{fmt::Debug, hash::Hash}; assert_fields!( @@ -69,12 +83,26 @@ mod tests { Serialize, Sync ); - const_assert_eq!(1, TextInputStyle::Short as u8); - const_assert_eq!(2, TextInputStyle::Paragraph as u8); #[test] fn text_input_style() { - serde_test::assert_tokens(&TextInputStyle::Short, &[Token::U8(1)]); - serde_test::assert_tokens(&TextInputStyle::Paragraph, &[Token::U8(2)]); + serde_test::assert_tokens( + &TextInputStyle::SHORT, + &[ + Token::NewtypeStruct { + name: "TextInputStyle", + }, + Token::U8(1), + ], + ); + serde_test::assert_tokens( + &TextInputStyle::PARAGRAPH, + &[ + Token::NewtypeStruct { + name: "TextInputStyle", + }, + Token::U8(2), + ], + ); } } diff --git a/twilight-model/src/channel/message/interaction.rs b/twilight-model/src/channel/message/interaction.rs index c7292dbfdc3..af9a24ad584 100644 --- a/twilight-model/src/channel/message/interaction.rs +++ b/twilight-model/src/channel/message/interaction.rs @@ -45,7 +45,7 @@ mod tests { let value = MessageInteraction { id: Id::new(1), - kind: InteractionType::ApplicationCommand, + kind: InteractionType::APPLICATION_COMMAND, member: Some(PartialMember { avatar: None, communication_disabled_until: None, @@ -72,7 +72,7 @@ mod tests { locale: Some("en-us".to_owned()), mfa_enabled: Some(true), name: "test".to_owned(), - premium_type: Some(PremiumType::Nitro), + premium_type: Some(PremiumType::NITRO), public_flags: Some( UserFlags::PREMIUM_EARLY_SUPPORTER | UserFlags::VERIFIED_DEVELOPER, ), @@ -92,7 +92,10 @@ mod tests { Token::NewtypeStruct { name: "Id" }, Token::Str("1"), Token::Str("type"), - Token::U8(InteractionType::ApplicationCommand as u8), + Token::NewtypeStruct { + name: "InteractionType", + }, + Token::U8(InteractionType::APPLICATION_COMMAND.get()), Token::Str("member"), Token::Some, Token::Struct { @@ -158,6 +161,9 @@ mod tests { Token::Str("test"), Token::Str("premium_type"), Token::Some, + Token::NewtypeStruct { + name: "PremiumType", + }, Token::U8(2), Token::Str("public_flags"), Token::Some, diff --git a/twilight-model/src/channel/message/kind.rs b/twilight-model/src/channel/message/kind.rs index 84cad3cf1c7..d1c64e7ee17 100644 --- a/twilight-model/src/channel/message/kind.rs +++ b/twilight-model/src/channel/message/kind.rs @@ -8,70 +8,137 @@ use serde::{Deserialize, Serialize}; /// [Discord Docs/Message Types]: https://discord.com/developers/docs/resources/channel#message-object-message-types /// [`Message`]: super::Message #[allow(missing_docs)] -#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)] -#[non_exhaustive] -#[serde(from = "u8", into = "u8")] -pub enum MessageType { +#[derive(Clone, Copy, Deserialize, Eq, Hash, PartialEq, Serialize)] +pub struct MessageType(u8); + +impl MessageType { /// Regular message. - Regular, + pub const REGULAR: Self = Self::new(0); + /// System message denoting a recipient has been added to a group. - RecipientAdd, + pub const RECIPIENT_ADD: Self = Self::new(1); + /// System message denoting a recipient has been removed from a group. - RecipientRemove, + pub const RECIPIENT_REMOVE: Self = Self::new(2); + /// System message denoting a call state, e.g. missed, started. - Call, + pub const CALL: Self = Self::new(3); + /// System message denoting a channel's name has been changed. - ChannelNameChange, + pub const CHANNEL_NAME_CHANGE: Self = Self::new(4); + /// System message denoting a channel's icon has been changed. - ChannelIconChange, + pub const CHANNEL_ICON_CHANGE: Self = Self::new(5); + /// System message denoting a message has been pinned. - ChannelMessagePinned, + pub const CHANNEL_MESSAGE_PINNED: Self = Self::new(6); + /// System message denoting a member has joined a guild. - UserJoin, + pub const USER_JOIN: Self = Self::new(7); + /// System message denoting a user nitro boosted a guild. - GuildBoost, + pub const GUILD_BOOST: Self = Self::new(8); + /// System message denoting a user nitro boosted a guild to level 1. - GuildBoostTier1, + pub const GUILD_BOOST_TIER1: Self = Self::new(9); + /// System message denoting a user nitro boosted a guild to level 2. - GuildBoostTier2, + pub const GUILD_BOOST_TIER2: Self = Self::new(10); + /// System message denoting a user nitro boosted a guild to level 3. - GuildBoostTier3, + pub const GUILD_BOOST_TIER3: Self = Self::new(11); + /// System message denoting a channel has been followed. - ChannelFollowAdd, + pub const CHANNEL_FOLLOW_ADD: Self = Self::new(12); + /// System message denoting a guild has been disqualified for Server Discovery. - GuildDiscoveryDisqualified, + pub const GUILD_DISCOVERY_DISQUALIFIED: Self = Self::new(14); + /// System message denoting a guild has been redisqualified for Server Discovery. - GuildDiscoveryRequalified, + pub const GUILD_DISCOVERY_REQUALIFIED: Self = Self::new(15); + /// System message denoting an initial warning for Server Discovery disqualification. - GuildDiscoveryGracePeriodInitialWarning, + pub const GUILD_DISCOVERY_GRACE_PERIOD_INITIAL_WARNING: Self = Self::new(16); + /// System message denoting a final warning for Server Discovery disqualification. - GuildDiscoveryGracePeriodFinalWarning, - ThreadCreated, + pub const GUILD_DISCOVERY_GRACE_PERIOD_FINAL_WARNING: Self = Self::new(17); + + /// Message is about a new thread. + pub const THREAD_CREATED: Self = Self::new(18); + /// Message is an inline reply. - Reply, + pub const REPLY: Self = Self::new(19); + /// Message is a chat input command. - ChatInputCommand, - ThreadStarterMessage, - GuildInviteReminder, - ContextMenuCommand, + pub const CHAT_INPUT_COMMAND: Self = Self::new(20); + + /// Message is the starter for a thread. + pub const THREAD_STARTER_MESSAGE: Self = Self::new(21); + + /// Message is a reminder for a scheduled event. + pub const GUILD_INVITE_REMINDER: Self = Self::new(22); + + /// Message is a context menu command use. + pub const CONTEXT_MENU_COMMAND: Self = Self::new(23); + /// Message is an auto moderation action. - AutoModerationAction, + pub const AUTO_MODERATION_ACTION: Self = Self::new(24); + /// System message denoting a user subscribed to a role. - RoleSubscriptionPurchase, + pub const ROLE_SUBSCRIPTION_PURCHASE: Self = Self::new(25); + /// System message denoting a interaction premium upsell. - InteractionPremiumUpsell, + pub const INTERACTION_PREMIUM_UPSELL: Self = Self::new(26); + /// System message denoting a guild application premium subscription. - GuildApplicationPremiumSubscription, - /// Variant value is unknown to the library. - Unknown(u8), -} + pub const GUILD_APPLICATION_PREMIUM_SUBSCRIPTION: Self = Self::new(32); + + /// Name of the associated constant. + /// + /// Returns `None` if the value doesn't have a defined constant. + pub const fn name(self) -> Option<&'static str> { + Some(match self { + Self::REGULAR => "REGULAR", + Self::RECIPIENT_ADD => "RECIPIENT_ADD", + Self::RECIPIENT_REMOVE => "RECIPIENT_REMOVE", + Self::CALL => "CALL", + Self::CHANNEL_NAME_CHANGE => "CHANNEL_NAME_CHANGE", + Self::CHANNEL_ICON_CHANGE => "CHANNEL_ICON_CHANGE", + Self::CHANNEL_MESSAGE_PINNED => "CHANNEL_MESSAGE_PINNED", + Self::USER_JOIN => "USER_JOIN", + Self::GUILD_BOOST => "GUILD_BOOST", + Self::GUILD_BOOST_TIER1 => "GUILD_BOOST_TIER1", + Self::GUILD_BOOST_TIER2 => "GUILD_BOOST_TIER2", + Self::GUILD_BOOST_TIER3 => "GUILD_BOOST_TIER3", + Self::CHANNEL_FOLLOW_ADD => "CHANNEL_FOLLOW_ADD", + Self::GUILD_DISCOVERY_DISQUALIFIED => "GUILD_DISCOVERY_DISQUALIFIED", + Self::GUILD_DISCOVERY_REQUALIFIED => "GUILD_DISCOVERY_REQUALIFIED", + Self::GUILD_DISCOVERY_GRACE_PERIOD_INITIAL_WARNING => { + "GUILD_DISCOVERY_GRACE_PERIOD_INITIAL_WARNING" + } + Self::GUILD_DISCOVERY_GRACE_PERIOD_FINAL_WARNING => { + "GUILD_DISCOVERY_GRACE_PERIOD_FINAL_WARNING" + } + Self::THREAD_CREATED => "THREAD_CREATED", + Self::REPLY => "REPLY", + Self::CHAT_INPUT_COMMAND => "CHAT_INPUT_COMMAND", + Self::THREAD_STARTER_MESSAGE => "THREAD_STARTER_MESSAGE", + Self::GUILD_INVITE_REMINDER => "GUILD_INVITE_REMINDER", + Self::CONTEXT_MENU_COMMAND => "CONTEXT_MENU_COMMAND", + Self::AUTO_MODERATION_ACTION => "AUTO_MODERATION_ACTION", + Self::INTERACTION_PREMIUM_UPSELL => "INTERACTION_PREMIUM_UPSELL", + Self::GUILD_APPLICATION_PREMIUM_SUBSCRIPTION => { + "GUILD_APPLICATION_PREMIUM_SUBSCRIPTION" + } + _ => return None, + }) + } -impl MessageType { /// Whether the message can be deleted, not taking permissions into account. /// Some message types can't be deleted, even by server administrators. /// /// Some message types can only be deleted with certain permissions. For - /// example, [`AutoModerationAction`][`Self::AutoModerationAction`] can only + /// example, [`AUTO_MODERATION_ACTION`][`Self::AUTO_MODERATION_ACTION`] can only /// be deleted if the user has the /// [Manage Messages] permission. /// @@ -83,22 +150,22 @@ impl MessageType { pub const fn deletable(self) -> bool { matches!( self, - Self::Regular - | Self::ChannelMessagePinned - | Self::UserJoin - | Self::GuildBoost - | Self::GuildBoostTier1 - | Self::GuildBoostTier2 - | Self::GuildBoostTier3 - | Self::ChannelFollowAdd - | Self::ThreadCreated - | Self::Reply - | Self::ChatInputCommand - | Self::GuildInviteReminder - | Self::ContextMenuCommand - | Self::AutoModerationAction - | Self::RoleSubscriptionPurchase - | Self::InteractionPremiumUpsell + Self::REGULAR + | Self::CHANNEL_MESSAGE_PINNED + | Self::USER_JOIN + | Self::GUILD_BOOST + | Self::GUILD_BOOST_TIER1 + | Self::GUILD_BOOST_TIER2 + | Self::GUILD_BOOST_TIER3 + | Self::CHANNEL_FOLLOW_ADD + | Self::THREAD_CREATED + | Self::REPLY + | Self::CHAT_INPUT_COMMAND + | Self::GUILD_INVITE_REMINDER + | Self::CONTEXT_MENU_COMMAND + | Self::AUTO_MODERATION_ACTION + | Self::ROLE_SUBSCRIPTION_PURCHASE + | Self::INTERACTION_PREMIUM_UPSELL ) } @@ -106,7 +173,7 @@ impl MessageType { /// Some message types can't be deleted, even by server administrators. /// /// Some message types can only be deleted with certain permissions. For - /// example, [`AutoModerationAction`][`Self::AutoModerationAction`] can only + /// example, [`AUTO_MODERATION_ACTION`][`Self::AUTO_MODERATION_ACTION`] can only /// be deleted if the user has the [Manage Messages] permission. /// /// To check whether a message can be deleted *without* taking permissions @@ -115,7 +182,7 @@ impl MessageType { /// [Manage Messages]: Permissions::MANAGE_MESSAGES pub const fn deletable_with_permissions(self, permissions: Permissions) -> bool { let required_permissions = match self { - Self::AutoModerationAction => Permissions::MANAGE_MESSAGES, + Self::AUTO_MODERATION_ACTION => Permissions::MANAGE_MESSAGES, _ => Permissions::empty(), }; @@ -127,75 +194,7 @@ impl MessageType { } } -impl From for MessageType { - fn from(value: u8) -> Self { - match value { - 0 => Self::Regular, - 1 => Self::RecipientAdd, - 2 => Self::RecipientRemove, - 3 => Self::Call, - 4 => Self::ChannelNameChange, - 5 => Self::ChannelIconChange, - 6 => Self::ChannelMessagePinned, - 7 => Self::UserJoin, - 8 => Self::GuildBoost, - 9 => Self::GuildBoostTier1, - 10 => Self::GuildBoostTier2, - 11 => Self::GuildBoostTier3, - 12 => Self::ChannelFollowAdd, - 14 => Self::GuildDiscoveryDisqualified, - 15 => Self::GuildDiscoveryRequalified, - 16 => Self::GuildDiscoveryGracePeriodInitialWarning, - 17 => Self::GuildDiscoveryGracePeriodFinalWarning, - 18 => Self::ThreadCreated, - 19 => Self::Reply, - 20 => Self::ChatInputCommand, - 21 => Self::ThreadStarterMessage, - 22 => Self::GuildInviteReminder, - 23 => Self::ContextMenuCommand, - 24 => Self::AutoModerationAction, - 25 => Self::RoleSubscriptionPurchase, - 26 => Self::InteractionPremiumUpsell, - 32 => Self::GuildApplicationPremiumSubscription, - unknown => Self::Unknown(unknown), - } - } -} - -impl From for u8 { - fn from(value: MessageType) -> Self { - match value { - MessageType::Regular => 0, - MessageType::RecipientAdd => 1, - MessageType::RecipientRemove => 2, - MessageType::Call => 3, - MessageType::ChannelNameChange => 4, - MessageType::ChannelIconChange => 5, - MessageType::ChannelMessagePinned => 6, - MessageType::UserJoin => 7, - MessageType::GuildBoost => 8, - MessageType::GuildBoostTier1 => 9, - MessageType::GuildBoostTier2 => 10, - MessageType::GuildBoostTier3 => 11, - MessageType::ChannelFollowAdd => 12, - MessageType::GuildDiscoveryDisqualified => 14, - MessageType::GuildDiscoveryRequalified => 15, - MessageType::GuildDiscoveryGracePeriodInitialWarning => 16, - MessageType::GuildDiscoveryGracePeriodFinalWarning => 17, - MessageType::ThreadCreated => 18, - MessageType::Reply => 19, - MessageType::ChatInputCommand => 20, - MessageType::ThreadStarterMessage => 21, - MessageType::GuildInviteReminder => 22, - MessageType::ContextMenuCommand => 23, - MessageType::AutoModerationAction => 24, - MessageType::RoleSubscriptionPurchase => 25, - MessageType::InteractionPremiumUpsell => 26, - MessageType::GuildApplicationPremiumSubscription => 32, - MessageType::Unknown(unknown) => unknown, - } - } -} +impl_typed!(MessageType, u8); #[cfg(test)] mod tests { @@ -219,58 +218,73 @@ mod tests { Sync ); + #[allow(clippy::too_many_lines)] #[test] fn message_type() { const MAP: &[(MessageType, u8, bool)] = &[ - (MessageType::Regular, 0, true), - (MessageType::RecipientAdd, 1, false), - (MessageType::RecipientRemove, 2, false), - (MessageType::Call, 3, false), - (MessageType::ChannelNameChange, 4, false), - (MessageType::ChannelIconChange, 5, false), - (MessageType::ChannelMessagePinned, 6, true), - (MessageType::UserJoin, 7, true), - (MessageType::GuildBoost, 8, true), - (MessageType::GuildBoostTier1, 9, true), - (MessageType::GuildBoostTier2, 10, true), - (MessageType::GuildBoostTier3, 11, true), - (MessageType::ChannelFollowAdd, 12, true), - (MessageType::GuildDiscoveryDisqualified, 14, false), - (MessageType::GuildDiscoveryRequalified, 15, false), + (MessageType::REGULAR, 0, true), + (MessageType::RECIPIENT_ADD, 1, false), + (MessageType::RECIPIENT_REMOVE, 2, false), + (MessageType::CALL, 3, false), + (MessageType::CHANNEL_NAME_CHANGE, 4, false), + (MessageType::CHANNEL_ICON_CHANGE, 5, false), + (MessageType::CHANNEL_MESSAGE_PINNED, 6, true), + (MessageType::USER_JOIN, 7, true), + (MessageType::GUILD_BOOST, 8, true), + (MessageType::GUILD_BOOST_TIER1, 9, true), + (MessageType::GUILD_BOOST_TIER2, 10, true), + (MessageType::GUILD_BOOST_TIER3, 11, true), + (MessageType::CHANNEL_FOLLOW_ADD, 12, true), + (MessageType::GUILD_DISCOVERY_DISQUALIFIED, 14, false), + (MessageType::GUILD_DISCOVERY_REQUALIFIED, 15, false), ( - MessageType::GuildDiscoveryGracePeriodInitialWarning, + MessageType::GUILD_DISCOVERY_GRACE_PERIOD_INITIAL_WARNING, 16, false, ), ( - MessageType::GuildDiscoveryGracePeriodFinalWarning, + MessageType::GUILD_DISCOVERY_GRACE_PERIOD_FINAL_WARNING, 17, false, ), - (MessageType::ThreadCreated, 18, true), - (MessageType::Reply, 19, true), - (MessageType::ChatInputCommand, 20, true), - (MessageType::ThreadStarterMessage, 21, false), - (MessageType::GuildInviteReminder, 22, true), - (MessageType::ContextMenuCommand, 23, true), - (MessageType::AutoModerationAction, 24, true), - (MessageType::RoleSubscriptionPurchase, 25, true), - (MessageType::InteractionPremiumUpsell, 26, true), - (MessageType::GuildApplicationPremiumSubscription, 32, false), + (MessageType::THREAD_CREATED, 18, true), + (MessageType::REPLY, 19, true), + (MessageType::CHAT_INPUT_COMMAND, 20, true), + (MessageType::THREAD_STARTER_MESSAGE, 21, false), + (MessageType::GUILD_INVITE_REMINDER, 22, true), + (MessageType::CONTEXT_MENU_COMMAND, 23, true), + (MessageType::AUTO_MODERATION_ACTION, 24, true), + (MessageType::ROLE_SUBSCRIPTION_PURCHASE, 25, true), + (MessageType::INTERACTION_PREMIUM_UPSELL, 26, true), + ( + MessageType::GUILD_APPLICATION_PREMIUM_SUBSCRIPTION, + 32, + false, + ), ]; for (message_type, number, deletable) in MAP { assert_eq!(*message_type, MessageType::from(*number)); assert_eq!(*number, u8::from(*message_type)); assert_eq!(*deletable, message_type.deletable()); - serde_test::assert_tokens(message_type, &[Token::U8(*number)]); + serde_test::assert_tokens( + message_type, + &[ + Token::NewtypeStruct { + name: "MessageType", + }, + Token::U8(*number), + ], + ); } } #[test] fn deletable_with_permissions() { - assert!(MessageType::AutoModerationAction + assert!(MessageType::AUTO_MODERATION_ACTION .deletable_with_permissions(Permissions::MANAGE_MESSAGES)); - assert!(!MessageType::AutoModerationAction.deletable_with_permissions(Permissions::empty())); + assert!( + !MessageType::AUTO_MODERATION_ACTION.deletable_with_permissions(Permissions::empty()) + ); } } diff --git a/twilight-model/src/channel/message/mod.rs b/twilight-model/src/channel/message/mod.rs index ab8054cd6dc..ad11c24b5a5 100644 --- a/twilight-model/src/channel/message/mod.rs +++ b/twilight-model/src/channel/message/mod.rs @@ -249,7 +249,7 @@ mod tests { guild_id: Some(Id::new(1)), id: Id::new(4), interaction: None, - kind: MessageType::Regular, + kind: MessageType::REGULAR, member: Some(PartialMember { avatar: None, communication_disabled_until: None, @@ -272,7 +272,7 @@ mod tests { reference: None, role_subscription_data: None, sticker_items: vec![MessageSticker { - format_type: StickerFormatType::Png, + format_type: StickerFormatType::PNG, id: Id::new(1), name: "sticker name".to_owned(), }], @@ -336,6 +336,9 @@ mod tests { Token::NewtypeStruct { name: "Id" }, Token::Str("4"), Token::Str("type"), + Token::NewtypeStruct { + name: "MessageType", + }, Token::U8(0), Token::Str("member"), Token::Some, @@ -379,6 +382,9 @@ mod tests { len: 3, }, Token::Str("format_type"), + Token::NewtypeStruct { + name: "StickerFormatType", + }, Token::U8(1), Token::Str("id"), Token::NewtypeStruct { name: "Id" }, @@ -406,7 +412,7 @@ mod tests { let value = Message { activity: Some(MessageActivity { - kind: MessageActivityType::Join, + kind: MessageActivityType::JOIN, party_id: None, }), application: Some(MessageApplication { @@ -444,7 +450,7 @@ mod tests { guild_id: Some(Id::new(1)), id: Id::new(4), interaction: None, - kind: MessageType::Regular, + kind: MessageType::REGULAR, member: Some(PartialMember { avatar: None, communication_disabled_until: None, @@ -461,7 +467,7 @@ mod tests { mention_channels: vec![ChannelMention { guild_id: Id::new(1), id: Id::new(2), - kind: ChannelType::GuildText, + kind: ChannelType::GUILD_TEXT, name: "channel".to_owned(), }], mention_everyone: false, @@ -483,7 +489,7 @@ mod tests { }), role_subscription_data: None, sticker_items: vec![MessageSticker { - format_type: StickerFormatType::Png, + format_type: StickerFormatType::PNG, id: Id::new(1), name: "sticker name".to_owned(), }], @@ -508,6 +514,9 @@ mod tests { len: 1, }, Token::Str("type"), + Token::NewtypeStruct { + name: "MessageActivityType", + }, Token::U8(1), Token::StructEnd, Token::Str("application"), @@ -581,6 +590,9 @@ mod tests { Token::NewtypeStruct { name: "Id" }, Token::Str("4"), Token::Str("type"), + Token::NewtypeStruct { + name: "MessageType", + }, Token::U8(0), Token::Str("member"), Token::Some, @@ -620,6 +632,9 @@ mod tests { Token::NewtypeStruct { name: "Id" }, Token::Str("2"), Token::Str("type"), + Token::NewtypeStruct { + name: "ChannelType", + }, Token::U8(0), Token::Str("name"), Token::Str("channel"), @@ -673,6 +688,9 @@ mod tests { len: 3, }, Token::Str("format_type"), + Token::NewtypeStruct { + name: "StickerFormatType", + }, Token::U8(1), Token::Str("id"), Token::NewtypeStruct { name: "Id" }, diff --git a/twilight-model/src/channel/message/sticker/format_type.rs b/twilight-model/src/channel/message/sticker/format_type.rs index f9c6eb20538..0cedfa27552 100644 --- a/twilight-model/src/channel/message/sticker/format_type.rs +++ b/twilight-model/src/channel/message/sticker/format_type.rs @@ -3,66 +3,63 @@ use serde::{Deserialize, Serialize}; /// Format type of a [`Sticker`]. /// /// [`Sticker`]: super::Sticker -#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)] -#[non_exhaustive] -#[serde(from = "u8", into = "u8")] -pub enum StickerFormatType { +#[derive(Clone, Copy, Deserialize, Eq, Hash, PartialEq, Serialize)] +pub struct StickerFormatType(u8); + +impl StickerFormatType { /// Sticker format is a PNG. - Png, + pub const PNG: Self = Self::new(1); + /// Sticker format is an APNG. - Apng, + pub const APNG: Self = Self::new(2); + /// Sticker format is a LOTTIE. - Lottie, + pub const LOTTIE: Self = Self::new(3); + /// Sticker format is a GIF. - Gif, - /// Variant value is unknown to the library. - Unknown(u8), -} + pub const GIF: Self = Self::new(4); -impl From for StickerFormatType { - fn from(value: u8) -> Self { - match value { - 1 => StickerFormatType::Png, - 2 => StickerFormatType::Apng, - 3 => StickerFormatType::Lottie, - 4 => StickerFormatType::Gif, - unknown => StickerFormatType::Unknown(unknown), - } + /// Name of the associated constant. + /// + /// Returns `None` if the value doesn't have a defined constant. + pub const fn name(self) -> Option<&'static str> { + Some(match self { + Self::APNG => "APNG", + Self::LOTTIE => "LOTTIE", + Self::PNG => "PNG", + _ => return None, + }) } } -impl From for u8 { - fn from(value: StickerFormatType) -> Self { - match value { - StickerFormatType::Png => 1, - StickerFormatType::Apng => 2, - StickerFormatType::Lottie => 3, - StickerFormatType::Gif => 4, - StickerFormatType::Unknown(unknown) => unknown, - } - } -} +impl_typed!(StickerFormatType, u8); #[cfg(test)] mod tests { use super::StickerFormatType; use serde_test::Token; - #[test] - fn variants() { - serde_test::assert_tokens(&StickerFormatType::Png, &[Token::U8(1)]); - serde_test::assert_tokens(&StickerFormatType::Apng, &[Token::U8(2)]); - serde_test::assert_tokens(&StickerFormatType::Lottie, &[Token::U8(3)]); - serde_test::assert_tokens(&StickerFormatType::Gif, &[Token::U8(4)]); - serde_test::assert_tokens(&StickerFormatType::Unknown(99), &[Token::U8(99)]); - } + const MAP: &[(StickerFormatType, u8)] = &[ + (StickerFormatType::PNG, 1), + (StickerFormatType::APNG, 2), + (StickerFormatType::LOTTIE, 3), + (StickerFormatType::GIF, 4), + ]; #[test] - fn conversions() { - assert_eq!(StickerFormatType::from(1), StickerFormatType::Png); - assert_eq!(StickerFormatType::from(2), StickerFormatType::Apng); - assert_eq!(StickerFormatType::from(3), StickerFormatType::Lottie); - assert_eq!(StickerFormatType::from(4), StickerFormatType::Gif); - assert_eq!(StickerFormatType::from(99), StickerFormatType::Unknown(99)); + fn variants() { + for (kind, num) in MAP { + serde_test::assert_tokens( + kind, + &[ + Token::NewtypeStruct { + name: "StickerFormatType", + }, + Token::U8(*num), + ], + ); + assert_eq!(*kind, StickerFormatType::from(*num)); + assert_eq!(*num, kind.get()); + } } } diff --git a/twilight-model/src/channel/message/sticker/kind.rs b/twilight-model/src/channel/message/sticker/kind.rs index b8334aa4775..a90125786b6 100644 --- a/twilight-model/src/channel/message/sticker/kind.rs +++ b/twilight-model/src/channel/message/sticker/kind.rs @@ -3,39 +3,31 @@ use serde::{Deserialize, Serialize}; /// Type of a [`Sticker`]. /// /// [`Sticker`]: super::Sticker -#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)] -#[non_exhaustive] -#[serde(from = "u8", into = "u8")] -pub enum StickerType { +#[derive(Clone, Copy, Deserialize, Eq, Hash, PartialEq, Serialize)] +pub struct StickerType(u8); + +impl StickerType { /// Official sticker in a pack. /// /// Part of nitro or in a removed purchasable pack. - Standard, + pub const STANDARD: Self = Self::new(1); + /// Sticker uploaded to a boosted guild for the guild's members. - Guild, - /// Variant value is unknown to the library. - Unknown(u8), -} + pub const GUILD: Self = Self::new(2); -impl From for StickerType { - fn from(value: u8) -> Self { - match value { - 1 => StickerType::Standard, - 2 => StickerType::Guild, - unknown => StickerType::Unknown(unknown), - } + /// Name of the associated constant. + /// + /// Returns `None` if the value doesn't have a defined constant. + pub const fn name(self) -> Option<&'static str> { + Some(match self { + Self::STANDARD => "STANDARD", + Self::GUILD => "GUILD", + _ => return None, + }) } } -impl From for u8 { - fn from(value: StickerType) -> Self { - match value { - StickerType::Standard => 1, - StickerType::Guild => 2, - StickerType::Unknown(unknown) => unknown, - } - } -} +impl_typed!(StickerType, u8); #[cfg(test)] mod tests { @@ -44,15 +36,39 @@ mod tests { #[test] fn variants() { - serde_test::assert_tokens(&StickerType::Standard, &[Token::U8(1)]); - serde_test::assert_tokens(&StickerType::Guild, &[Token::U8(2)]); - serde_test::assert_tokens(&StickerType::Unknown(99), &[Token::U8(99)]); + serde_test::assert_tokens( + &StickerType::STANDARD, + &[ + Token::NewtypeStruct { + name: "StickerType", + }, + Token::U8(1), + ], + ); + serde_test::assert_tokens( + &StickerType::GUILD, + &[ + Token::NewtypeStruct { + name: "StickerType", + }, + Token::U8(2), + ], + ); + serde_test::assert_tokens( + &StickerType::new(99), + &[ + Token::NewtypeStruct { + name: "StickerType", + }, + Token::U8(99), + ], + ); } #[test] fn conversions() { - assert_eq!(StickerType::from(1), StickerType::Standard); - assert_eq!(StickerType::from(2), StickerType::Guild); - assert_eq!(StickerType::from(99), StickerType::Unknown(99)); + assert_eq!(StickerType::from(1), StickerType::STANDARD); + assert_eq!(StickerType::from(2), StickerType::GUILD); + assert_eq!(StickerType::from(99), StickerType::new(99)); } } diff --git a/twilight-model/src/channel/message/sticker/message.rs b/twilight-model/src/channel/message/sticker/message.rs index dc2df19793f..f04f575a53e 100644 --- a/twilight-model/src/channel/message/sticker/message.rs +++ b/twilight-model/src/channel/message/sticker/message.rs @@ -41,7 +41,7 @@ mod tests { #[test] fn full() { let value = MessageSticker { - format_type: StickerFormatType::Lottie, + format_type: StickerFormatType::LOTTIE, id: Id::new(1), name: "sticker".into(), }; @@ -54,7 +54,10 @@ mod tests { len: 3, }, Token::Str("format_type"), - Token::U8(StickerFormatType::Lottie.into()), + Token::NewtypeStruct { + name: "StickerFormatType", + }, + Token::U8(StickerFormatType::LOTTIE.get()), Token::Str("id"), Token::NewtypeStruct { name: "Id" }, Token::Str("1"), diff --git a/twilight-model/src/channel/message/sticker/mod.rs b/twilight-model/src/channel/message/sticker/mod.rs index fdd6da1b23e..af45b8cc3ef 100644 --- a/twilight-model/src/channel/message/sticker/mod.rs +++ b/twilight-model/src/channel/message/sticker/mod.rs @@ -100,10 +100,10 @@ mod tests { let value = Sticker { available: false, description: Some("foo2".to_owned()), - format_type: StickerFormatType::Png, + format_type: StickerFormatType::PNG, guild_id: None, id: Id::new(1), - kind: StickerType::Standard, + kind: StickerType::STANDARD, name: "sticker name".to_owned(), pack_id: None, sort_value: None, @@ -122,11 +122,17 @@ mod tests { Token::Some, Token::Str("foo2"), Token::Str("format_type"), - Token::U8(StickerFormatType::Png.into()), + Token::NewtypeStruct { + name: "StickerFormatType", + }, + Token::U8(StickerFormatType::PNG.get()), Token::Str("id"), Token::NewtypeStruct { name: "Id" }, Token::Str("1"), Token::Str("type"), + Token::NewtypeStruct { + name: "StickerType", + }, Token::U8(1), Token::Str("name"), Token::Str("sticker name"), @@ -143,10 +149,10 @@ mod tests { let value = Sticker { available: true, description: Some("sticker".into()), - format_type: StickerFormatType::Png, + format_type: StickerFormatType::PNG, guild_id: Some(Id::new(1)), id: Id::new(2), - kind: StickerType::Guild, + kind: StickerType::GUILD, name: "stick".into(), pack_id: Some(Id::new(3)), sort_value: Some(1), @@ -163,7 +169,7 @@ mod tests { locale: Some("en-us".to_owned()), mfa_enabled: Some(true), name: "test".to_owned(), - premium_type: Some(PremiumType::Nitro), + premium_type: Some(PremiumType::NITRO), public_flags: Some( UserFlags::PREMIUM_EARLY_SUPPORTER | UserFlags::VERIFIED_DEVELOPER, ), @@ -185,7 +191,10 @@ mod tests { Token::Some, Token::Str("sticker"), Token::Str("format_type"), - Token::U8(StickerFormatType::Png.into()), + Token::NewtypeStruct { + name: "StickerFormatType", + }, + Token::U8(StickerFormatType::PNG.get()), Token::Str("guild_id"), Token::Some, Token::NewtypeStruct { name: "Id" }, @@ -194,6 +203,9 @@ mod tests { Token::NewtypeStruct { name: "Id" }, Token::Str("2"), Token::Str("type"), + Token::NewtypeStruct { + name: "StickerType", + }, Token::U8(2), Token::Str("name"), Token::Str("stick"), @@ -242,6 +254,9 @@ mod tests { Token::Str("test"), Token::Str("premium_type"), Token::Some, + Token::NewtypeStruct { + name: "PremiumType", + }, Token::U8(2), Token::Str("public_flags"), Token::Some, diff --git a/twilight-model/src/channel/message/sticker/pack.rs b/twilight-model/src/channel/message/sticker/pack.rs index 86de0c17f85..6e36071346f 100644 --- a/twilight-model/src/channel/message/sticker/pack.rs +++ b/twilight-model/src/channel/message/sticker/pack.rs @@ -5,9 +5,9 @@ use crate::id::{ }; use serde::{Deserialize, Serialize}; -/// Pack of [`Standard`] stickers. +/// Pack of [`STANDARD`] stickers. /// -/// [`Standard`]: super::StickerType::Standard +/// [`STANDARD`]: super::StickerType::STANDARD #[derive(Clone, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)] pub struct StickerPack { /// ID of the sticker pack's banner image. @@ -72,10 +72,10 @@ mod tests { stickers: Vec::from([Sticker { available: true, description: Some("Wumpus waves hello".into()), - format_type: StickerFormatType::Lottie, + format_type: StickerFormatType::LOTTIE, guild_id: None, id: Id::new(749_054_660_769_218_631), - kind: StickerType::Standard, + kind: StickerType::STANDARD, name: "Wave".into(), pack_id: Some(Id::new(847_199_849_233_514_549)), sort_value: Some(12), @@ -121,11 +121,17 @@ mod tests { Token::Some, Token::Str("Wumpus waves hello"), Token::Str("format_type"), + Token::NewtypeStruct { + name: "StickerFormatType", + }, Token::U8(3), Token::Str("id"), Token::NewtypeStruct { name: "Id" }, Token::Str("749054660769218631"), Token::Str("type"), + Token::NewtypeStruct { + name: "StickerType", + }, Token::U8(1), Token::Str("name"), Token::Str("Wave"), diff --git a/twilight-model/src/channel/mod.rs b/twilight-model/src/channel/mod.rs index 9ed666eb023..5c7ea952e64 100644 --- a/twilight-model/src/channel/mod.rs +++ b/twilight-model/src/channel/mod.rs @@ -172,7 +172,7 @@ pub struct Channel { pub user_limit: Option, /// Camera video quality mode of the channel. /// - /// Defaults to [`VideoQualityMode::Auto`] for applicable channels. + /// Defaults to [`VideoQualityMode::AUTO`] for applicable channels. #[serde(skip_serializing_if = "Option::is_none")] pub video_quality_mode: Option, } @@ -233,7 +233,7 @@ mod tests { icon: None, id: Id::new(2), invitable: None, - kind: ChannelType::GuildText, + kind: ChannelType::GUILD_TEXT, last_message_id: Some(Id::new(3)), last_pin_timestamp: None, member: None, @@ -275,7 +275,7 @@ mod tests { icon: None, id: Id::new(1), invitable: None, - kind: ChannelType::GuildCategory, + kind: ChannelType::GUILD_CATEGORY, last_message_id: None, last_pin_timestamp: None, member: None, @@ -329,7 +329,7 @@ mod tests { icon: None, id: Id::new(1), invitable: None, - kind: ChannelType::GuildAnnouncement, + kind: ChannelType::GUILD_ANNOUNCEMENT, last_message_id: Some(Id::new(4)), last_pin_timestamp: None, member: None, @@ -364,7 +364,7 @@ mod tests { "permission_overwrites": permission_overwrites, "position": 3, "topic": "a news channel", - "type": ChannelType::GuildAnnouncement, + "type": ChannelType::GUILD_ANNOUNCEMENT, })) .unwrap() ); @@ -380,7 +380,7 @@ mod tests { applied_tags: None, available_tags: None, bitrate: None, - default_auto_archive_duration: Some(AutoArchiveDuration::Hour), + default_auto_archive_duration: Some(AutoArchiveDuration::HOUR), default_forum_layout: None, default_reaction_emoji: None, default_sort_order: None, @@ -390,7 +390,7 @@ mod tests { icon: None, id: Id::new(6), invitable: None, - kind: ChannelType::AnnouncementThread, + kind: ChannelType::ANNOUNCEMENT_THREAD, last_message_id: Some(Id::new(3)), last_pin_timestamp: None, member: Some(ThreadMember { @@ -415,7 +415,7 @@ mod tests { rtc_region: None, thread_metadata: Some(ThreadMetadata { archived: false, - auto_archive_duration: AutoArchiveDuration::Day, + auto_archive_duration: AutoArchiveDuration::DAY, archive_timestamp: timestamp, create_timestamp: Some(timestamp), invitable: None, @@ -431,7 +431,7 @@ mod tests { serde_json::from_value(serde_json::json!({ "id": "6", "guild_id": "1", - "type": ChannelType::AnnouncementThread, + "type": ChannelType::ANNOUNCEMENT_THREAD, "last_message_id": "3", "member": { "flags": 0, @@ -450,7 +450,7 @@ mod tests { "thread_metadata": { "archive_timestamp": formatted, "archived": false, - "auto_archive_duration": AutoArchiveDuration::Day, + "auto_archive_duration": AutoArchiveDuration::DAY, "create_timestamp": formatted, "locked": false } @@ -468,7 +468,7 @@ mod tests { applied_tags: None, available_tags: None, bitrate: None, - default_auto_archive_duration: Some(AutoArchiveDuration::Hour), + default_auto_archive_duration: Some(AutoArchiveDuration::HOUR), default_forum_layout: None, default_reaction_emoji: None, default_sort_order: None, @@ -478,7 +478,7 @@ mod tests { icon: None, id: Id::new(6), invitable: None, - kind: ChannelType::PublicThread, + kind: ChannelType::PUBLIC_THREAD, last_message_id: Some(Id::new(3)), last_pin_timestamp: None, member: Some(ThreadMember { @@ -503,7 +503,7 @@ mod tests { rtc_region: None, thread_metadata: Some(ThreadMetadata { archived: false, - auto_archive_duration: AutoArchiveDuration::Day, + auto_archive_duration: AutoArchiveDuration::DAY, archive_timestamp: timestamp, create_timestamp: Some(timestamp), invitable: None, @@ -519,7 +519,7 @@ mod tests { serde_json::from_value(serde_json::json!({ "id": "6", "guild_id": "1", - "type": ChannelType::PublicThread, + "type": ChannelType::PUBLIC_THREAD, "last_message_id": "3", "member": { "flags": 0, @@ -538,7 +538,7 @@ mod tests { "thread_metadata": { "archive_timestamp": timestamp, "archived": false, - "auto_archive_duration": AutoArchiveDuration::Day, + "auto_archive_duration": AutoArchiveDuration::DAY, "create_timestamp": timestamp, "locked": false } @@ -557,7 +557,7 @@ mod tests { applied_tags: None, available_tags: None, bitrate: None, - default_auto_archive_duration: Some(AutoArchiveDuration::Hour), + default_auto_archive_duration: Some(AutoArchiveDuration::HOUR), default_forum_layout: None, default_reaction_emoji: None, default_sort_order: None, @@ -567,7 +567,7 @@ mod tests { icon: None, id: Id::new(6), invitable: Some(true), - kind: ChannelType::PrivateThread, + kind: ChannelType::PRIVATE_THREAD, last_message_id: Some(Id::new(3)), last_pin_timestamp: None, member: Some(ThreadMember { @@ -591,7 +591,7 @@ mod tests { allow: Permissions::empty(), deny: Permissions::empty(), id: Id::new(5), - kind: PermissionOverwriteType::Member, + kind: PermissionOverwriteType::MEMBER, }])), position: None, rate_limit_per_user: Some(1000), @@ -599,7 +599,7 @@ mod tests { rtc_region: None, thread_metadata: Some(ThreadMetadata { archived: false, - auto_archive_duration: AutoArchiveDuration::Day, + auto_archive_duration: AutoArchiveDuration::DAY, archive_timestamp: timestamp, create_timestamp: Some(timestamp), invitable: None, @@ -615,7 +615,7 @@ mod tests { serde_json::from_value(serde_json::json!({ "id": "6", "guild_id": "1", - "type": ChannelType::PrivateThread, + "type": ChannelType::PRIVATE_THREAD, "last_message_id": "3", "member": { "flags": 0, @@ -635,7 +635,7 @@ mod tests { "thread_metadata": { "archive_timestamp": formatted, "archived": false, - "auto_archive_duration": AutoArchiveDuration::Day, + "auto_archive_duration": AutoArchiveDuration::DAY, "create_timestamp": formatted, "locked": false }, diff --git a/twilight-model/src/channel/permission_overwrite.rs b/twilight-model/src/channel/permission_overwrite.rs index dc1f77ac0bd..8b848242345 100644 --- a/twilight-model/src/channel/permission_overwrite.rs +++ b/twilight-model/src/channel/permission_overwrite.rs @@ -16,37 +16,29 @@ pub struct PermissionOverwrite { /// Type of a permission overwrite target. // Keep in sync with `twilight_util::permission_calculator::PermissionCalculator`! -#[derive(Clone, Copy, Debug, Serialize, Eq, Hash, PartialEq, Deserialize)] -#[non_exhaustive] -#[serde(from = "u8", into = "u8", rename_all = "snake_case")] -pub enum PermissionOverwriteType { +#[derive(Clone, Copy, Serialize, Eq, Hash, PartialEq, Deserialize)] +pub struct PermissionOverwriteType(u8); + +impl PermissionOverwriteType { /// Permission overwrite targets an individual member. - Member, + pub const MEMBER: Self = Self::new(1); + /// Permission overwrite targets an individual role. - Role, - /// Variant value is unknown to the library. - Unknown(u8), -} + pub const ROLE: Self = Self::new(0); -impl From for PermissionOverwriteType { - fn from(value: u8) -> Self { - match value { - 0 => PermissionOverwriteType::Role, - 1 => PermissionOverwriteType::Member, - unknown => PermissionOverwriteType::Unknown(unknown), - } + /// Name of the associated constant. + /// + /// Returns `None` if the value doesn't have a defined constant. + pub const fn name(self) -> Option<&'static str> { + Some(match self { + Self::MEMBER => "MEMBER", + Self::ROLE => "ROLE", + _ => return None, + }) } } -impl From for u8 { - fn from(value: PermissionOverwriteType) -> Self { - match value { - PermissionOverwriteType::Member => 1, - PermissionOverwriteType::Role => 0, - PermissionOverwriteType::Unknown(unknown) => unknown, - } - } -} +impl_typed!(PermissionOverwriteType, u8); #[cfg(test)] mod tests { @@ -86,7 +78,7 @@ mod tests { allow: Permissions::CREATE_INVITE, deny: Permissions::KICK_MEMBERS, id: Id::new(12_345_678), - kind: PermissionOverwriteType::Member, + kind: PermissionOverwriteType::MEMBER, }; serde_test::assert_tokens( @@ -104,7 +96,10 @@ mod tests { Token::NewtypeStruct { name: "Id" }, Token::Str("12345678"), Token::Str("type"), - Token::U8(PermissionOverwriteType::Member.into()), + Token::NewtypeStruct { + name: "PermissionOverwriteType", + }, + Token::U8(PermissionOverwriteType::MEMBER.get()), Token::StructEnd, ], ); @@ -124,7 +119,7 @@ mod tests { allow: Permissions::CREATE_INVITE, deny: Permissions::KICK_MEMBERS, id: Id::new(1), - kind: PermissionOverwriteType::Member, + kind: PermissionOverwriteType::MEMBER, }; let deserialized = serde_json::from_str::(raw).unwrap(); @@ -146,6 +141,9 @@ mod tests { Token::NewtypeStruct { name: "Id" }, Token::Str("1"), Token::Str("type"), + Token::NewtypeStruct { + name: "PermissionOverwriteType", + }, Token::U8(1), Token::StructEnd, ], @@ -154,8 +152,23 @@ mod tests { #[test] fn overwrite_type_name() { - serde_test::assert_tokens(&PermissionOverwriteType::Member, &[Token::U8(1)]); - serde_test::assert_tokens(&PermissionOverwriteType::Role, &[Token::U8(0)]); - serde_test::assert_tokens(&PermissionOverwriteType::Unknown(99), &[Token::U8(99)]); + serde_test::assert_tokens( + &PermissionOverwriteType::MEMBER, + &[ + Token::NewtypeStruct { + name: "PermissionOverwriteType", + }, + Token::U8(1), + ], + ); + serde_test::assert_tokens( + &PermissionOverwriteType::ROLE, + &[ + Token::NewtypeStruct { + name: "PermissionOverwriteType", + }, + Token::U8(0), + ], + ); } } diff --git a/twilight-model/src/channel/stage_instance/mod.rs b/twilight-model/src/channel/stage_instance/mod.rs index f2093689872..461b739e850 100644 --- a/twilight-model/src/channel/stage_instance/mod.rs +++ b/twilight-model/src/channel/stage_instance/mod.rs @@ -34,7 +34,7 @@ mod tests { guild_id: Id::new(200), guild_scheduled_event_id: Some(Id::new(300)), id: Id::new(400), - privacy_level: PrivacyLevel::GuildOnly, + privacy_level: PrivacyLevel::GUILD_ONLY, topic: "a topic".into(), }; @@ -59,6 +59,9 @@ mod tests { Token::NewtypeStruct { name: "Id" }, Token::Str("400"), Token::Str("privacy_level"), + Token::NewtypeStruct { + name: "PrivacyLevel", + }, Token::U8(2), Token::Str("topic"), Token::Str("a topic"), diff --git a/twilight-model/src/channel/stage_instance/privacy_level.rs b/twilight-model/src/channel/stage_instance/privacy_level.rs index c1da74901ea..080b3e6e9cb 100644 --- a/twilight-model/src/channel/stage_instance/privacy_level.rs +++ b/twilight-model/src/channel/stage_instance/privacy_level.rs @@ -1,19 +1,45 @@ -use serde_repr::{Deserialize_repr, Serialize_repr}; +use serde::{Deserialize, Serialize}; -#[derive(Clone, Copy, Debug, Deserialize_repr, Eq, Hash, PartialEq, Serialize_repr)] -#[non_exhaustive] -#[repr(u8)] -pub enum PrivacyLevel { - GuildOnly = 2, +#[derive(Clone, Copy, Deserialize, Eq, Hash, PartialEq, Serialize)] +pub struct PrivacyLevel(u8); + +impl PrivacyLevel { + pub const GUILD_ONLY: Self = Self::new(2); + + /// Name of the associated constant. + /// + /// Returns `None` if the value doesn't have a defined constant. + pub const fn name(self) -> Option<&'static str> { + Some(match self { + Self::GUILD_ONLY => "GUILD_ONLY", + _ => return None, + }) + } } +impl_typed!(PrivacyLevel, u8); + #[cfg(test)] mod tests { use super::PrivacyLevel; use serde_test::Token; + const MAP: &[(PrivacyLevel, u8)] = &[(PrivacyLevel::GUILD_ONLY, 2)]; + #[test] fn variants() { - serde_test::assert_tokens(&PrivacyLevel::GuildOnly, &[Token::U8(2)]); + for (kind, num) in MAP { + serde_test::assert_tokens( + kind, + &[ + Token::NewtypeStruct { + name: "PrivacyLevel", + }, + Token::U8(*num), + ], + ); + assert_eq!(*kind, PrivacyLevel::from(*num)); + assert_eq!(*num, kind.get()); + } } } diff --git a/twilight-model/src/channel/thread/auto_archive_duration.rs b/twilight-model/src/channel/thread/auto_archive_duration.rs index ae979b2b721..4c86799411d 100644 --- a/twilight-model/src/channel/thread/auto_archive_duration.rs +++ b/twilight-model/src/channel/thread/auto_archive_duration.rs @@ -1,71 +1,39 @@ -use crate::visitor::U16EnumVisitor; -use serde::{ - de::{Deserialize, Deserializer}, - ser::{Serialize, Serializer}, -}; +use serde::{Deserialize, Serialize}; use std::time::Duration; -#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)] -pub enum AutoArchiveDuration { - Hour, - Day, - ThreeDays, - Week, - Unknown { value: u16 }, -} +#[derive(Clone, Copy, Deserialize, Eq, Hash, PartialEq, Serialize)] +pub struct AutoArchiveDuration(u16); impl AutoArchiveDuration { - /// Retrieve the length of the duration in minutes, used by the API - /// - /// # Examples - /// - /// ``` - /// use twilight_model::channel::thread::AutoArchiveDuration; - /// - /// assert_eq!(60, AutoArchiveDuration::Hour.number()); - /// ``` - pub const fn number(self) -> u16 { - match self { - Self::Hour => 60, - Self::Day => 1440, - Self::ThreeDays => 4320, - Self::Week => 10080, - Self::Unknown { value } => value, - } - } -} + pub const HOUR: Self = Self::new(60); -impl From for AutoArchiveDuration { - fn from(value: u16) -> Self { - match value { - 60 => Self::Hour, - 1440 => Self::Day, - 4320 => Self::ThreeDays, - 10080 => Self::Week, - value => Self::Unknown { value }, - } + pub const DAY: Self = Self::new(Self::HOUR.get() * 24); + + pub const THREE_DAYS: Self = Self::new(Self::DAY.get() * 3); + + pub const WEEK: Self = Self::new(Self::DAY.get() * 7); + + /// Name of the associated constant. + /// + /// Returns `None` if the value doesn't have a defined constant. + pub const fn name(self) -> Option<&'static str> { + Some(match self { + Self::HOUR => "HOUR", + Self::DAY => "DAY", + Self::THREE_DAYS => "THREE_DAYS", + Self::WEEK => "WEEK", + _ => return None, + }) } } impl From for Duration { fn from(value: AutoArchiveDuration) -> Self { - Self::from_secs(u64::from(value.number()) * 60) - } -} - -impl<'de> Deserialize<'de> for AutoArchiveDuration { - fn deserialize>(deserializer: D) -> Result { - deserializer - .deserialize_u16(U16EnumVisitor::new("auto archive duration")) - .map(u16::into) + Self::from_secs(u64::from(value.get()) * 60) } } -impl Serialize for AutoArchiveDuration { - fn serialize(&self, serializer: S) -> Result { - serializer.serialize_u16(self.number()) - } -} +impl_typed!(AutoArchiveDuration, u16); #[cfg(test)] mod tests { @@ -74,34 +42,34 @@ mod tests { use std::time::Duration; const MAP: &[(AutoArchiveDuration, u16)] = &[ - (AutoArchiveDuration::Hour, 60), - (AutoArchiveDuration::Day, 1440), - (AutoArchiveDuration::ThreeDays, 4320), - (AutoArchiveDuration::Week, 10080), + (AutoArchiveDuration::HOUR, 60), + (AutoArchiveDuration::DAY, 1440), + (AutoArchiveDuration::THREE_DAYS, 4320), + (AutoArchiveDuration::WEEK, 10080), ]; #[test] fn variants() { for (kind, num) in MAP { - serde_test::assert_tokens(kind, &[Token::U16(*num)]); + serde_test::assert_tokens( + kind, + &[ + Token::NewtypeStruct { + name: "AutoArchiveDuration", + }, + Token::U16(*num), + ], + ); assert_eq!(*kind, AutoArchiveDuration::from(*num)); - assert_eq!(*num, kind.number()); + assert_eq!(*num, kind.get()); } } - #[test] - fn unknown_conversion() { - assert_eq!( - AutoArchiveDuration::Unknown { value: 250 }, - AutoArchiveDuration::from(250) - ); - } - #[test] fn std_time_duration() { for (kind, _) in MAP { let std_duration = Duration::from(*kind); - assert_eq!(u64::from(kind.number()) * 60, std_duration.as_secs()); + assert_eq!(u64::from(kind.get()) * 60, std_duration.as_secs()); } } } diff --git a/twilight-model/src/channel/thread/metadata.rs b/twilight-model/src/channel/thread/metadata.rs index 65f251cd1bb..056fdb79f0f 100644 --- a/twilight-model/src/channel/thread/metadata.rs +++ b/twilight-model/src/channel/thread/metadata.rs @@ -39,7 +39,7 @@ mod tests { let value = ThreadMetadata { archived: true, - auto_archive_duration: AutoArchiveDuration::Day, + auto_archive_duration: AutoArchiveDuration::DAY, archive_timestamp: timestamp, create_timestamp: Some(timestamp), invitable: Some(false), @@ -56,6 +56,9 @@ mod tests { Token::Str("archived"), Token::Bool(true), Token::Str("auto_archive_duration"), + Token::NewtypeStruct { + name: "AutoArchiveDuration", + }, Token::U16(1440), Token::Str("archive_timestamp"), Token::Str(DATETIME), diff --git a/twilight-model/src/channel/video_quality_mode.rs b/twilight-model/src/channel/video_quality_mode.rs index 6422cbca9a7..0149a7c28f7 100644 --- a/twilight-model/src/channel/video_quality_mode.rs +++ b/twilight-model/src/channel/video_quality_mode.rs @@ -1,63 +1,51 @@ use serde::{Deserialize, Serialize}; -#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)] -#[non_exhaustive] -#[serde(from = "u8", into = "u8")] -pub enum VideoQualityMode { +#[derive(Clone, Copy, Deserialize, Eq, Hash, PartialEq, Serialize)] +pub struct VideoQualityMode(u8); + +impl VideoQualityMode { /// Discord chooses the quality for optimal performance. - Auto, - /// 720p. - Full, - /// Variant value is unknown to the library. - Unknown(u8), -} + pub const AUTO: Self = Self::new(1); -impl From for VideoQualityMode { - fn from(value: u8) -> Self { - match value { - 1 => VideoQualityMode::Auto, - 2 => VideoQualityMode::Full, - unknown => VideoQualityMode::Unknown(unknown), - } - } -} + /// 720p. + pub const FULL: Self = Self::new(2); -impl From for u8 { - fn from(value: VideoQualityMode) -> Self { - match value { - VideoQualityMode::Auto => 1, - VideoQualityMode::Full => 2, - VideoQualityMode::Unknown(unknown) => unknown, - } + /// Name of the associated constant. + /// + /// Returns `None` if the value doesn't have a defined constant. + pub const fn name(self) -> Option<&'static str> { + Some(match self { + Self::AUTO => "AUTO", + Self::FULL => "FULL", + _ => return None, + }) } } -impl VideoQualityMode { - pub const fn name(self) -> &'static str { - match self { - Self::Auto => "Auto", - Self::Full => "Full", - Self::Unknown(_) => "Unknown", - } - } -} +impl_typed!(VideoQualityMode, u8); #[cfg(test)] mod tests { use super::VideoQualityMode; use serde_test::Token; - #[test] - fn variants() { - serde_test::assert_tokens(&VideoQualityMode::Auto, &[Token::U8(1)]); - serde_test::assert_tokens(&VideoQualityMode::Full, &[Token::U8(2)]); - serde_test::assert_tokens(&VideoQualityMode::Unknown(99), &[Token::U8(99)]); - } + const MAP: &[(VideoQualityMode, u8)] = + &[(VideoQualityMode::AUTO, 1), (VideoQualityMode::FULL, 2)]; #[test] - fn names() { - assert_eq!("Auto", VideoQualityMode::Auto.name()); - assert_eq!("Full", VideoQualityMode::Full.name()); - assert_eq!("Unknown", VideoQualityMode::Unknown(99).name()); + fn variants() { + for (kind, num) in MAP { + serde_test::assert_tokens( + kind, + &[ + Token::NewtypeStruct { + name: "VideoQualityMode", + }, + Token::U8(*num), + ], + ); + assert_eq!(*kind, VideoQualityMode::from(*num)); + assert_eq!(*num, kind.get()); + } } } diff --git a/twilight-model/src/channel/webhook/kind.rs b/twilight-model/src/channel/webhook/kind.rs index 4caf6646c01..61a84a6dc83 100644 --- a/twilight-model/src/channel/webhook/kind.rs +++ b/twilight-model/src/channel/webhook/kind.rs @@ -1,59 +1,62 @@ use serde::{Deserialize, Serialize}; -#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)] -#[serde(from = "u8", into = "u8")] -pub enum WebhookType { - Incoming, - ChannelFollower, - /// Webhooks used with interactions. - Application, - /// Variant value is unknown to the library. - Unknown(u8), -} +#[derive(Clone, Copy, Deserialize, Eq, Hash, PartialEq, Serialize)] +pub struct WebhookType(u8); -impl From for WebhookType { - fn from(value: u8) -> Self { - match value { - 1 => WebhookType::Incoming, - 2 => WebhookType::ChannelFollower, - 3 => WebhookType::Application, - unknown => WebhookType::Unknown(unknown), - } - } -} +impl WebhookType { + pub const INCOMING: Self = Self::new(1); -impl From for u8 { - fn from(value: WebhookType) -> Self { - match value { - WebhookType::Incoming => 1, - WebhookType::ChannelFollower => 2, - WebhookType::Application => 3, - WebhookType::Unknown(unknown) => unknown, - } + pub const CHANNEL_FOLLOWER: Self = Self::new(2); + + /// Webhooks used with interactions. + pub const APPLICATION: Self = Self::new(3); + + /// Name of the associated constant. + /// + /// Returns `None` if the value doesn't have a defined constant. + pub const fn name(self) -> Option<&'static str> { + Some(match self { + Self::APPLICATION => "APPLICATION", + Self::CHANNEL_FOLLOWER => "CHANNEL_FOLLOWER", + Self::INCOMING => "INCOMING", + _ => return None, + }) } } impl Default for WebhookType { fn default() -> Self { - Self::Incoming + Self::INCOMING } } +impl_typed!(WebhookType, u8); + #[cfg(test)] mod tests { use super::WebhookType; use serde_test::Token; - #[test] - fn default() { - assert_eq!(WebhookType::Incoming, WebhookType::default()); - } + const MAP: &[(WebhookType, u8)] = &[ + (WebhookType::INCOMING, 1), + (WebhookType::CHANNEL_FOLLOWER, 2), + (WebhookType::APPLICATION, 3), + ]; #[test] fn variants() { - serde_test::assert_tokens(&WebhookType::Incoming, &[Token::U8(1)]); - serde_test::assert_tokens(&WebhookType::ChannelFollower, &[Token::U8(2)]); - serde_test::assert_tokens(&WebhookType::Application, &[Token::U8(3)]); - serde_test::assert_tokens(&WebhookType::Unknown(99), &[Token::U8(99)]); + for (kind, num) in MAP { + serde_test::assert_tokens( + kind, + &[ + Token::NewtypeStruct { + name: "WebhookType", + }, + Token::U8(*num), + ], + ); + assert_eq!(*kind, WebhookType::from(*num)); + assert_eq!(*num, kind.get()); + } } } diff --git a/twilight-model/src/channel/webhook/mod.rs b/twilight-model/src/channel/webhook/mod.rs index dcdee4fac3e..2657ff5ee77 100644 --- a/twilight-model/src/channel/webhook/mod.rs +++ b/twilight-model/src/channel/webhook/mod.rs @@ -86,7 +86,7 @@ mod tests { channel_id: Id::new(1), guild_id: Some(Id::new(2)), id: Id::new(3), - kind: WebhookType::Incoming, + kind: WebhookType::INCOMING, name: Some("a webhook".to_owned()), source_channel: None, source_guild: None, @@ -120,6 +120,9 @@ mod tests { Token::NewtypeStruct { name: "Id" }, Token::Str("3"), Token::Str("type"), + Token::NewtypeStruct { + name: "WebhookType", + }, Token::U8(1), Token::Str("name"), Token::Some, @@ -141,7 +144,7 @@ mod tests { channel_id: Id::new(1), guild_id: Some(Id::new(2)), id: Id::new(3), - kind: WebhookType::Incoming, + kind: WebhookType::INCOMING, name: Some("a webhook".to_owned()), source_channel: Some(WebhookChannel { id: Id::new(4), @@ -198,6 +201,9 @@ mod tests { Token::NewtypeStruct { name: "Id" }, Token::Str("3"), Token::Str("type"), + Token::NewtypeStruct { + name: "WebhookType", + }, Token::U8(1), Token::Str("name"), Token::Some, diff --git a/twilight-model/src/gateway/close_code.rs b/twilight-model/src/gateway/close_code.rs index fb21669a3f9..9c7ff7a8a54 100644 --- a/twilight-model/src/gateway/close_code.rs +++ b/twilight-model/src/gateway/close_code.rs @@ -1,211 +1,129 @@ -use serde_repr::{Deserialize_repr, Serialize_repr}; -use std::{ - error::Error, - fmt::{Display, Formatter, Result as FmtResult}, -}; +use serde::{Deserialize, Serialize}; /// Gateway close event codes. /// /// See [Discord Docs/Gateway Close Event Codes] for more information. /// /// [Discord Docs/Gateway Close Event Codes]: https://discord.com/developers/docs/topics/opcodes-and-status-codes#gateway-gateway-close-event-codes -#[derive(Clone, Copy, Debug, Deserialize_repr, Eq, Hash, PartialEq, Serialize_repr)] -#[non_exhaustive] -#[repr(u16)] -pub enum CloseCode { +#[derive(Clone, Copy, Deserialize, Eq, Hash, PartialEq, Serialize)] +pub struct CloseCode(u16); + +impl CloseCode { /// An unknown error occurred. - UnknownError = 4000, + pub const UNKNOWN_ERROR: Self = Self::new(4000); + /// An invalid opcode or payload for an opcode was sent. - UnknownOpcode = 4001, + pub const UNKNOWN_OPCODE: Self = Self::new(4001); + /// An invalid payload was sent. - DecodeError = 4002, + pub const DECODE_ERROR: Self = Self::new(4002); + /// A payload was sent prior to identifying. - NotAuthenticated = 4003, + pub const NOT_AUTHENTICATED: Self = Self::new(4003); + /// An invalid token was sent when identifying. - AuthenticationFailed = 4004, + pub const AUTHENTICATION_FAILED: Self = Self::new(4004); + /// Multiple identify payloads were sent. - AlreadyAuthenticated = 4005, + pub const ALREADY_AUTHENTICATED: Self = Self::new(4005); + /// An invalid sequence was sent for resuming. - InvalidSequence = 4007, + pub const INVALID_SEQUENCE: Self = Self::new(4007); + /// Too many payloads were sent in a certain amount of time. - RateLimited = 4008, + pub const RATE_LIMITED: Self = Self::new(4008); + /// The session timed out. - SessionTimedOut = 4009, + pub const SESSION_TIMED_OUT: Self = Self::new(4009); + /// An invalid shard was sent when identifying. - InvalidShard = 4010, + pub const INVALID_SHARD: Self = Self::new(4010); + /// Sharding is required because there are too many guilds. - ShardingRequired = 4011, + pub const SHARDING_REQUIRED: Self = Self::new(4011); + /// An invalid version for the gateway was sent. - InvalidApiVersion = 4012, + pub const INVALID_API_VERSION: Self = Self::new(4012); + /// An invalid intent was sent. - InvalidIntents = 4013, + pub const INVALID_INTENTS: Self = Self::new(4013); + /// A disallowed intent was sent, may need allowlisting. - DisallowedIntents = 4014, -} + pub const DISALLOWED_INTENTS: Self = Self::new(4014); + + /// Name of the associated constant. + /// + /// Returns `None` if the value doesn't have a defined constant. + pub const fn name(self) -> Option<&'static str> { + Some(match self { + Self::UNKNOWN_ERROR => "UNKNOWN_ERROR", + Self::UNKNOWN_OPCODE => "UNKNOWN_OPCODE", + Self::DECODE_ERROR => "DECODE_ERROR", + Self::NOT_AUTHENTICATED => "NOT_AUTHENTICATED", + Self::AUTHENTICATION_FAILED => "AUTHENTICATION_FAILED", + Self::ALREADY_AUTHENTICATED => "ALREADY_AUTHENTICATED", + Self::INVALID_SEQUENCE => "INVALID_SEQUENCE", + Self::RATE_LIMITED => "RATE_LIMITED", + Self::SESSION_TIMED_OUT => "SESSION_TIMED_OUT", + Self::INVALID_SHARD => "INVALID_SHARD", + Self::SHARDING_REQUIRED => "SHARDING_REQUIRED", + Self::INVALID_API_VERSION => "INVALID_API_VERSION", + Self::INVALID_INTENTS => "INVALID_INTENTS", + Self::DISALLOWED_INTENTS => "DISALLOWED_INTENTS", + _ => return None, + }) + } -impl CloseCode { /// Whether the close code is one that allows reconnection of a shard. /// /// Refer to the type-level documentation for Discord's table on close codes /// that can be reconnected. pub const fn can_reconnect(self) -> bool { - match self { - Self::UnknownError - | Self::UnknownOpcode - | Self::DecodeError - | Self::NotAuthenticated - | Self::AlreadyAuthenticated - | Self::InvalidSequence - | Self::RateLimited - | Self::SessionTimedOut => true, - Self::AuthenticationFailed - | Self::InvalidShard - | Self::ShardingRequired - | Self::InvalidApiVersion - | Self::InvalidIntents - | Self::DisallowedIntents => false, - } + !matches!( + self, + Self::AUTHENTICATION_FAILED + | Self::INVALID_SHARD + | Self::SHARDING_REQUIRED + | Self::INVALID_API_VERSION + | Self::INVALID_INTENTS + | Self::DISALLOWED_INTENTS + ) } } -impl Display for CloseCode { - fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult { - f.write_str(match self { - CloseCode::UnknownError => "Unknown Error", - CloseCode::UnknownOpcode => "Unknown Opcode", - CloseCode::DecodeError => "Decode Error", - CloseCode::NotAuthenticated => "Not Authenticated", - CloseCode::AuthenticationFailed => "Authentication Failed", - CloseCode::AlreadyAuthenticated => "Already Authenticated", - CloseCode::InvalidSequence => "Invalid Sequence", - CloseCode::RateLimited => "Rate Limited", - CloseCode::SessionTimedOut => "Session Timed Out", - CloseCode::InvalidShard => "Invalid Shard", - CloseCode::ShardingRequired => "Sharding Required", - CloseCode::InvalidApiVersion => "Invalid Api Version", - CloseCode::InvalidIntents => "Invalid Intents", - CloseCode::DisallowedIntents => "Disallowed Intents", - }) - } -} - -#[derive(Debug, Eq, PartialEq)] -pub struct CloseCodeConversionError { - code: u16, -} - -impl CloseCodeConversionError { - const fn new(code: u16) -> Self { - Self { code } - } - - pub const fn code(&self) -> u16 { - self.code - } -} - -impl Display for CloseCodeConversionError { - fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult { - Display::fmt(&self.code, f)?; - - f.write_str(" isn't a valid close code") - } -} - -impl Error for CloseCodeConversionError {} - -impl TryFrom for CloseCode { - type Error = CloseCodeConversionError; - - fn try_from(value: u16) -> Result { - let close_code = match value { - 4000 => CloseCode::UnknownError, - 4001 => CloseCode::UnknownOpcode, - 4002 => CloseCode::DecodeError, - 4003 => CloseCode::NotAuthenticated, - 4004 => CloseCode::AuthenticationFailed, - 4005 => CloseCode::AlreadyAuthenticated, - 4007 => CloseCode::InvalidSequence, - 4008 => CloseCode::RateLimited, - 4009 => CloseCode::SessionTimedOut, - 4010 => CloseCode::InvalidShard, - 4011 => CloseCode::ShardingRequired, - 4012 => CloseCode::InvalidApiVersion, - 4013 => CloseCode::InvalidIntents, - 4014 => CloseCode::DisallowedIntents, - _ => return Err(CloseCodeConversionError::new(value)), - }; - - Ok(close_code) - } -} +impl_typed!(CloseCode, u16); #[cfg(test)] mod tests { use super::CloseCode; use serde_test::Token; - #[test] - fn variants() { - serde_test::assert_tokens(&CloseCode::UnknownError, &[Token::U16(4000)]); - serde_test::assert_tokens(&CloseCode::UnknownOpcode, &[Token::U16(4001)]); - serde_test::assert_tokens(&CloseCode::DecodeError, &[Token::U16(4002)]); - serde_test::assert_tokens(&CloseCode::NotAuthenticated, &[Token::U16(4003)]); - serde_test::assert_tokens(&CloseCode::AuthenticationFailed, &[Token::U16(4004)]); - serde_test::assert_tokens(&CloseCode::AlreadyAuthenticated, &[Token::U16(4005)]); - serde_test::assert_tokens(&CloseCode::InvalidSequence, &[Token::U16(4007)]); - serde_test::assert_tokens(&CloseCode::RateLimited, &[Token::U16(4008)]); - serde_test::assert_tokens(&CloseCode::SessionTimedOut, &[Token::U16(4009)]); - serde_test::assert_tokens(&CloseCode::InvalidShard, &[Token::U16(4010)]); - serde_test::assert_tokens(&CloseCode::ShardingRequired, &[Token::U16(4011)]); - serde_test::assert_tokens(&CloseCode::InvalidApiVersion, &[Token::U16(4012)]); - serde_test::assert_tokens(&CloseCode::InvalidIntents, &[Token::U16(4013)]); - serde_test::assert_tokens(&CloseCode::DisallowedIntents, &[Token::U16(4014)]); - } + pub const MAP: &[(CloseCode, u16)] = &[ + (CloseCode::UNKNOWN_ERROR, 4000), + (CloseCode::UNKNOWN_OPCODE, 4001), + (CloseCode::DECODE_ERROR, 4002), + (CloseCode::NOT_AUTHENTICATED, 4003), + (CloseCode::AUTHENTICATION_FAILED, 4004), + (CloseCode::ALREADY_AUTHENTICATED, 4005), + (CloseCode::INVALID_SEQUENCE, 4007), + (CloseCode::RATE_LIMITED, 4008), + (CloseCode::SESSION_TIMED_OUT, 4009), + (CloseCode::INVALID_SHARD, 4010), + (CloseCode::SHARDING_REQUIRED, 4011), + (CloseCode::INVALID_API_VERSION, 4012), + (CloseCode::INVALID_INTENTS, 4013), + (CloseCode::DISALLOWED_INTENTS, 4014), + ]; #[test] - fn conversion() { - assert_eq!(CloseCode::try_from(4000).unwrap(), CloseCode::UnknownError); - assert_eq!(CloseCode::try_from(4001).unwrap(), CloseCode::UnknownOpcode); - assert_eq!(CloseCode::try_from(4002).unwrap(), CloseCode::DecodeError); - assert_eq!( - CloseCode::try_from(4003).unwrap(), - CloseCode::NotAuthenticated - ); - assert_eq!( - CloseCode::try_from(4004).unwrap(), - CloseCode::AuthenticationFailed - ); - assert_eq!( - CloseCode::try_from(4005).unwrap(), - CloseCode::AlreadyAuthenticated - ); - assert_eq!( - CloseCode::try_from(4007).unwrap(), - CloseCode::InvalidSequence - ); - assert_eq!(CloseCode::try_from(4008).unwrap(), CloseCode::RateLimited); - assert_eq!( - CloseCode::try_from(4009).unwrap(), - CloseCode::SessionTimedOut - ); - assert_eq!(CloseCode::try_from(4010).unwrap(), CloseCode::InvalidShard); - assert_eq!( - CloseCode::try_from(4011).unwrap(), - CloseCode::ShardingRequired - ); - assert_eq!( - CloseCode::try_from(4012).unwrap(), - CloseCode::InvalidApiVersion - ); - assert_eq!( - CloseCode::try_from(4013).unwrap(), - CloseCode::InvalidIntents - ); - assert_eq!( - CloseCode::try_from(4014).unwrap(), - CloseCode::DisallowedIntents - ); - assert!(CloseCode::try_from(5000).is_err()); + fn variants() { + for (kind, num) in MAP { + serde_test::assert_tokens( + kind, + &[Token::NewtypeStruct { name: "CloseCode" }, Token::U16(*num)], + ); + assert_eq!(*kind, CloseCode::from(*num)); + assert_eq!(*num, kind.get()); + } } } diff --git a/twilight-model/src/gateway/event/dispatch.rs b/twilight-model/src/gateway/event/dispatch.rs index d3722408406..9a526596590 100644 --- a/twilight-model/src/gateway/event/dispatch.rs +++ b/twilight-model/src/gateway/event/dispatch.rs @@ -84,70 +84,70 @@ impl DispatchEvent { /// Returns the type of event that this event is. pub const fn kind(&self) -> EventType { match self { - Self::AutoModerationActionExecution(_) => EventType::AutoModerationActionExecution, - Self::AutoModerationRuleCreate(_) => EventType::AutoModerationRuleCreate, - Self::AutoModerationRuleDelete(_) => EventType::AutoModerationRuleDelete, - Self::AutoModerationRuleUpdate(_) => EventType::AutoModerationRuleUpdate, - Self::BanAdd(_) => EventType::BanAdd, - Self::BanRemove(_) => EventType::BanRemove, - Self::ChannelCreate(_) => EventType::ChannelCreate, - Self::ChannelDelete(_) => EventType::ChannelDelete, - Self::ChannelPinsUpdate(_) => EventType::ChannelPinsUpdate, - Self::ChannelUpdate(_) => EventType::ChannelUpdate, - Self::CommandPermissionsUpdate(_) => EventType::CommandPermissionsUpdate, - Self::GiftCodeUpdate => EventType::GiftCodeUpdate, - Self::GuildAuditLogEntryCreate(_) => EventType::GuildAuditLogEntryCreate, - Self::GuildCreate(_) => EventType::GuildCreate, - Self::GuildDelete(_) => EventType::GuildDelete, - Self::GuildEmojisUpdate(_) => EventType::GuildEmojisUpdate, - Self::GuildIntegrationsUpdate(_) => EventType::GuildIntegrationsUpdate, - Self::GuildScheduledEventCreate(_) => EventType::GuildScheduledEventCreate, - Self::GuildScheduledEventDelete(_) => EventType::GuildScheduledEventDelete, - Self::GuildScheduledEventUpdate(_) => EventType::GuildScheduledEventUpdate, - Self::GuildScheduledEventUserAdd(_) => EventType::GuildScheduledEventUserAdd, - Self::GuildScheduledEventUserRemove(_) => EventType::GuildScheduledEventUserRemove, - Self::GuildStickersUpdate(_) => EventType::GuildStickersUpdate, - Self::GuildUpdate(_) => EventType::GuildUpdate, - Self::IntegrationCreate(_) => EventType::IntegrationCreate, - Self::IntegrationDelete(_) => EventType::IntegrationDelete, - Self::IntegrationUpdate(_) => EventType::IntegrationUpdate, - Self::InteractionCreate(_) => EventType::InteractionCreate, - Self::InviteCreate(_) => EventType::InviteCreate, - Self::InviteDelete(_) => EventType::InviteDelete, - Self::MemberAdd(_) => EventType::MemberAdd, - Self::MemberRemove(_) => EventType::MemberRemove, - Self::MemberUpdate(_) => EventType::MemberUpdate, - Self::MemberChunk(_) => EventType::MemberChunk, - Self::MessageCreate(_) => EventType::MessageCreate, - Self::MessageDelete(_) => EventType::MessageDelete, - Self::MessageDeleteBulk(_) => EventType::MessageDeleteBulk, - Self::MessageUpdate(_) => EventType::MessageUpdate, - Self::PresenceUpdate(_) => EventType::PresenceUpdate, - Self::PresencesReplace => EventType::PresencesReplace, - Self::ReactionAdd(_) => EventType::ReactionAdd, - Self::ReactionRemove(_) => EventType::ReactionRemove, - Self::ReactionRemoveAll(_) => EventType::ReactionRemoveAll, - Self::ReactionRemoveEmoji(_) => EventType::ReactionRemoveEmoji, - Self::Ready(_) => EventType::Ready, - Self::Resumed => EventType::Resumed, - Self::RoleCreate(_) => EventType::RoleCreate, - Self::RoleDelete(_) => EventType::RoleDelete, - Self::RoleUpdate(_) => EventType::RoleUpdate, - Self::StageInstanceCreate(_) => EventType::StageInstanceCreate, - Self::StageInstanceDelete(_) => EventType::StageInstanceDelete, - Self::StageInstanceUpdate(_) => EventType::StageInstanceUpdate, - Self::ThreadCreate(_) => EventType::ThreadCreate, - Self::ThreadDelete(_) => EventType::ThreadDelete, - Self::ThreadListSync(_) => EventType::ThreadListSync, - Self::ThreadMemberUpdate(_) => EventType::ThreadMemberUpdate, - Self::ThreadMembersUpdate(_) => EventType::ThreadMembersUpdate, - Self::ThreadUpdate(_) => EventType::ThreadUpdate, - Self::TypingStart(_) => EventType::TypingStart, - Self::UnavailableGuild(_) => EventType::UnavailableGuild, - Self::UserUpdate(_) => EventType::UserUpdate, - Self::VoiceServerUpdate(_) => EventType::VoiceServerUpdate, - Self::VoiceStateUpdate(_) => EventType::VoiceStateUpdate, - Self::WebhooksUpdate(_) => EventType::WebhooksUpdate, + Self::AutoModerationActionExecution(_) => EventType::AUTO_MODERATION_ACTION_EXECUTION, + Self::AutoModerationRuleCreate(_) => EventType::AUTO_MODERATION_RULE_CREATE, + Self::AutoModerationRuleDelete(_) => EventType::AUTO_MODERATION_RULE_DELETE, + Self::AutoModerationRuleUpdate(_) => EventType::AUTO_MODERATION_RULE_UPDATE, + Self::BanAdd(_) => EventType::BAN_ADD, + Self::BanRemove(_) => EventType::BAN_REMOVE, + Self::ChannelCreate(_) => EventType::CHANNEL_CREATE, + Self::ChannelDelete(_) => EventType::CHANNEL_DELETE, + Self::ChannelPinsUpdate(_) => EventType::CHANNEL_PINS_UPDATE, + Self::ChannelUpdate(_) => EventType::CHANNEL_UPDATE, + Self::CommandPermissionsUpdate(_) => EventType::COMMAND_PERMISSIONS_UPDATE, + Self::GiftCodeUpdate => EventType::GIFT_CODE_UPDATE, + Self::GuildAuditLogEntryCreate(_) => EventType::GUILD_AUDIT_LOG_ENTRY_CREATE, + Self::GuildCreate(_) => EventType::GUILD_CREATE, + Self::GuildDelete(_) => EventType::GUILD_DELETE, + Self::GuildEmojisUpdate(_) => EventType::GUILD_EMOJIS_UPDATE, + Self::GuildIntegrationsUpdate(_) => EventType::GUILD_INTEGRATIONS_UPDATE, + Self::GuildScheduledEventCreate(_) => EventType::GUILD_SCHEDULED_EVENT_CREATE, + Self::GuildScheduledEventDelete(_) => EventType::GUILD_SCHEDULED_EVENT_DELETE, + Self::GuildScheduledEventUpdate(_) => EventType::GUILD_SCHEDULED_EVENT_UPDATE, + Self::GuildScheduledEventUserAdd(_) => EventType::GUILD_SCHEDULED_EVENT_USER_ADD, + Self::GuildScheduledEventUserRemove(_) => EventType::GUILD_SCHEDULED_EVENT_USER_REMOVE, + Self::GuildStickersUpdate(_) => EventType::GUILD_STICKERS_UPDATE, + Self::GuildUpdate(_) => EventType::GUILD_UPDATE, + Self::IntegrationCreate(_) => EventType::INTEGRATION_CREATE, + Self::IntegrationDelete(_) => EventType::INTEGRATION_DELETE, + Self::IntegrationUpdate(_) => EventType::INTEGRATION_UPDATE, + Self::InteractionCreate(_) => EventType::INTERACTION_CREATE, + Self::InviteCreate(_) => EventType::INVITE_CREATE, + Self::InviteDelete(_) => EventType::INVITE_DELETE, + Self::MemberAdd(_) => EventType::MEMBER_ADD, + Self::MemberRemove(_) => EventType::MEMBER_REMOVE, + Self::MemberUpdate(_) => EventType::MEMBER_UPDATE, + Self::MemberChunk(_) => EventType::MEMBER_CHUNK, + Self::MessageCreate(_) => EventType::MESSAGE_CREATE, + Self::MessageDelete(_) => EventType::MESSAGE_DELETE, + Self::MessageDeleteBulk(_) => EventType::MESSAGE_DELETE_BULK, + Self::MessageUpdate(_) => EventType::MESSAGE_UPDATE, + Self::PresenceUpdate(_) => EventType::PRESENCE_UPDATE, + Self::PresencesReplace => EventType::PRESENCES_REPLACE, + Self::ReactionAdd(_) => EventType::REACTION_ADD, + Self::ReactionRemove(_) => EventType::REACTION_REMOVE, + Self::ReactionRemoveAll(_) => EventType::REACTION_REMOVE_ALL, + Self::ReactionRemoveEmoji(_) => EventType::REACTION_REMOVE_EMOJI, + Self::Ready(_) => EventType::READY, + Self::Resumed => EventType::RESUMED, + Self::RoleCreate(_) => EventType::ROLE_CREATE, + Self::RoleDelete(_) => EventType::ROLE_DELETE, + Self::RoleUpdate(_) => EventType::ROLE_UPDATE, + Self::StageInstanceCreate(_) => EventType::STAGE_INSTANCE_CREATE, + Self::StageInstanceDelete(_) => EventType::STAGE_INSTANCE_DELETE, + Self::StageInstanceUpdate(_) => EventType::STAGE_INSTANCE_UPDATE, + Self::ThreadCreate(_) => EventType::THREAD_CREATE, + Self::ThreadDelete(_) => EventType::THREAD_DELETE, + Self::ThreadListSync(_) => EventType::THREAD_LIST_SYNC, + Self::ThreadMemberUpdate(_) => EventType::THREAD_MEMBER_UPDATE, + Self::ThreadMembersUpdate(_) => EventType::THREAD_MEMBERS_UPDATE, + Self::ThreadUpdate(_) => EventType::THREAD_UPDATE, + Self::TypingStart(_) => EventType::TYPING_START, + Self::UnavailableGuild(_) => EventType::UNAVAILABLE_GUILD, + Self::UserUpdate(_) => EventType::USER_UPDATE, + Self::VoiceServerUpdate(_) => EventType::VOICE_SERVER_UPDATE, + Self::VoiceStateUpdate(_) => EventType::VOICE_STATE_UPDATE, + Self::WebhooksUpdate(_) => EventType::WEBHOOKS_UPDATE, } } } diff --git a/twilight-model/src/gateway/event/gateway.rs b/twilight-model/src/gateway/event/gateway.rs index 1b5d1e19724..e96376a3745 100644 --- a/twilight-model/src/gateway/event/gateway.rs +++ b/twilight-model/src/gateway/event/gateway.rs @@ -3,10 +3,7 @@ use super::{ }; use crate::gateway::payload::incoming::Hello; use serde::{ - de::{ - value::U8Deserializer, DeserializeSeed, Deserializer, Error as DeError, IgnoredAny, - IntoDeserializer, MapAccess, Unexpected, Visitor, - }, + de::{DeserializeSeed, Deserializer, Error as DeError, IgnoredAny, MapAccess, Visitor}, ser::{SerializeStruct, Serializer}, Deserialize, Serialize, }; @@ -236,7 +233,7 @@ impl<'de> Visitor<'de> for GatewayEventVisitor<'_> { V: MapAccess<'de>, { static VALID_OPCODES: &[&str] = &[ - "EVENT", + "DISPATCH", "HEARTBEAT", "HEARTBEAT_ACK", "HELLO", @@ -249,17 +246,10 @@ impl<'de> Visitor<'de> for GatewayEventVisitor<'_> { let _span_enter = span.enter(); tracing::trace!(event_type=?self.2, op=self.0, seq=?self.1); - let op_deser: U8Deserializer = self.0.into_deserializer(); - - let op = OpCode::deserialize(op_deser).ok().ok_or_else(|| { - tracing::trace!(op = self.0, "unknown opcode"); - let unexpected = Unexpected::Unsigned(u64::from(self.0)); - - DeError::invalid_value(unexpected, &"an opcode") - })?; + let op = OpCode::new(self.0); Ok(match op { - OpCode::Dispatch => { + OpCode::DISPATCH => { let t = self .2 .ok_or_else(|| DeError::custom("event type not provided beforehand"))?; @@ -319,7 +309,7 @@ impl<'de> Visitor<'de> for GatewayEventVisitor<'_> { GatewayEvent::Dispatch(s, d) } - OpCode::Heartbeat => { + OpCode::HEARTBEAT => { tracing::trace!("deserializing gateway heartbeat"); let seq = Self::field(&mut map, Field::D)?; tracing::trace!(seq = %seq); @@ -328,14 +318,14 @@ impl<'de> Visitor<'de> for GatewayEventVisitor<'_> { GatewayEvent::Heartbeat(seq) } - OpCode::HeartbeatAck => { + OpCode::HEARTBEAT_ACK => { tracing::trace!("deserializing gateway heartbeat ack"); Self::ignore_all(&mut map)?; GatewayEvent::HeartbeatAck } - OpCode::Hello => { + OpCode::HELLO => { tracing::trace!("deserializing gateway hello"); let hello = Self::field::(&mut map, Field::D)?; tracing::trace!(hello = ?hello); @@ -344,7 +334,7 @@ impl<'de> Visitor<'de> for GatewayEventVisitor<'_> { GatewayEvent::Hello(hello) } - OpCode::InvalidSession => { + OpCode::INVALID_SESSION => { tracing::trace!("deserializing invalid session"); let invalidate = Self::field::(&mut map, Field::D)?; tracing::trace!(invalidate = %invalidate); @@ -353,24 +343,16 @@ impl<'de> Visitor<'de> for GatewayEventVisitor<'_> { GatewayEvent::InvalidateSession(invalidate) } - OpCode::Identify => return Err(DeError::unknown_variant("Identify", VALID_OPCODES)), - OpCode::Reconnect => { + OpCode::RECONNECT => { Self::ignore_all(&mut map)?; GatewayEvent::Reconnect } - OpCode::RequestGuildMembers => { + other => { return Err(DeError::unknown_variant( - "RequestGuildMembers", + other.name().unwrap_or("UNKNOWN"), VALID_OPCODES, - )) - } - OpCode::Resume => return Err(DeError::unknown_variant("Resume", VALID_OPCODES)), - OpCode::PresenceUpdate => { - return Err(DeError::unknown_variant("PresenceUpdate", VALID_OPCODES)) - } - OpCode::VoiceStateUpdate => { - return Err(DeError::unknown_variant("VoiceStateUpdate", VALID_OPCODES)) + )); } }) } @@ -394,12 +376,12 @@ impl Serialize for GatewayEvent { fn serialize(&self, serializer: S) -> Result { const fn opcode(gateway_event: &GatewayEvent) -> OpCode { match gateway_event { - GatewayEvent::Dispatch(_, _) => OpCode::Dispatch, - GatewayEvent::Heartbeat(_) => OpCode::Heartbeat, - GatewayEvent::HeartbeatAck => OpCode::HeartbeatAck, - GatewayEvent::Hello(_) => OpCode::Hello, - GatewayEvent::InvalidateSession(_) => OpCode::InvalidSession, - GatewayEvent::Reconnect => OpCode::Reconnect, + GatewayEvent::Dispatch(_, _) => OpCode::DISPATCH, + GatewayEvent::Heartbeat(_) => OpCode::HEARTBEAT, + GatewayEvent::HeartbeatAck => OpCode::HEARTBEAT_ACK, + GatewayEvent::Hello(_) => OpCode::HELLO, + GatewayEvent::InvalidateSession(_) => OpCode::INVALID_SESSION, + GatewayEvent::Reconnect => OpCode::RECONNECT, } } @@ -781,14 +763,13 @@ mod tests { len: 4, }, Token::Str("t"), - Token::UnitVariant { - name: "EventType", - variant: "GUILD_ROLE_DELETE", - }, + Token::NewtypeStruct { name: "EventType" }, + Token::Str("GUILD_ROLE_DELETE"), Token::Str("s"), Token::U64(2_048), Token::Str("op"), - Token::U8(OpCode::Dispatch as u8), + Token::NewtypeStruct { name: "OpCode" }, + Token::U8(OpCode::DISPATCH.get()), Token::Str("d"), Token::Struct { name: "RoleDelete", @@ -820,7 +801,8 @@ mod tests { Token::Str("s"), Token::None, Token::Str("op"), - Token::U8(OpCode::Heartbeat as u8), + Token::NewtypeStruct { name: "OpCode" }, + Token::U8(OpCode::HEARTBEAT.get()), Token::Str("d"), Token::U64(1024), Token::StructEnd, @@ -842,7 +824,8 @@ mod tests { Token::Str("s"), Token::None, Token::Str("op"), - Token::U8(OpCode::HeartbeatAck as u8), + Token::NewtypeStruct { name: "OpCode" }, + Token::U8(OpCode::HEARTBEAT_ACK.get()), Token::Str("d"), Token::None, Token::StructEnd, @@ -866,7 +849,8 @@ mod tests { Token::Str("s"), Token::None, Token::Str("op"), - Token::U8(OpCode::Hello as u8), + Token::NewtypeStruct { name: "OpCode" }, + Token::U8(OpCode::HELLO.get()), Token::Str("d"), Token::Struct { name: "Hello", @@ -896,7 +880,8 @@ mod tests { Token::Str("s"), Token::None, Token::Str("op"), - Token::U8(OpCode::InvalidSession as u8), + Token::NewtypeStruct { name: "OpCode" }, + Token::U8(OpCode::INVALID_SESSION.get()), Token::Str("d"), Token::Bool(true), Token::StructEnd, @@ -918,7 +903,8 @@ mod tests { Token::Str("s"), Token::None, Token::Str("op"), - Token::U8(OpCode::Reconnect as u8), + Token::NewtypeStruct { name: "OpCode" }, + Token::U8(OpCode::RECONNECT.get()), Token::Str("d"), Token::None, Token::StructEnd, diff --git a/twilight-model/src/gateway/event/kind.rs b/twilight-model/src/gateway/event/kind.rs index 7e03c9bc982..688c1d33581 100644 --- a/twilight-model/src/gateway/event/kind.rs +++ b/twilight-model/src/gateway/event/kind.rs @@ -1,244 +1,234 @@ +use crate::util::known_string::KnownString; use serde::{Deserialize, Serialize}; /// The type of an event. -#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)] -#[serde(rename_all = "SCREAMING_SNAKE_CASE")] -pub enum EventType { - AutoModerationActionExecution, - AutoModerationRuleCreate, - AutoModerationRuleDelete, - AutoModerationRuleUpdate, - #[serde(rename = "GUILD_BAN_ADD")] - BanAdd, - #[serde(rename = "GUILD_BAN_REMOVE")] - BanRemove, - ChannelCreate, - ChannelDelete, - ChannelPinsUpdate, - ChannelUpdate, - #[serde(rename = "APPLICATION_COMMAND_PERMISSIONS_UPDATE")] - CommandPermissionsUpdate, - GatewayClose, - GatewayHeartbeat, - GatewayHeartbeatAck, - GatewayHello, - GatewayInvalidateSession, - GatewayReconnect, - GiftCodeUpdate, - GuildAuditLogEntryCreate, - GuildCreate, - GuildDelete, - GuildEmojisUpdate, - GuildIntegrationsUpdate, - GuildScheduledEventCreate, - GuildScheduledEventDelete, - GuildScheduledEventUpdate, - GuildScheduledEventUserAdd, - GuildScheduledEventUserRemove, - GuildStickersUpdate, - GuildUpdate, - IntegrationCreate, - IntegrationDelete, - IntegrationUpdate, - InteractionCreate, - InviteCreate, - InviteDelete, - #[serde(rename = "GUILD_MEMBER_ADD")] - MemberAdd, - #[serde(rename = "GUILD_MEMBERS_CHUNK")] - MemberChunk, - #[serde(rename = "GUILD_MEMBER_REMOVE")] - MemberRemove, - #[serde(rename = "GUILD_MEMBER_UPDATE")] - MemberUpdate, - MessageCreate, - MessageDelete, - MessageDeleteBulk, - MessageUpdate, - PresenceUpdate, - PresencesReplace, - #[serde(rename = "MESSAGE_REACTION_ADD")] - ReactionAdd, - #[serde(rename = "MESSAGE_REACTION_REMOVE")] - ReactionRemove, - #[serde(rename = "MESSAGE_REACTION_REMOVE_ALL")] - ReactionRemoveAll, - #[serde(rename = "MESSAGE_REACTION_REMOVE_EMOJI")] - ReactionRemoveEmoji, - Ready, - Resumed, - #[serde(rename = "GUILD_ROLE_CREATE")] - RoleCreate, - #[serde(rename = "GUILD_ROLE_DELETE")] - RoleDelete, - #[serde(rename = "GUILD_ROLE_UPDATE")] - RoleUpdate, - StageInstanceCreate, - StageInstanceDelete, - StageInstanceUpdate, - ThreadCreate, - ThreadDelete, - ThreadListSync, - ThreadMemberUpdate, - ThreadMembersUpdate, - ThreadUpdate, - TypingStart, - UnavailableGuild, - UserUpdate, - VoiceServerUpdate, - VoiceStateUpdate, - WebhooksUpdate, -} +#[derive(Clone, Copy, Deserialize, Eq, Hash, PartialEq, Serialize)] +pub struct EventType(KnownString<64>); impl EventType { + pub const AUTO_MODERATION_ACTION_EXECUTION: Self = + Self::from_bytes(b"AUTO_MODERATION_ACTION_EXECUTION"); + + pub const AUTO_MODERATION_RULE_CREATE: Self = Self::from_bytes(b"AUTO_MODERATION_RULE_CREATE"); + + pub const AUTO_MODERATION_RULE_DELETE: Self = Self::from_bytes(b"AUTO_MODERATION_RULE_DELETE"); + + pub const AUTO_MODERATION_RULE_UPDATE: Self = Self::from_bytes(b"AUTO_MODERATION_RULE_UPDATE"); + + pub const BAN_ADD: Self = Self::from_bytes(b"GUILD_BAN_ADD"); + + pub const BAN_REMOVE: Self = Self::from_bytes(b"GUILD_BAN_REMOVE"); + + pub const CHANNEL_CREATE: Self = Self::from_bytes(b"CHANNEL_CREATE"); + + pub const CHANNEL_DELETE: Self = Self::from_bytes(b"CHANNEL_DELETE"); + + pub const CHANNEL_PINS_UPDATE: Self = Self::from_bytes(b"CHANNEL_PINS_UPDATE"); + + pub const CHANNEL_UPDATE: Self = Self::from_bytes(b"CHANNEL_UPDATE"); + + pub const COMMAND_PERMISSIONS_UPDATE: Self = + Self::from_bytes(b"APPLICATION_COMMAND_PERMISSIONS_UPDATE"); + + pub const GATEWAY_CLOSE: Self = Self::from_bytes(b"GATEWAY_CLOSE"); + + pub const GATEWAY_HEARTBEAT: Self = Self::from_bytes(b"GATEWAY_HEARTBEAT"); + + pub const GATEWAY_HEARTBEAT_ACK: Self = Self::from_bytes(b"GATEWAY_HEARTBEAT_ACK"); + + pub const GATEWAY_HELLO: Self = Self::from_bytes(b"GATEWAY_HELLO"); + + pub const GATEWAY_INVALIDATE_SESSION: Self = Self::from_bytes(b"GATEWAY_INVALIDATE_SESSION"); + + pub const GATEWAY_RECONNECT: Self = Self::from_bytes(b"GATEWAY_RECONNECT"); + + pub const GIFT_CODE_UPDATE: Self = Self::from_bytes(b"GIFT_CODE_UPDATE"); + + pub const GUILD_AUDIT_LOG_ENTRY_CREATE: Self = + Self::from_bytes(b"GUILD_AUDIT_LOG_ENTRY_CREATE"); + + pub const GUILD_CREATE: Self = Self::from_bytes(b"GUILD_CREATE"); + + pub const GUILD_DELETE: Self = Self::from_bytes(b"GUILD_DELETE"); + + pub const GUILD_EMOJIS_UPDATE: Self = Self::from_bytes(b"GUILD_EMOJIS_UPDATE"); + + pub const GUILD_INTEGRATIONS_UPDATE: Self = Self::from_bytes(b"GUILD_INTEGRATIONS_UPDATE"); + + pub const GUILD_SCHEDULED_EVENT_CREATE: Self = + Self::from_bytes(b"GUILD_SCHEDULED_EVENT_CREATE"); + + pub const GUILD_SCHEDULED_EVENT_DELETE: Self = + Self::from_bytes(b"GUILD_SCHEDULED_EVENT_DELETE"); + + pub const GUILD_SCHEDULED_EVENT_UPDATE: Self = + Self::from_bytes(b"GUILD_SCHEDULED_EVENT_UPDATE"); + + pub const GUILD_SCHEDULED_EVENT_USER_ADD: Self = + Self::from_bytes(b"GUILD_SCHEDULED_EVENT_USER_ADD"); + + pub const GUILD_SCHEDULED_EVENT_USER_REMOVE: Self = + Self::from_bytes(b"GUILD_SCHEDULED_EVENT_USER_REMOVE"); + + pub const GUILD_STICKERS_UPDATE: Self = Self::from_bytes(b"GUILD_STICKERS_UPDATE"); + + pub const GUILD_UPDATE: Self = Self::from_bytes(b"GUILD_UPDATE"); + + pub const INTEGRATION_CREATE: Self = Self::from_bytes(b"INTEGRATION_CREATE"); + + pub const INTEGRATION_DELETE: Self = Self::from_bytes(b"INTEGRATION_DELETE"); + + pub const INTEGRATION_UPDATE: Self = Self::from_bytes(b"INTEGRATION_UPDATE"); + + pub const INTERACTION_CREATE: Self = Self::from_bytes(b"INTERACTION_CREATE"); + + pub const INVITE_CREATE: Self = Self::from_bytes(b"INVITE_CREATE"); + + pub const INVITE_DELETE: Self = Self::from_bytes(b"INVITE_DELETE"); + + pub const MEMBER_ADD: Self = Self::from_bytes(b"GUILD_MEMBER_ADD"); + + pub const MEMBER_CHUNK: Self = Self::from_bytes(b"GUILD_MEMBERS_CHUNK"); + + pub const MEMBER_REMOVE: Self = Self::from_bytes(b"GUILD_MEMBER_REMOVE"); + + pub const MEMBER_UPDATE: Self = Self::from_bytes(b"GUILD_MEMBER_UPDATE"); + + pub const MESSAGE_CREATE: Self = Self::from_bytes(b"MESSAGE_CREATE"); + + pub const MESSAGE_DELETE: Self = Self::from_bytes(b"MESSAGE_DELETE"); + + pub const MESSAGE_DELETE_BULK: Self = Self::from_bytes(b"MESSAGE_DELETE_BULK"); + + pub const MESSAGE_UPDATE: Self = Self::from_bytes(b"MESSAGE_UPDATE"); + + pub const PRESENCE_UPDATE: Self = Self::from_bytes(b"PRESENCE_UPDATE"); + + pub const PRESENCES_REPLACE: Self = Self::from_bytes(b"PRESENCES_REPLACE"); + + pub const REACTION_ADD: Self = Self::from_bytes(b"MESSAGE_REACTION_ADD"); + + pub const REACTION_REMOVE: Self = Self::from_bytes(b"MESSAGE_REACTION_REMOVE"); + + pub const REACTION_REMOVE_ALL: Self = Self::from_bytes(b"MESSAGE_REACTION_REMOVE_ALL"); + + pub const REACTION_REMOVE_EMOJI: Self = Self::from_bytes(b"MESSAGE_REACTION_REMOVE_EMOJI"); + + pub const READY: Self = Self::from_bytes(b"READY"); + + pub const RESUMED: Self = Self::from_bytes(b"RESUMED"); + + pub const ROLE_CREATE: Self = Self::from_bytes(b"GUILD_ROLE_CREATE"); + + pub const ROLE_DELETE: Self = Self::from_bytes(b"GUILD_ROLE_DELETE"); + + pub const ROLE_UPDATE: Self = Self::from_bytes(b"GUILD_ROLE_UPDATE"); + + pub const STAGE_INSTANCE_CREATE: Self = Self::from_bytes(b"STAGE_INSTANCE_CREATE"); + + pub const STAGE_INSTANCE_DELETE: Self = Self::from_bytes(b"STAGE_INSTANCE_DELETE"); + + pub const STAGE_INSTANCE_UPDATE: Self = Self::from_bytes(b"STAGE_INSTANCE_UPDATE"); + + pub const THREAD_CREATE: Self = Self::from_bytes(b"THREAD_CREATE"); + + pub const THREAD_DELETE: Self = Self::from_bytes(b"THREAD_DELETE"); + + pub const THREAD_LIST_SYNC: Self = Self::from_bytes(b"THREAD_LIST_SYNC"); + + pub const THREAD_MEMBER_UPDATE: Self = Self::from_bytes(b"THREAD_MEMBER_UPDATE"); + + pub const THREAD_MEMBERS_UPDATE: Self = Self::from_bytes(b"THREAD_MEMBERS_UPDATE"); + + pub const THREAD_UPDATE: Self = Self::from_bytes(b"THREAD_UPDATE"); + + pub const TYPING_START: Self = Self::from_bytes(b"TYPING_START"); + + pub const UNAVAILABLE_GUILD: Self = Self::from_bytes(b"UNAVAILABLE_GUILD"); + + pub const USER_UPDATE: Self = Self::from_bytes(b"USER_UPDATE"); + + pub const VOICE_SERVER_UPDATE: Self = Self::from_bytes(b"VOICE_SERVER_UPDATE"); + + pub const VOICE_STATE_UPDATE: Self = Self::from_bytes(b"VOICE_STATE_UPDATE"); + + pub const WEBHOOKS_UPDATE: Self = Self::from_bytes(b"WEBHOOKS_UPDATE"); + + /// Name of the associated constant. + /// + /// Returns `None` if the value doesn't have a defined constant. pub const fn name(self) -> Option<&'static str> { match self { - Self::AutoModerationActionExecution => Some("AUTO_MODERATION_ACTION_EXECUTION"), - Self::AutoModerationRuleCreate => Some("AUTO_MODERATION_RULE_CREATE"), - Self::AutoModerationRuleDelete => Some("AUTO_MODERATION_RULE_DELETE"), - Self::AutoModerationRuleUpdate => Some("AUTO_MODERATION_RULE_UPDATE"), - Self::BanAdd => Some("GUILD_BAN_ADD"), - Self::BanRemove => Some("GUILD_BAN_REMOVE"), - Self::ChannelCreate => Some("CHANNEL_CREATE"), - Self::ChannelDelete => Some("CHANNEL_DELETE"), - Self::ChannelPinsUpdate => Some("CHANNEL_PINS_UPDATE"), - Self::ChannelUpdate => Some("CHANNEL_UPDATE"), - Self::CommandPermissionsUpdate => Some("APPLICATION_COMMAND_PERMISSIONS_UPDATE"), - Self::GiftCodeUpdate => Some("GIFT_CODE_UPDATE"), - Self::GuildAuditLogEntryCreate => Some("GUILD_AUDIT_LOG_ENTRY_CREATE"), - Self::GuildCreate => Some("GUILD_CREATE"), - Self::GuildDelete => Some("GUILD_DELETE"), - Self::GuildEmojisUpdate => Some("GUILD_EMOJIS_UPDATE"), - Self::GuildIntegrationsUpdate => Some("GUILD_INTEGRATIONS_UPDATE"), - Self::GuildScheduledEventCreate => Some("GUILD_SCHEDULED_EVENT_CREATE"), - Self::GuildScheduledEventDelete => Some("GUILD_SCHEDULED_EVENT_DELETE"), - Self::GuildScheduledEventUpdate => Some("GUILD_SCHEDULED_EVENT_UPDATE"), - Self::GuildScheduledEventUserAdd => Some("GUILD_SCHEDULED_EVENT_USER_ADD"), - Self::GuildScheduledEventUserRemove => Some("GUILD_SCHEDULED_EVENT_USER_REMOVE"), - Self::GuildStickersUpdate => Some("GUILD_STICKERS_UPDATE"), - Self::GuildUpdate => Some("GUILD_UPDATE"), - Self::IntegrationCreate => Some("INTEGRATION_CREATE"), - Self::IntegrationDelete => Some("INTEGRATION_DELETE"), - Self::IntegrationUpdate => Some("INTEGRATION_UPDATE"), - Self::InteractionCreate => Some("INTERACTION_CREATE"), - Self::InviteCreate => Some("INVITE_CREATE"), - Self::InviteDelete => Some("INVITE_DELETE"), - Self::MemberAdd => Some("GUILD_MEMBER_ADD"), - Self::MemberChunk => Some("GUILD_MEMBERS_CHUNK"), - Self::MemberRemove => Some("GUILD_MEMBER_REMOVE"), - Self::MemberUpdate => Some("GUILD_MEMBER_UPDATE"), - Self::MessageCreate => Some("MESSAGE_CREATE"), - Self::MessageDelete => Some("MESSAGE_DELETE"), - Self::MessageDeleteBulk => Some("MESSAGE_DELETE_BULK"), - Self::MessageUpdate => Some("MESSAGE_UPDATE"), - Self::PresencesReplace => Some("PRESENCES_REPLACE"), - Self::PresenceUpdate => Some("PRESENCE_UPDATE"), - Self::ReactionAdd => Some("MESSAGE_REACTION_ADD"), - Self::ReactionRemove => Some("MESSAGE_REACTION_REMOVE"), - Self::ReactionRemoveAll => Some("MESSAGE_REACTION_REMOVE_ALL"), - Self::ReactionRemoveEmoji => Some("MESSAGE_REACTION_REMOVE_EMOJI"), - Self::Ready => Some("READY"), - Self::Resumed => Some("RESUMED"), - Self::RoleCreate => Some("GUILD_ROLE_CREATE"), - Self::RoleDelete => Some("GUILD_ROLE_DELETE"), - Self::RoleUpdate => Some("GUILD_ROLE_UPDATE"), - Self::StageInstanceCreate => Some("STAGE_INSTANCE_CREATE"), - Self::StageInstanceDelete => Some("STAGE_INSTANCE_DELETE"), - Self::StageInstanceUpdate => Some("STAGE_INSTANCE_UPDATE"), - Self::ThreadCreate => Some("THREAD_CREATE"), - Self::ThreadDelete => Some("THREAD_DELETE"), - Self::ThreadListSync => Some("THREAD_LIST_SYNC"), - Self::ThreadMembersUpdate => Some("THREAD_MEMBERS_UPDATE"), - Self::ThreadMemberUpdate => Some("THREAD_MEMBER_UPDATE"), - Self::ThreadUpdate => Some("THREAD_UPDATE"), - Self::TypingStart => Some("TYPING_START"), - Self::UnavailableGuild => Some("UNAVAILABLE_GUILD"), - Self::UserUpdate => Some("USER_UPDATE"), - Self::VoiceServerUpdate => Some("VOICE_SERVER_UPDATE"), - Self::VoiceStateUpdate => Some("VOICE_STATE_UPDATE"), - Self::WebhooksUpdate => Some("WEBHOOKS_UPDATE"), - Self::GatewayClose - | Self::GatewayHeartbeat - | Self::GatewayHeartbeatAck - | Self::GatewayHello - | Self::GatewayInvalidateSession - | Self::GatewayReconnect => None, + Self::AUTO_MODERATION_ACTION_EXECUTION => Some("AUTO_MODERATION_ACTION_EXECUTION"), + Self::AUTO_MODERATION_RULE_CREATE => Some("AUTO_MODERATION_RULE_CREATE"), + Self::AUTO_MODERATION_RULE_DELETE => Some("AUTO_MODERATION_RULE_DELETE"), + Self::AUTO_MODERATION_RULE_UPDATE => Some("AUTO_MODERATION_RULE_UPDATE"), + Self::BAN_ADD => Some("GUILD_BAN_ADD"), + Self::BAN_REMOVE => Some("GUILD_BAN_REMOVE"), + Self::CHANNEL_CREATE => Some("CHANNEL_CREATE"), + Self::CHANNEL_DELETE => Some("CHANNEL_DELETE"), + Self::CHANNEL_PINS_UPDATE => Some("CHANNEL_PINS_UPDATE"), + Self::CHANNEL_UPDATE => Some("CHANNEL_UPDATE"), + Self::COMMAND_PERMISSIONS_UPDATE => Some("APPLICATION_COMMAND_PERMISSIONS_UPDATE"), + Self::GIFT_CODE_UPDATE => Some("GIFT_CODE_UPDATE"), + Self::GUILD_AUDIT_LOG_ENTRY_CREATE => Some("GUILD_AUDIT_LOG_ENTRY_CREATE"), + Self::GUILD_CREATE => Some("GUILD_CREATE"), + Self::GUILD_DELETE => Some("GUILD_DELETE"), + Self::GUILD_EMOJIS_UPDATE => Some("GUILD_EMOJIS_UPDATE"), + Self::GUILD_INTEGRATIONS_UPDATE => Some("GUILD_INTEGRATIONS_UPDATE"), + Self::GUILD_SCHEDULED_EVENT_CREATE => Some("GUILD_SCHEDULED_EVENT_CREATE"), + Self::GUILD_SCHEDULED_EVENT_DELETE => Some("GUILD_SCHEDULED_EVENT_DELETE"), + Self::GUILD_SCHEDULED_EVENT_UPDATE => Some("GUILD_SCHEDULED_EVENT_UPDATE"), + Self::GUILD_SCHEDULED_EVENT_USER_ADD => Some("GUILD_SCHEDULED_EVENT_USER_ADD"), + Self::GUILD_SCHEDULED_EVENT_USER_REMOVE => Some("GUILD_SCHEDULED_EVENT_USER_REMOVE"), + Self::GUILD_STICKERS_UPDATE => Some("GUILD_STICKERS_UPDATE"), + Self::GUILD_UPDATE => Some("GUILD_UPDATE"), + Self::INTEGRATION_CREATE => Some("INTEGRATION_CREATE"), + Self::INTEGRATION_DELETE => Some("INTEGRATION_DELETE"), + Self::INTEGRATION_UPDATE => Some("INTEGRATION_UPDATE"), + Self::INTERACTION_CREATE => Some("INTERACTION_CREATE"), + Self::INVITE_CREATE => Some("INVITE_CREATE"), + Self::INVITE_DELETE => Some("INVITE_DELETE"), + Self::MEMBER_ADD => Some("GUILD_MEMBER_ADD"), + Self::MEMBER_CHUNK => Some("GUILD_MEMBERS_CHUNK"), + Self::MEMBER_REMOVE => Some("GUILD_MEMBER_REMOVE"), + Self::MEMBER_UPDATE => Some("GUILD_MEMBER_UPDATE"), + Self::MESSAGE_CREATE => Some("MESSAGE_CREATE"), + Self::MESSAGE_DELETE => Some("MESSAGE_DELETE"), + Self::MESSAGE_DELETE_BULK => Some("MESSAGE_DELETE_BULK"), + Self::MESSAGE_UPDATE => Some("MESSAGE_UPDATE"), + Self::PRESENCES_REPLACE => Some("PRESENCES_REPLACE"), + Self::PRESENCE_UPDATE => Some("PRESENCE_UPDATE"), + Self::REACTION_ADD => Some("MESSAGE_REACTION_ADD"), + Self::REACTION_REMOVE => Some("MESSAGE_REACTION_REMOVE"), + Self::REACTION_REMOVE_ALL => Some("MESSAGE_REACTION_REMOVE_ALL"), + Self::REACTION_REMOVE_EMOJI => Some("MESSAGE_REACTION_REMOVE_EMOJI"), + Self::READY => Some("READY"), + Self::RESUMED => Some("RESUMED"), + Self::ROLE_CREATE => Some("GUILD_ROLE_CREATE"), + Self::ROLE_DELETE => Some("GUILD_ROLE_DELETE"), + Self::ROLE_UPDATE => Some("GUILD_ROLE_UPDATE"), + Self::STAGE_INSTANCE_CREATE => Some("STAGE_INSTANCE_CREATE"), + Self::STAGE_INSTANCE_DELETE => Some("STAGE_INSTANCE_DELETE"), + Self::STAGE_INSTANCE_UPDATE => Some("STAGE_INSTANCE_UPDATE"), + Self::THREAD_CREATE => Some("THREAD_CREATE"), + Self::THREAD_DELETE => Some("THREAD_DELETE"), + Self::THREAD_LIST_SYNC => Some("THREAD_LIST_SYNC"), + Self::THREAD_MEMBERS_UPDATE => Some("THREAD_MEMBERS_UPDATE"), + Self::THREAD_MEMBER_UPDATE => Some("THREAD_MEMBER_UPDATE"), + Self::THREAD_UPDATE => Some("THREAD_UPDATE"), + Self::TYPING_START => Some("TYPING_START"), + Self::UNAVAILABLE_GUILD => Some("UNAVAILABLE_GUILD"), + Self::USER_UPDATE => Some("USER_UPDATE"), + Self::VOICE_SERVER_UPDATE => Some("VOICE_SERVER_UPDATE"), + Self::VOICE_STATE_UPDATE => Some("VOICE_STATE_UPDATE"), + Self::WEBHOOKS_UPDATE => Some("WEBHOOKS_UPDATE"), + _ => None, } } } -impl<'a> TryFrom<&'a str> for EventType { - type Error = &'a str; - - fn try_from(event_type: &'a str) -> Result { - match event_type { - "AUTO_MODERATION_ACTION_EXECUTION" => Ok(Self::AutoModerationActionExecution), - "AUTO_MODERATION_RULE_CREATE" => Ok(Self::AutoModerationRuleCreate), - "AUTO_MODERATION_RULE_DELETE" => Ok(Self::AutoModerationRuleDelete), - "AUTO_MODERATION_RULE_UPDATE" => Ok(Self::AutoModerationRuleUpdate), - "GUILD_AUDIT_LOG_ENTRY_CREATE" => Ok(Self::GuildAuditLogEntryCreate), - "GUILD_BAN_ADD" => Ok(Self::BanAdd), - "GUILD_BAN_REMOVE" => Ok(Self::BanRemove), - "CHANNEL_CREATE" => Ok(Self::ChannelCreate), - "CHANNEL_DELETE" => Ok(Self::ChannelDelete), - "CHANNEL_PINS_UPDATE" => Ok(Self::ChannelPinsUpdate), - "CHANNEL_UPDATE" => Ok(Self::ChannelUpdate), - "APPLICATION_COMMAND_PERMISSIONS_UPDATE" => Ok(Self::CommandPermissionsUpdate), - "GIFT_CODE_UPDATE" => Ok(Self::GiftCodeUpdate), - "GUILD_CREATE" => Ok(Self::GuildCreate), - "GUILD_DELETE" => Ok(Self::GuildDelete), - "GUILD_EMOJIS_UPDATE" => Ok(Self::GuildEmojisUpdate), - "GUILD_INTEGRATIONS_UPDATE" => Ok(Self::GuildIntegrationsUpdate), - "GUILD_SCHEDULED_EVENT_CREATE" => Ok(Self::GuildScheduledEventCreate), - "GUILD_SCHEDULED_EVENT_DELETE" => Ok(Self::GuildScheduledEventDelete), - "GUILD_SCHEDULED_EVENT_UPDATE" => Ok(Self::GuildScheduledEventUpdate), - "GUILD_SCHEDULED_EVENT_USER_ADD" => Ok(Self::GuildScheduledEventUserAdd), - "GUILD_SCHEDULED_EVENT_USER_REMOVE" => Ok(Self::GuildScheduledEventUserRemove), - "GUILD_UPDATE" => Ok(Self::GuildUpdate), - "INTEGRATION_CREATE" => Ok(Self::IntegrationCreate), - "INTEGRATION_DELETE" => Ok(Self::IntegrationDelete), - "INTEGRATION_UPDATE" => Ok(Self::IntegrationUpdate), - "INTERACTION_CREATE" => Ok(Self::InteractionCreate), - "INVITE_CREATE" => Ok(Self::InviteCreate), - "INVITE_DELETE" => Ok(Self::InviteDelete), - "GUILD_MEMBER_ADD" => Ok(Self::MemberAdd), - "GUILD_MEMBER_REMOVE" => Ok(Self::MemberRemove), - "GUILD_MEMBER_UPDATE" => Ok(Self::MemberUpdate), - "GUILD_MEMBERS_CHUNK" => Ok(Self::MemberChunk), - "MESSAGE_CREATE" => Ok(Self::MessageCreate), - "MESSAGE_DELETE" => Ok(Self::MessageDelete), - "MESSAGE_DELETE_BULK" => Ok(Self::MessageDeleteBulk), - "MESSAGE_UPDATE" => Ok(Self::MessageUpdate), - "PRESENCE_UPDATE" => Ok(Self::PresenceUpdate), - "PRESENCES_REPLACE" => Ok(Self::PresencesReplace), - "MESSAGE_REACTION_ADD" => Ok(Self::ReactionAdd), - "MESSAGE_REACTION_REMOVE" => Ok(Self::ReactionRemove), - "MESSAGE_REACTION_REMOVE_ALL" => Ok(Self::ReactionRemoveAll), - "MESSAGE_REACTION_REMOVE_EMOJI" => Ok(Self::ReactionRemoveEmoji), - "READY" => Ok(Self::Ready), - "RESUMED" => Ok(Self::Resumed), - "GUILD_ROLE_CREATE" => Ok(Self::RoleCreate), - "GUILD_ROLE_DELETE" => Ok(Self::RoleDelete), - "GUILD_ROLE_UPDATE" => Ok(Self::RoleUpdate), - "STAGE_INSTANCE_CREATE" => Ok(Self::StageInstanceCreate), - "STAGE_INSTANCE_DELETE" => Ok(Self::StageInstanceDelete), - "STAGE_INSTANCE_UPDATE" => Ok(Self::StageInstanceUpdate), - "THREAD_CREATE" => Ok(Self::ThreadCreate), - "THREAD_DELETE" => Ok(Self::ThreadDelete), - "THREAD_LIST_SYNC" => Ok(Self::ThreadListSync), - "THREAD_MEMBER_UPDATE" => Ok(Self::ThreadMemberUpdate), - "THREAD_MEMBERS_UPDATE" => Ok(Self::ThreadMembersUpdate), - "THREAD_UPDATE" => Ok(Self::ThreadUpdate), - "TYPING_START" => Ok(Self::TypingStart), - "UNAVAILABLE_GUILD" => Ok(Self::UnavailableGuild), - "USER_UPDATE" => Ok(Self::UserUpdate), - "VOICE_SERVER_UPDATE" => Ok(Self::VoiceServerUpdate), - "VOICE_STATE_UPDATE" => Ok(Self::VoiceStateUpdate), - "WEBHOOKS_UPDATE" => Ok(Self::WebhooksUpdate), - _ => Err(event_type), - } - } -} +impl_typed!(EventType, String); #[cfg(test)] mod tests { @@ -248,10 +238,7 @@ mod tests { fn assert_variant(kind: EventType, name: &'static str) { serde_test::assert_tokens( &kind, - &[Token::UnitVariant { - name: "EventType", - variant: name, - }], + &[Token::NewtypeStruct { name: "EventType" }, Token::Str(name)], ); } @@ -259,115 +246,118 @@ mod tests { #[test] fn variants() { assert_variant( - EventType::AutoModerationActionExecution, + EventType::AUTO_MODERATION_ACTION_EXECUTION, "AUTO_MODERATION_ACTION_EXECUTION", ); assert_variant( - EventType::AutoModerationRuleCreate, + EventType::AUTO_MODERATION_RULE_CREATE, "AUTO_MODERATION_RULE_CREATE", ); assert_variant( - EventType::AutoModerationRuleDelete, + EventType::AUTO_MODERATION_RULE_DELETE, "AUTO_MODERATION_RULE_DELETE", ); assert_variant( - EventType::AutoModerationRuleUpdate, + EventType::AUTO_MODERATION_RULE_UPDATE, "AUTO_MODERATION_RULE_UPDATE", ); - assert_variant(EventType::BanAdd, "GUILD_BAN_ADD"); - assert_variant(EventType::BanRemove, "GUILD_BAN_REMOVE"); - assert_variant(EventType::ChannelCreate, "CHANNEL_CREATE"); - assert_variant(EventType::ChannelDelete, "CHANNEL_DELETE"); - assert_variant(EventType::ChannelPinsUpdate, "CHANNEL_PINS_UPDATE"); - assert_variant(EventType::ChannelUpdate, "CHANNEL_UPDATE"); + assert_variant(EventType::BAN_ADD, "GUILD_BAN_ADD"); + assert_variant(EventType::BAN_REMOVE, "GUILD_BAN_REMOVE"); + assert_variant(EventType::CHANNEL_CREATE, "CHANNEL_CREATE"); + assert_variant(EventType::CHANNEL_DELETE, "CHANNEL_DELETE"); + assert_variant(EventType::CHANNEL_PINS_UPDATE, "CHANNEL_PINS_UPDATE"); + assert_variant(EventType::CHANNEL_UPDATE, "CHANNEL_UPDATE"); assert_variant( - EventType::CommandPermissionsUpdate, + EventType::COMMAND_PERMISSIONS_UPDATE, "APPLICATION_COMMAND_PERMISSIONS_UPDATE", ); - assert_variant(EventType::GatewayClose, "GATEWAY_CLOSE"); - assert_variant(EventType::GatewayHeartbeat, "GATEWAY_HEARTBEAT"); - assert_variant(EventType::GatewayHeartbeatAck, "GATEWAY_HEARTBEAT_ACK"); - assert_variant(EventType::GatewayHello, "GATEWAY_HELLO"); + assert_variant(EventType::GATEWAY_CLOSE, "GATEWAY_CLOSE"); + assert_variant(EventType::GATEWAY_HEARTBEAT, "GATEWAY_HEARTBEAT"); + assert_variant(EventType::GATEWAY_HEARTBEAT_ACK, "GATEWAY_HEARTBEAT_ACK"); + assert_variant(EventType::GATEWAY_HELLO, "GATEWAY_HELLO"); assert_variant( - EventType::GatewayInvalidateSession, + EventType::GATEWAY_INVALIDATE_SESSION, "GATEWAY_INVALIDATE_SESSION", ); - assert_variant(EventType::GatewayReconnect, "GATEWAY_RECONNECT"); - assert_variant(EventType::GiftCodeUpdate, "GIFT_CODE_UPDATE"); + assert_variant(EventType::GATEWAY_RECONNECT, "GATEWAY_RECONNECT"); + assert_variant(EventType::GIFT_CODE_UPDATE, "GIFT_CODE_UPDATE"); assert_variant( - EventType::GuildAuditLogEntryCreate, + EventType::GUILD_AUDIT_LOG_ENTRY_CREATE, "GUILD_AUDIT_LOG_ENTRY_CREATE", ); - assert_variant(EventType::GuildCreate, "GUILD_CREATE"); - assert_variant(EventType::GuildDelete, "GUILD_DELETE"); - assert_variant(EventType::GuildEmojisUpdate, "GUILD_EMOJIS_UPDATE"); + assert_variant(EventType::GUILD_CREATE, "GUILD_CREATE"); + assert_variant(EventType::GUILD_DELETE, "GUILD_DELETE"); + assert_variant(EventType::GUILD_EMOJIS_UPDATE, "GUILD_EMOJIS_UPDATE"); assert_variant( - EventType::GuildIntegrationsUpdate, + EventType::GUILD_INTEGRATIONS_UPDATE, "GUILD_INTEGRATIONS_UPDATE", ); assert_variant( - EventType::GuildScheduledEventCreate, + EventType::GUILD_SCHEDULED_EVENT_CREATE, "GUILD_SCHEDULED_EVENT_CREATE", ); assert_variant( - EventType::GuildScheduledEventDelete, + EventType::GUILD_SCHEDULED_EVENT_DELETE, "GUILD_SCHEDULED_EVENT_DELETE", ); assert_variant( - EventType::GuildScheduledEventUpdate, + EventType::GUILD_SCHEDULED_EVENT_UPDATE, "GUILD_SCHEDULED_EVENT_UPDATE", ); assert_variant( - EventType::GuildScheduledEventUserAdd, + EventType::GUILD_SCHEDULED_EVENT_USER_ADD, "GUILD_SCHEDULED_EVENT_USER_ADD", ); assert_variant( - EventType::GuildScheduledEventUserRemove, + EventType::GUILD_SCHEDULED_EVENT_USER_REMOVE, "GUILD_SCHEDULED_EVENT_USER_REMOVE", ); - assert_variant(EventType::GuildUpdate, "GUILD_UPDATE"); - assert_variant(EventType::IntegrationCreate, "INTEGRATION_CREATE"); - assert_variant(EventType::IntegrationDelete, "INTEGRATION_DELETE"); - assert_variant(EventType::IntegrationUpdate, "INTEGRATION_UPDATE"); - assert_variant(EventType::InteractionCreate, "INTERACTION_CREATE"); - assert_variant(EventType::InviteCreate, "INVITE_CREATE"); - assert_variant(EventType::InviteDelete, "INVITE_DELETE"); - assert_variant(EventType::MemberAdd, "GUILD_MEMBER_ADD"); - assert_variant(EventType::MemberChunk, "GUILD_MEMBERS_CHUNK"); - assert_variant(EventType::MemberRemove, "GUILD_MEMBER_REMOVE"); - assert_variant(EventType::MemberUpdate, "GUILD_MEMBER_UPDATE"); - assert_variant(EventType::MessageCreate, "MESSAGE_CREATE"); - assert_variant(EventType::MessageDelete, "MESSAGE_DELETE"); - assert_variant(EventType::MessageDeleteBulk, "MESSAGE_DELETE_BULK"); - assert_variant(EventType::MessageUpdate, "MESSAGE_UPDATE"); - assert_variant(EventType::PresenceUpdate, "PRESENCE_UPDATE"); - assert_variant(EventType::PresencesReplace, "PRESENCES_REPLACE"); - assert_variant(EventType::ReactionAdd, "MESSAGE_REACTION_ADD"); - assert_variant(EventType::ReactionRemove, "MESSAGE_REACTION_REMOVE"); - assert_variant(EventType::ReactionRemoveAll, "MESSAGE_REACTION_REMOVE_ALL"); + assert_variant(EventType::GUILD_UPDATE, "GUILD_UPDATE"); + assert_variant(EventType::INTEGRATION_CREATE, "INTEGRATION_CREATE"); + assert_variant(EventType::INTEGRATION_DELETE, "INTEGRATION_DELETE"); + assert_variant(EventType::INTEGRATION_UPDATE, "INTEGRATION_UPDATE"); + assert_variant(EventType::INTERACTION_CREATE, "INTERACTION_CREATE"); + assert_variant(EventType::INVITE_CREATE, "INVITE_CREATE"); + assert_variant(EventType::INVITE_DELETE, "INVITE_DELETE"); + assert_variant(EventType::MEMBER_ADD, "GUILD_MEMBER_ADD"); + assert_variant(EventType::MEMBER_CHUNK, "GUILD_MEMBERS_CHUNK"); + assert_variant(EventType::MEMBER_REMOVE, "GUILD_MEMBER_REMOVE"); + assert_variant(EventType::MEMBER_UPDATE, "GUILD_MEMBER_UPDATE"); + assert_variant(EventType::MESSAGE_CREATE, "MESSAGE_CREATE"); + assert_variant(EventType::MESSAGE_DELETE, "MESSAGE_DELETE"); + assert_variant(EventType::MESSAGE_DELETE_BULK, "MESSAGE_DELETE_BULK"); + assert_variant(EventType::MESSAGE_UPDATE, "MESSAGE_UPDATE"); + assert_variant(EventType::PRESENCE_UPDATE, "PRESENCE_UPDATE"); + assert_variant(EventType::PRESENCES_REPLACE, "PRESENCES_REPLACE"); + assert_variant(EventType::REACTION_ADD, "MESSAGE_REACTION_ADD"); + assert_variant(EventType::REACTION_REMOVE, "MESSAGE_REACTION_REMOVE"); + assert_variant( + EventType::REACTION_REMOVE_ALL, + "MESSAGE_REACTION_REMOVE_ALL", + ); assert_variant( - EventType::ReactionRemoveEmoji, + EventType::REACTION_REMOVE_EMOJI, "MESSAGE_REACTION_REMOVE_EMOJI", ); - assert_variant(EventType::Ready, "READY"); - assert_variant(EventType::Resumed, "RESUMED"); - assert_variant(EventType::RoleCreate, "GUILD_ROLE_CREATE"); - assert_variant(EventType::RoleDelete, "GUILD_ROLE_DELETE"); - assert_variant(EventType::RoleUpdate, "GUILD_ROLE_UPDATE"); - assert_variant(EventType::StageInstanceCreate, "STAGE_INSTANCE_CREATE"); - assert_variant(EventType::StageInstanceDelete, "STAGE_INSTANCE_DELETE"); - assert_variant(EventType::StageInstanceUpdate, "STAGE_INSTANCE_UPDATE"); - assert_variant(EventType::ThreadCreate, "THREAD_CREATE"); - assert_variant(EventType::ThreadDelete, "THREAD_DELETE"); - assert_variant(EventType::ThreadListSync, "THREAD_LIST_SYNC"); - assert_variant(EventType::ThreadMemberUpdate, "THREAD_MEMBER_UPDATE"); - assert_variant(EventType::ThreadMembersUpdate, "THREAD_MEMBERS_UPDATE"); - assert_variant(EventType::ThreadUpdate, "THREAD_UPDATE"); - assert_variant(EventType::TypingStart, "TYPING_START"); - assert_variant(EventType::UnavailableGuild, "UNAVAILABLE_GUILD"); - assert_variant(EventType::UserUpdate, "USER_UPDATE"); - assert_variant(EventType::VoiceServerUpdate, "VOICE_SERVER_UPDATE"); - assert_variant(EventType::VoiceStateUpdate, "VOICE_STATE_UPDATE"); - assert_variant(EventType::WebhooksUpdate, "WEBHOOKS_UPDATE"); + assert_variant(EventType::READY, "READY"); + assert_variant(EventType::RESUMED, "RESUMED"); + assert_variant(EventType::ROLE_CREATE, "GUILD_ROLE_CREATE"); + assert_variant(EventType::ROLE_DELETE, "GUILD_ROLE_DELETE"); + assert_variant(EventType::ROLE_UPDATE, "GUILD_ROLE_UPDATE"); + assert_variant(EventType::STAGE_INSTANCE_CREATE, "STAGE_INSTANCE_CREATE"); + assert_variant(EventType::STAGE_INSTANCE_DELETE, "STAGE_INSTANCE_DELETE"); + assert_variant(EventType::STAGE_INSTANCE_UPDATE, "STAGE_INSTANCE_UPDATE"); + assert_variant(EventType::THREAD_CREATE, "THREAD_CREATE"); + assert_variant(EventType::THREAD_DELETE, "THREAD_DELETE"); + assert_variant(EventType::THREAD_LIST_SYNC, "THREAD_LIST_SYNC"); + assert_variant(EventType::THREAD_MEMBER_UPDATE, "THREAD_MEMBER_UPDATE"); + assert_variant(EventType::THREAD_MEMBERS_UPDATE, "THREAD_MEMBERS_UPDATE"); + assert_variant(EventType::THREAD_UPDATE, "THREAD_UPDATE"); + assert_variant(EventType::TYPING_START, "TYPING_START"); + assert_variant(EventType::UNAVAILABLE_GUILD, "UNAVAILABLE_GUILD"); + assert_variant(EventType::USER_UPDATE, "USER_UPDATE"); + assert_variant(EventType::VOICE_SERVER_UPDATE, "VOICE_SERVER_UPDATE"); + assert_variant(EventType::VOICE_STATE_UPDATE, "VOICE_STATE_UPDATE"); + assert_variant(EventType::WEBHOOKS_UPDATE, "WEBHOOKS_UPDATE"); } } diff --git a/twilight-model/src/gateway/event/mod.rs b/twilight-model/src/gateway/event/mod.rs index bcb282caa25..c4d8f3f7fb7 100644 --- a/twilight-model/src/gateway/event/mod.rs +++ b/twilight-model/src/gateway/event/mod.rs @@ -256,76 +256,76 @@ impl Event { pub const fn kind(&self) -> EventType { match self { - Self::AutoModerationActionExecution(_) => EventType::AutoModerationActionExecution, - Self::AutoModerationRuleCreate(_) => EventType::AutoModerationRuleCreate, - Self::AutoModerationRuleDelete(_) => EventType::AutoModerationRuleDelete, - Self::AutoModerationRuleUpdate(_) => EventType::AutoModerationRuleUpdate, - Self::BanAdd(_) => EventType::BanAdd, - Self::BanRemove(_) => EventType::BanRemove, - Self::ChannelCreate(_) => EventType::ChannelCreate, - Self::ChannelDelete(_) => EventType::ChannelDelete, - Self::ChannelPinsUpdate(_) => EventType::ChannelPinsUpdate, - Self::ChannelUpdate(_) => EventType::ChannelUpdate, - Self::CommandPermissionsUpdate(_) => EventType::CommandPermissionsUpdate, - Self::GatewayClose(_) => EventType::GatewayClose, - Self::GatewayHeartbeat(_) => EventType::GatewayHeartbeat, - Self::GatewayHeartbeatAck => EventType::GatewayHeartbeatAck, - Self::GatewayHello(_) => EventType::GatewayHello, - Self::GatewayInvalidateSession(_) => EventType::GatewayInvalidateSession, - Self::GatewayReconnect => EventType::GatewayReconnect, - Self::GiftCodeUpdate => EventType::GiftCodeUpdate, - Self::GuildAuditLogEntryCreate(_) => EventType::GuildAuditLogEntryCreate, - Self::GuildCreate(_) => EventType::GuildCreate, - Self::GuildDelete(_) => EventType::GuildDelete, - Self::GuildEmojisUpdate(_) => EventType::GuildEmojisUpdate, - Self::GuildIntegrationsUpdate(_) => EventType::GuildIntegrationsUpdate, - Self::GuildScheduledEventCreate(_) => EventType::GuildScheduledEventCreate, - Self::GuildScheduledEventDelete(_) => EventType::GuildScheduledEventDelete, - Self::GuildScheduledEventUpdate(_) => EventType::GuildScheduledEventUpdate, - Self::GuildScheduledEventUserAdd(_) => EventType::GuildScheduledEventUserAdd, - Self::GuildScheduledEventUserRemove(_) => EventType::GuildScheduledEventUserRemove, - Self::GuildStickersUpdate(_) => EventType::GuildStickersUpdate, - Self::GuildUpdate(_) => EventType::GuildUpdate, - Self::IntegrationCreate(_) => EventType::IntegrationCreate, - Self::IntegrationDelete(_) => EventType::IntegrationDelete, - Self::IntegrationUpdate(_) => EventType::IntegrationUpdate, - Self::InteractionCreate(_) => EventType::InteractionCreate, - Self::InviteCreate(_) => EventType::InviteCreate, - Self::InviteDelete(_) => EventType::InviteDelete, - Self::MemberAdd(_) => EventType::MemberAdd, - Self::MemberRemove(_) => EventType::MemberRemove, - Self::MemberUpdate(_) => EventType::MemberUpdate, - Self::MemberChunk(_) => EventType::MemberChunk, - Self::MessageCreate(_) => EventType::MessageCreate, - Self::MessageDelete(_) => EventType::MessageDelete, - Self::MessageDeleteBulk(_) => EventType::MessageDeleteBulk, - Self::MessageUpdate(_) => EventType::MessageUpdate, - Self::PresenceUpdate(_) => EventType::PresenceUpdate, - Self::PresencesReplace => EventType::PresencesReplace, - Self::ReactionAdd(_) => EventType::ReactionAdd, - Self::ReactionRemove(_) => EventType::ReactionRemove, - Self::ReactionRemoveAll(_) => EventType::ReactionRemoveAll, - Self::ReactionRemoveEmoji(_) => EventType::ReactionRemoveEmoji, - Self::Ready(_) => EventType::Ready, - Self::Resumed => EventType::Resumed, - Self::RoleCreate(_) => EventType::RoleCreate, - Self::RoleDelete(_) => EventType::RoleDelete, - Self::RoleUpdate(_) => EventType::RoleUpdate, - Self::StageInstanceCreate(_) => EventType::StageInstanceCreate, - Self::StageInstanceDelete(_) => EventType::StageInstanceDelete, - Self::StageInstanceUpdate(_) => EventType::StageInstanceUpdate, - Self::ThreadCreate(_) => EventType::ThreadCreate, - Self::ThreadDelete(_) => EventType::ThreadDelete, - Self::ThreadListSync(_) => EventType::ThreadListSync, - Self::ThreadMemberUpdate(_) => EventType::ThreadMemberUpdate, - Self::ThreadMembersUpdate(_) => EventType::ThreadMembersUpdate, - Self::ThreadUpdate(_) => EventType::ThreadUpdate, - Self::TypingStart(_) => EventType::TypingStart, - Self::UnavailableGuild(_) => EventType::UnavailableGuild, - Self::UserUpdate(_) => EventType::UserUpdate, - Self::VoiceServerUpdate(_) => EventType::VoiceServerUpdate, - Self::VoiceStateUpdate(_) => EventType::VoiceStateUpdate, - Self::WebhooksUpdate(_) => EventType::WebhooksUpdate, + Self::AutoModerationActionExecution(_) => EventType::AUTO_MODERATION_ACTION_EXECUTION, + Self::AutoModerationRuleCreate(_) => EventType::AUTO_MODERATION_RULE_CREATE, + Self::AutoModerationRuleDelete(_) => EventType::AUTO_MODERATION_RULE_DELETE, + Self::AutoModerationRuleUpdate(_) => EventType::AUTO_MODERATION_RULE_UPDATE, + Self::BanAdd(_) => EventType::BAN_ADD, + Self::BanRemove(_) => EventType::BAN_REMOVE, + Self::ChannelCreate(_) => EventType::CHANNEL_CREATE, + Self::ChannelDelete(_) => EventType::CHANNEL_DELETE, + Self::ChannelPinsUpdate(_) => EventType::CHANNEL_PINS_UPDATE, + Self::ChannelUpdate(_) => EventType::CHANNEL_UPDATE, + Self::CommandPermissionsUpdate(_) => EventType::COMMAND_PERMISSIONS_UPDATE, + Self::GatewayClose(_) => EventType::GATEWAY_CLOSE, + Self::GatewayHeartbeat(_) => EventType::GATEWAY_HEARTBEAT, + Self::GatewayHeartbeatAck => EventType::GATEWAY_HEARTBEAT_ACK, + Self::GatewayHello(_) => EventType::GATEWAY_HELLO, + Self::GatewayInvalidateSession(_) => EventType::GATEWAY_INVALIDATE_SESSION, + Self::GatewayReconnect => EventType::GATEWAY_RECONNECT, + Self::GiftCodeUpdate => EventType::GIFT_CODE_UPDATE, + Self::GuildAuditLogEntryCreate(_) => EventType::GUILD_AUDIT_LOG_ENTRY_CREATE, + Self::GuildCreate(_) => EventType::GUILD_CREATE, + Self::GuildDelete(_) => EventType::GUILD_DELETE, + Self::GuildEmojisUpdate(_) => EventType::GUILD_EMOJIS_UPDATE, + Self::GuildIntegrationsUpdate(_) => EventType::GUILD_INTEGRATIONS_UPDATE, + Self::GuildScheduledEventCreate(_) => EventType::GUILD_SCHEDULED_EVENT_CREATE, + Self::GuildScheduledEventDelete(_) => EventType::GUILD_SCHEDULED_EVENT_DELETE, + Self::GuildScheduledEventUpdate(_) => EventType::GUILD_SCHEDULED_EVENT_UPDATE, + Self::GuildScheduledEventUserAdd(_) => EventType::GUILD_SCHEDULED_EVENT_USER_ADD, + Self::GuildScheduledEventUserRemove(_) => EventType::GUILD_SCHEDULED_EVENT_USER_REMOVE, + Self::GuildStickersUpdate(_) => EventType::GUILD_STICKERS_UPDATE, + Self::GuildUpdate(_) => EventType::GUILD_UPDATE, + Self::IntegrationCreate(_) => EventType::INTEGRATION_CREATE, + Self::IntegrationDelete(_) => EventType::INTEGRATION_DELETE, + Self::IntegrationUpdate(_) => EventType::INTEGRATION_UPDATE, + Self::InteractionCreate(_) => EventType::INTERACTION_CREATE, + Self::InviteCreate(_) => EventType::INVITE_CREATE, + Self::InviteDelete(_) => EventType::INVITE_DELETE, + Self::MemberAdd(_) => EventType::MEMBER_ADD, + Self::MemberRemove(_) => EventType::MEMBER_REMOVE, + Self::MemberUpdate(_) => EventType::MEMBER_UPDATE, + Self::MemberChunk(_) => EventType::MEMBER_CHUNK, + Self::MessageCreate(_) => EventType::MESSAGE_CREATE, + Self::MessageDelete(_) => EventType::MESSAGE_DELETE, + Self::MessageDeleteBulk(_) => EventType::MESSAGE_DELETE_BULK, + Self::MessageUpdate(_) => EventType::MESSAGE_UPDATE, + Self::PresenceUpdate(_) => EventType::PRESENCE_UPDATE, + Self::PresencesReplace => EventType::PRESENCES_REPLACE, + Self::ReactionAdd(_) => EventType::REACTION_ADD, + Self::ReactionRemove(_) => EventType::REACTION_REMOVE, + Self::ReactionRemoveAll(_) => EventType::REACTION_REMOVE_ALL, + Self::ReactionRemoveEmoji(_) => EventType::REACTION_REMOVE_EMOJI, + Self::Ready(_) => EventType::READY, + Self::Resumed => EventType::RESUMED, + Self::RoleCreate(_) => EventType::ROLE_CREATE, + Self::RoleDelete(_) => EventType::ROLE_DELETE, + Self::RoleUpdate(_) => EventType::ROLE_UPDATE, + Self::StageInstanceCreate(_) => EventType::STAGE_INSTANCE_CREATE, + Self::StageInstanceDelete(_) => EventType::STAGE_INSTANCE_DELETE, + Self::StageInstanceUpdate(_) => EventType::STAGE_INSTANCE_UPDATE, + Self::ThreadCreate(_) => EventType::THREAD_CREATE, + Self::ThreadDelete(_) => EventType::THREAD_DELETE, + Self::ThreadListSync(_) => EventType::THREAD_LIST_SYNC, + Self::ThreadMemberUpdate(_) => EventType::THREAD_MEMBER_UPDATE, + Self::ThreadMembersUpdate(_) => EventType::THREAD_MEMBERS_UPDATE, + Self::ThreadUpdate(_) => EventType::THREAD_UPDATE, + Self::TypingStart(_) => EventType::TYPING_START, + Self::UnavailableGuild(_) => EventType::UNAVAILABLE_GUILD, + Self::UserUpdate(_) => EventType::USER_UPDATE, + Self::VoiceServerUpdate(_) => EventType::VOICE_SERVER_UPDATE, + Self::VoiceStateUpdate(_) => EventType::VOICE_STATE_UPDATE, + Self::WebhooksUpdate(_) => EventType::WEBHOOKS_UPDATE, } } } diff --git a/twilight-model/src/gateway/mod.rs b/twilight-model/src/gateway/mod.rs index 327b0d265e7..ce0b5731e21 100644 --- a/twilight-model/src/gateway/mod.rs +++ b/twilight-model/src/gateway/mod.rs @@ -12,7 +12,7 @@ mod reaction; mod session_start_limit; pub use self::{ - close_code::{CloseCode, CloseCodeConversionError}, + close_code::CloseCode, frame::CloseFrame, id::{ShardId, ShardIdParseError, ShardIdParseErrorType}, intents::Intents, diff --git a/twilight-model/src/gateway/opcode.rs b/twilight-model/src/gateway/opcode.rs index 2d3b1b55eb7..1541be30fc0 100644 --- a/twilight-model/src/gateway/opcode.rs +++ b/twilight-model/src/gateway/opcode.rs @@ -1,73 +1,84 @@ -use serde_repr::{Deserialize_repr, Serialize_repr}; +use serde::{Deserialize, Serialize}; /// Gateway event opcodes. /// /// The documentation is written from a client's perspective. /// -/// [`PresenceUpdate`], [`RequestGuildMembers`], and [`VoiceStateUpdate`] are +/// [`PRESENCE_UPDATE`], [`REQUEST_GUILD_MEMBERS`], and [`VOICE_STATE_UPDATE`] are /// not requiried for establishing or maintaining a gateway connection. /// -/// [`PresenceUpdate`]: Self::PresenceUpdate -/// [`RequestGuildMembers`]: Self::RequestGuildMembers -/// [`VoiceStateUpdate`]: Self::VoiceStateUpdate -#[derive(Clone, Copy, Debug, Deserialize_repr, Eq, Hash, PartialEq, Serialize_repr)] -#[non_exhaustive] -#[repr(u8)] -pub enum OpCode { +/// [`PRESENCE_UPDATE`]: Self::PRESENCE_UPDATE +/// [`REQUEST_GUILD_MEMBERS`]: Self::REQUEST_GUILD_MEMBERS +/// [`VOICE_STATE_UPDATE`]: Self::VOICE_STATE_UPDATE +#[derive(Clone, Copy, Deserialize, Eq, Hash, PartialEq, Serialize)] +pub struct OpCode(u8); + +impl OpCode { /// [`DispatchEvent`] and sequence number. /// /// Will only be received after establishing or resuming a session. /// /// [`DispatchEvent`]: super::event::DispatchEvent - Dispatch = 0, + pub const DISPATCH: Self = Self::new(0); + /// Periodically sent to maintain the connection and may be received to /// immediately request one. - Heartbeat = 1, + pub const HEARTBEAT: Self = Self::new(1); + /// Start a new session. - Identify = 2, + pub const IDENTIFY: Self = Self::new(2); + /// Request to update the client's presence. - PresenceUpdate = 3, + pub const PRESENCE_UPDATE: Self = Self::new(3); + /// Request to join, leave or move between voice channels. - VoiceStateUpdate = 4, - /// Resume a previously disconnected session, skipping over [`Identify`]. + pub const VOICE_STATE_UPDATE: Self = Self::new(4); + + /// Resume a previously disconnected session, skipping over [`IDENTIFY`]. /// - /// [`Identify`]: Self::Identify - Resume = 6, + /// [`IDENTIFY`]: Self::IDENTIFY + pub const RESUME: Self = Self::new(6); + /// Indicates that a reconnect is required. - Reconnect = 7, + pub const RECONNECT: Self = Self::new(7); + /// Request a list of members for a guild. - RequestGuildMembers = 8, + pub const REQUEST_GUILD_MEMBERS: Self = Self::new(8); + /// Received when the session is invalidated. - InvalidSession = 9, + pub const INVALID_SESSION: Self = Self::new(9); + /// Received after connecting, contains the heartbeat interval. - Hello = 10, - /// Received in response to sending a [`Heartbeat`]. + pub const HELLO: Self = Self::new(10); + + /// Received in response to sending a [`HEARTBEAT`]. /// - /// [`Heartbeat`]: Self::Heartbeat - HeartbeatAck = 11, -} + /// [`HEARTBEAT`]: Self::HEARTBEAT + pub const HEARTBEAT_ACK: Self = Self::new(11); -impl OpCode { - /// Try to match an integer value to an opcode, returning [`None`] if no - /// match is found. - pub const fn from(code: u8) -> Option { - Some(match code { - 0 => Self::Dispatch, - 1 => Self::Heartbeat, - 2 => Self::Identify, - 3 => Self::PresenceUpdate, - 4 => Self::VoiceStateUpdate, - 6 => Self::Resume, - 7 => Self::Reconnect, - 8 => Self::RequestGuildMembers, - 9 => Self::InvalidSession, - 10 => Self::Hello, - 11 => Self::HeartbeatAck, + /// Name of the associated constant. + /// + /// Returns `None` if the value doesn't have a defined constant. + pub const fn name(self) -> Option<&'static str> { + Some(match self { + Self::DISPATCH => "DISPATCH", + Self::HEARTBEAT => "HEARTBEAT", + Self::HEARTBEAT_ACK => "HEARTBEAT_ACK", + Self::HELLO => "HELLO", + Self::IDENTIFY => "IDENTIFY", + Self::INVALID_SESSION => "INVALID_SESSION", + Self::PRESENCE_UPDATE => "PRESENCE_UPDATE", + Self::RECONNECT => "RECONNECT", + Self::REQUEST_GUILD_MEMBERS => "REQUEST_GUILD_MEMBERS", + Self::RESUME => "RESUME", + Self::VOICE_STATE_UPDATE => "VOICE_STATE_UPDATE", _ => return None, }) } } +impl_typed!(OpCode, u8); + #[cfg(test)] mod tests { use super::OpCode; @@ -88,18 +99,29 @@ mod tests { Sync, ); + const MAP: &[(OpCode, u8)] = &[ + (OpCode::DISPATCH, 0), + (OpCode::HEARTBEAT, 1), + (OpCode::IDENTIFY, 2), + (OpCode::PRESENCE_UPDATE, 3), + (OpCode::VOICE_STATE_UPDATE, 4), + (OpCode::RESUME, 6), + (OpCode::RECONNECT, 7), + (OpCode::REQUEST_GUILD_MEMBERS, 8), + (OpCode::INVALID_SESSION, 9), + (OpCode::HELLO, 10), + (OpCode::HEARTBEAT_ACK, 11), + ]; + #[test] fn variants() { - serde_test::assert_tokens(&OpCode::Dispatch, &[Token::U8(0)]); - serde_test::assert_tokens(&OpCode::Heartbeat, &[Token::U8(1)]); - serde_test::assert_tokens(&OpCode::Identify, &[Token::U8(2)]); - serde_test::assert_tokens(&OpCode::PresenceUpdate, &[Token::U8(3)]); - serde_test::assert_tokens(&OpCode::VoiceStateUpdate, &[Token::U8(4)]); - serde_test::assert_tokens(&OpCode::Resume, &[Token::U8(6)]); - serde_test::assert_tokens(&OpCode::Reconnect, &[Token::U8(7)]); - serde_test::assert_tokens(&OpCode::RequestGuildMembers, &[Token::U8(8)]); - serde_test::assert_tokens(&OpCode::InvalidSession, &[Token::U8(9)]); - serde_test::assert_tokens(&OpCode::Hello, &[Token::U8(10)]); - serde_test::assert_tokens(&OpCode::HeartbeatAck, &[Token::U8(11)]); + for (kind, num) in MAP { + serde_test::assert_tokens( + kind, + &[Token::NewtypeStruct { name: "OpCode" }, Token::U8(*num)], + ); + assert_eq!(*kind, OpCode::from(*num)); + assert_eq!(*num, kind.get()); + } } } diff --git a/twilight-model/src/gateway/payload/incoming/auto_moderation_action_execution.rs b/twilight-model/src/gateway/payload/incoming/auto_moderation_action_execution.rs index 188fc363ccf..66cec5fec2c 100644 --- a/twilight-model/src/gateway/payload/incoming/auto_moderation_action_execution.rs +++ b/twilight-model/src/gateway/payload/incoming/auto_moderation_action_execution.rs @@ -21,9 +21,9 @@ pub struct AutoModerationActionExecution { /// action. /// /// Will not exist if this event does not correspond to an action with type - /// [`SendAlertMessage`]. + /// [`SEND_ALERT_MESSAGE`]. /// - /// [`SendAlertMessage`]: crate::guild::auto_moderation::AutoModerationActionType::SendAlertMessage + /// [`SEND_ALERT_MESSAGE`]: crate::guild::auto_moderation::AutoModerationActionType::SEND_ALERT_MESSAGE #[serde(skip_serializing_if = "Option::is_none")] pub alert_system_message_id: Option>, /// ID of the channel in which user content was posted. @@ -114,7 +114,7 @@ mod tests { let value = AutoModerationActionExecution { action: AutoModerationAction { - kind: AutoModerationActionType::BlockMessage, + kind: AutoModerationActionType::BLOCK_MESSAGE, metadata: None, }, alert_system_message_id: Some(ALERT_SYSTEM_MESSAGE_ID), @@ -125,7 +125,7 @@ mod tests { matched_keyword: Some("darn".into()), message_id: Some(MESSAGE_ID), rule_id: RULE_ID, - rule_trigger_type: AutoModerationTriggerType::Keyword, + rule_trigger_type: AutoModerationTriggerType::KEYWORD, user_id: USER_ID, }; @@ -142,7 +142,10 @@ mod tests { len: 1, }, Token::Str("type"), - Token::U8(u8::from(AutoModerationActionType::BlockMessage)), + Token::NewtypeStruct { + name: "AutoModerationActionType", + }, + Token::U8(u8::from(AutoModerationActionType::BLOCK_MESSAGE)), Token::StructEnd, Token::Str("alert_system_message_id"), Token::Some, @@ -171,7 +174,10 @@ mod tests { Token::NewtypeStruct { name: "Id" }, Token::Str("5"), Token::Str("rule_trigger_type"), - Token::U8(u8::from(AutoModerationTriggerType::Keyword)), + Token::NewtypeStruct { + name: "AutoModerationTriggerType", + }, + Token::U8(AutoModerationTriggerType::KEYWORD.get()), Token::Str("user_id"), Token::NewtypeStruct { name: "Id" }, Token::Str("6"), diff --git a/twilight-model/src/gateway/payload/incoming/member_chunk.rs b/twilight-model/src/gateway/payload/incoming/member_chunk.rs index 73e7365be5e..68081313640 100644 --- a/twilight-model/src/gateway/payload/incoming/member_chunk.rs +++ b/twilight-model/src/gateway/payload/incoming/member_chunk.rs @@ -437,10 +437,10 @@ mod tests { client_status: ClientStatus { desktop: None, mobile: None, - web: Some(Status::Online), + web: Some(Status::ONLINE), }, guild_id: Id::new(1), - status: Status::Online, + status: Status::ONLINE, user: UserOrId::UserId { id: Id::new(2) }, }, Presence { @@ -448,21 +448,21 @@ mod tests { client_status: ClientStatus { desktop: None, mobile: None, - web: Some(Status::Online), + web: Some(Status::ONLINE), }, guild_id: Id::new(1), - status: Status::Online, + status: Status::ONLINE, user: UserOrId::UserId { id: Id::new(3) }, }, Presence { activities: Vec::new(), client_status: ClientStatus { - desktop: Some(Status::DoNotDisturb), + desktop: Some(Status::DO_NOT_DISTURB), mobile: None, web: None, }, guild_id: Id::new(1), - status: Status::DoNotDisturb, + status: Status::DO_NOT_DISTURB, user: UserOrId::UserId { id: Id::new(5) }, }, ]), diff --git a/twilight-model/src/gateway/payload/incoming/thread_members_update.rs b/twilight-model/src/gateway/payload/incoming/thread_members_update.rs index f61e82d786f..0d01012e9eb 100644 --- a/twilight-model/src/gateway/payload/incoming/thread_members_update.rs +++ b/twilight-model/src/gateway/payload/incoming/thread_members_update.rs @@ -150,7 +150,7 @@ mod tests { flags: None, id: Some("aaaaaaaaaaaaaaaa".to_owned()), instance: None, - kind: ActivityType::Custom, + kind: ActivityType::CUSTOM, name: "foo".to_owned(), emoji: Some(ActivityEmoji { name: "Test".to_string(), @@ -166,12 +166,12 @@ mod tests { let presence = Presence { activities: vec![activity], client_status: ClientStatus { - desktop: Some(Status::Online), + desktop: Some(Status::ONLINE), mobile: None, web: None, }, guild_id: Id::new(2), - status: Status::Online, + status: Status::ONLINE, user: UserOrId::UserId { id: Id::new(3) }, }; @@ -296,6 +296,9 @@ mod tests { Token::Some, Token::Str("aaaaaaaaaaaaaaaa"), Token::Str("type"), + Token::NewtypeStruct { + name: "ActivityType", + }, Token::U8(4), Token::Str("name"), Token::Str("foo"), @@ -308,18 +311,16 @@ mod tests { }, Token::Str("desktop"), Token::Some, - Token::Enum { name: "Status" }, + Token::NewtypeStruct { name: "Status" }, Token::Str("online"), - Token::Unit, Token::StructEnd, Token::Str("guild_id"), Token::Some, Token::NewtypeStruct { name: "GuildId" }, Token::Str("2"), Token::Str("status"), - Token::Enum { name: "Status" }, + Token::NewtypeStruct { name: "Status" }, Token::Str("online"), - Token::Unit, Token::Str("user"), Token::Struct { name: "UserOrId", diff --git a/twilight-model/src/gateway/payload/outgoing/heartbeat.rs b/twilight-model/src/gateway/payload/outgoing/heartbeat.rs index 7b5944dbf52..0125dec62a9 100644 --- a/twilight-model/src/gateway/payload/outgoing/heartbeat.rs +++ b/twilight-model/src/gateway/payload/outgoing/heartbeat.rs @@ -11,7 +11,7 @@ impl Heartbeat { pub const fn new(seq: Option) -> Self { Self { d: seq, - op: OpCode::Heartbeat, + op: OpCode::HEARTBEAT, } } } diff --git a/twilight-model/src/gateway/payload/outgoing/identify.rs b/twilight-model/src/gateway/payload/outgoing/identify.rs index 24fcdda8be4..4cb01e7fbde 100644 --- a/twilight-model/src/gateway/payload/outgoing/identify.rs +++ b/twilight-model/src/gateway/payload/outgoing/identify.rs @@ -12,7 +12,7 @@ impl Identify { pub const fn new(info: IdentifyInfo) -> Self { Self { d: info, - op: OpCode::Identify, + op: OpCode::IDENTIFY, } } } diff --git a/twilight-model/src/gateway/payload/outgoing/request_guild_members.rs b/twilight-model/src/gateway/payload/outgoing/request_guild_members.rs index aae236f9e7e..ef25ca30b83 100644 --- a/twilight-model/src/gateway/payload/outgoing/request_guild_members.rs +++ b/twilight-model/src/gateway/payload/outgoing/request_guild_members.rs @@ -169,7 +169,7 @@ impl RequestGuildMembersBuilder { query: Some(query), user_ids: None, }, - op: OpCode::RequestGuildMembers, + op: OpCode::REQUEST_GUILD_MEMBERS, } } @@ -208,7 +208,7 @@ impl RequestGuildMembersBuilder { query: None, user_ids: Some(RequestGuildMemberId::One(user_id)), }, - op: OpCode::RequestGuildMembers, + op: OpCode::REQUEST_GUILD_MEMBERS, } } @@ -264,7 +264,7 @@ impl RequestGuildMembersBuilder { query: None, user_ids: Some(RequestGuildMemberId::Multiple(user_ids)), }, - op: OpCode::RequestGuildMembers, + op: OpCode::REQUEST_GUILD_MEMBERS, }) } } diff --git a/twilight-model/src/gateway/payload/outgoing/resume.rs b/twilight-model/src/gateway/payload/outgoing/resume.rs index a7cf42da7c0..d96825dfe19 100644 --- a/twilight-model/src/gateway/payload/outgoing/resume.rs +++ b/twilight-model/src/gateway/payload/outgoing/resume.rs @@ -11,7 +11,7 @@ impl Resume { pub fn new(seq: u64, session_id: impl Into, token: impl Into) -> Self { Self { d: ResumeInfo::new(seq, session_id, token), - op: OpCode::Resume, + op: OpCode::RESUME, } } } diff --git a/twilight-model/src/gateway/payload/outgoing/update_presence.rs b/twilight-model/src/gateway/payload/outgoing/update_presence.rs index c60b91a2c40..5dcf45f8799 100644 --- a/twilight-model/src/gateway/payload/outgoing/update_presence.rs +++ b/twilight-model/src/gateway/payload/outgoing/update_presence.rs @@ -83,7 +83,7 @@ impl UpdatePresence { Ok(Self { d, - op: OpCode::PresenceUpdate, + op: OpCode::PRESENCE_UPDATE, }) } } diff --git a/twilight-model/src/gateway/payload/outgoing/update_voice_state.rs b/twilight-model/src/gateway/payload/outgoing/update_voice_state.rs index 2c7ca4f5735..53527297cec 100644 --- a/twilight-model/src/gateway/payload/outgoing/update_voice_state.rs +++ b/twilight-model/src/gateway/payload/outgoing/update_voice_state.rs @@ -22,7 +22,7 @@ impl UpdateVoiceState { ) -> Self { Self { d: UpdateVoiceStateInfo::new(guild_id, channel_id, self_deaf, self_mute), - op: OpCode::VoiceStateUpdate, + op: OpCode::VOICE_STATE_UPDATE, } } } diff --git a/twilight-model/src/gateway/presence/activity_type.rs b/twilight-model/src/gateway/presence/activity_type.rs index 728a0f0a80d..53ffe49b153 100644 --- a/twilight-model/src/gateway/presence/activity_type.rs +++ b/twilight-model/src/gateway/presence/activity_type.rs @@ -1,70 +1,68 @@ use serde::{Deserialize, Serialize}; -#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)] -#[non_exhaustive] -#[serde(from = "u8", into = "u8")] -pub enum ActivityType { - Playing, - Streaming, - Listening, - Watching, - Custom, - Competing, - Unknown(u8), -} +#[derive(Clone, Copy, Deserialize, Eq, Hash, PartialEq, Serialize)] +pub struct ActivityType(u8); -impl From for ActivityType { - fn from(value: u8) -> Self { - match value { - 0 => ActivityType::Playing, - 1 => ActivityType::Streaming, - 2 => ActivityType::Listening, - 3 => ActivityType::Watching, - 4 => ActivityType::Custom, - 5 => ActivityType::Competing, - unknown => ActivityType::Unknown(unknown), - } - } -} +impl ActivityType { + pub const PLAYING: Self = Self::new(0); + pub const STREAMING: Self = Self::new(1); + pub const LISTENING: Self = Self::new(2); + pub const WATCHING: Self = Self::new(3); + pub const CUSTOM: Self = Self::new(4); + pub const COMPETING: Self = Self::new(5); -impl From for u8 { - fn from(value: ActivityType) -> Self { - match value { - ActivityType::Playing => 0, - ActivityType::Streaming => 1, - ActivityType::Listening => 2, - ActivityType::Watching => 3, - ActivityType::Custom => 4, - ActivityType::Competing => 5, - ActivityType::Unknown(unknown) => unknown, - } + /// Name of the associated constant. + /// + /// Returns `None` if the value doesn't have a defined constant. + pub const fn name(self) -> Option<&'static str> { + Some(match self { + Self::PLAYING => "PLAYING", + Self::STREAMING => "STREAMING", + Self::LISTENING => "LISTENING", + Self::WATCHING => "WATCHING", + Self::CUSTOM => "CUSTOM", + Self::COMPETING => "COMPETING", + _ => return None, + }) } } impl Default for ActivityType { fn default() -> Self { - Self::Playing + Self::PLAYING } } +impl_typed!(ActivityType, u8); + #[cfg(test)] mod tests { use super::ActivityType; use serde_test::Token; - #[test] - fn default() { - assert_eq!(ActivityType::Playing, ActivityType::default()); - } + const MAP: &[(ActivityType, u8)] = &[ + (ActivityType::PLAYING, 0), + (ActivityType::STREAMING, 1), + (ActivityType::LISTENING, 2), + (ActivityType::WATCHING, 3), + (ActivityType::CUSTOM, 4), + (ActivityType::COMPETING, 5), + ]; #[test] fn variants() { - serde_test::assert_tokens(&ActivityType::Playing, &[Token::U8(0)]); - serde_test::assert_tokens(&ActivityType::Streaming, &[Token::U8(1)]); - serde_test::assert_tokens(&ActivityType::Listening, &[Token::U8(2)]); - serde_test::assert_tokens(&ActivityType::Watching, &[Token::U8(3)]); - serde_test::assert_tokens(&ActivityType::Custom, &[Token::U8(4)]); - serde_test::assert_tokens(&ActivityType::Competing, &[Token::U8(5)]); - serde_test::assert_tokens(&ActivityType::Unknown(99), &[Token::U8(99)]); + for (kind, num) in MAP { + serde_test::assert_tokens( + kind, + &[ + Token::NewtypeStruct { + name: "ActivityType", + }, + Token::U8(*num), + ], + ); + assert_eq!(*kind, ActivityType::from(*num)); + assert_eq!(*num, kind.get()); + } } } diff --git a/twilight-model/src/gateway/presence/client_status.rs b/twilight-model/src/gateway/presence/client_status.rs index 6af395edf38..f208a7109d3 100644 --- a/twilight-model/src/gateway/presence/client_status.rs +++ b/twilight-model/src/gateway/presence/client_status.rs @@ -19,9 +19,9 @@ mod tests { #[test] fn mobile_online() { let value = ClientStatus { - desktop: Some(Status::Idle), - mobile: Some(Status::Online), - web: Some(Status::DoNotDisturb), + desktop: Some(Status::IDLE), + mobile: Some(Status::ONLINE), + web: Some(Status::DO_NOT_DISTURB), }; serde_test::assert_tokens( @@ -33,19 +33,16 @@ mod tests { }, Token::Str("desktop"), Token::Some, - Token::Enum { name: "Status" }, + Token::NewtypeStruct { name: "Status" }, Token::Str("idle"), - Token::Unit, Token::Str("mobile"), Token::Some, - Token::Enum { name: "Status" }, + Token::NewtypeStruct { name: "Status" }, Token::Str("online"), - Token::Unit, Token::Str("web"), Token::Some, - Token::Enum { name: "Status" }, + Token::NewtypeStruct { name: "Status" }, Token::Str("dnd"), - Token::Unit, Token::StructEnd, ], ); diff --git a/twilight-model/src/gateway/presence/mod.rs b/twilight-model/src/gateway/presence/mod.rs index 1c1ce303900..33bf76f64f1 100644 --- a/twilight-model/src/gateway/presence/mod.rs +++ b/twilight-model/src/gateway/presence/mod.rs @@ -190,7 +190,7 @@ mod tests { flags: None, id: Some("aaaaaaaaaaaaaaaa".to_owned()), instance: None, - kind: ActivityType::Custom, + kind: ActivityType::CUSTOM, name: "foo".to_owned(), emoji: Some(ActivityEmoji { name: "Test".to_string(), @@ -206,12 +206,12 @@ mod tests { let value = Presence { activities: vec![activity], client_status: ClientStatus { - desktop: Some(Status::Online), + desktop: Some(Status::ONLINE), mobile: None, web: None, }, guild_id: Id::new(2), - status: Status::Online, + status: Status::ONLINE, user: UserOrId::UserId { id: Id::new(1) }, }; @@ -234,9 +234,8 @@ mod tests { Token::NewtypeStruct { name: "Id" }, Token::Str("2"), Token::Str("status"), - Token::Enum { name: "Status" }, + Token::NewtypeStruct { name: "Status" }, Token::Str("online"), - Token::Unit, Token::Str("client_status"), Token::Struct { name: "ClientStatus", @@ -244,9 +243,8 @@ mod tests { }, Token::Str("desktop"), Token::Some, - Token::Enum { name: "Status" }, + Token::NewtypeStruct { name: "Status" }, Token::Str("online"), - Token::Unit, Token::Str("mobile"), Token::None, Token::Str("web"), @@ -259,6 +257,9 @@ mod tests { len: 4, }, Token::Str("type"), + Token::NewtypeStruct { + name: "ActivityType", + }, Token::U8(4), Token::Str("name"), Token::Str("foo"), @@ -308,12 +309,12 @@ mod tests { let expected = Vec::from([Presence { activities: vec![], client_status: ClientStatus { - desktop: Some(Status::Online), + desktop: Some(Status::ONLINE), mobile: None, web: None, }, guild_id: Id::new(2), - status: Status::Online, + status: Status::ONLINE, user: UserOrId::UserId { id: Id::new(1) }, }]); diff --git a/twilight-model/src/gateway/presence/status.rs b/twilight-model/src/gateway/presence/status.rs index f05034d5439..865d8600902 100644 --- a/twilight-model/src/gateway/presence/status.rs +++ b/twilight-model/src/gateway/presence/status.rs @@ -1,60 +1,83 @@ +use crate::util::known_string::KnownString; use serde::{Deserialize, Serialize}; -#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)] -pub enum Status { - #[serde(rename = "dnd")] - DoNotDisturb, - #[serde(rename = "idle")] - Idle, - #[serde(rename = "invisible")] - Invisible, - #[serde(rename = "offline")] - Offline, - #[serde(rename = "online")] - Online, +#[derive(Clone, Copy, Deserialize, Eq, Hash, PartialEq, Serialize)] +pub struct Status(KnownString<16>); + +impl Status { + pub const DO_NOT_DISTURB: Self = Self::from_bytes(b"dnd"); + + pub const IDLE: Self = Self::from_bytes(b"idle"); + + pub const INVISIBLE: Self = Self::from_bytes(b"invisible"); + + pub const OFFLINE: Self = Self::from_bytes(b"offline"); + + pub const ONLINE: Self = Self::from_bytes(b"online"); + + /// Name of the associated constant. + /// + /// Returns `None` if the value doesn't have a defined constant. + pub const fn name(self) -> Option<&'static str> { + Some(match self { + Self::DO_NOT_DISTURB => "DO_NOT_DISTURB", + Self::IDLE => "IDLE", + Self::INVISIBLE => "INVISIBLE", + Self::OFFLINE => "OFFLINE", + Self::ONLINE => "ONLINE", + _ => return None, + }) + } } +impl_typed!(Status, String); + #[cfg(test)] mod tests { use super::Status; + use serde::{Deserialize, Serialize}; use serde_test::Token; + use static_assertions::assert_impl_all; + use std::{fmt::Debug, hash::Hash, str::FromStr, string::ToString}; + + assert_impl_all!( + Status: AsRef, + Clone, + Copy, + Debug, + Deserialize<'static>, + Eq, + FromStr, + Hash, + PartialEq, + Send, + Serialize, + Sync, + ToString, + TryFrom<&'static str>, + ); + + const MAP: &[(Status, &str)] = &[ + (Status::DO_NOT_DISTURB, "dnd"), + (Status::IDLE, "idle"), + (Status::INVISIBLE, "invisible"), + (Status::OFFLINE, "offline"), + (Status::ONLINE, "online"), + ]; #[test] fn variants() { - serde_test::assert_tokens( - &Status::DoNotDisturb, - &[Token::UnitVariant { - name: "Status", - variant: "dnd", - }], - ); - serde_test::assert_tokens( - &Status::Idle, - &[Token::UnitVariant { - name: "Status", - variant: "idle", - }], - ); - serde_test::assert_tokens( - &Status::Invisible, - &[Token::UnitVariant { - name: "Status", - variant: "invisible", - }], - ); - serde_test::assert_tokens( - &Status::Offline, - &[Token::UnitVariant { - name: "Status", - variant: "offline", - }], - ); - serde_test::assert_tokens( - &Status::Online, - &[Token::UnitVariant { - name: "Status", - variant: "online", - }], - ); + for (kind, name) in MAP { + serde_test::assert_tokens( + kind, + &[Token::NewtypeStruct { name: "Status" }, Token::Str(name)], + ); + assert_eq!(Some(*kind), Status::new(name)); + assert_eq!(*name, kind.as_ref()); + assert_eq!(Ok(*kind), Status::from_str(name)); + assert_eq!(Ok(*kind), Status::try_from(*name)); + assert_eq!(name, &kind.to_string()); + assert_eq!(*name, kind.get()); + } } } diff --git a/twilight-model/src/guild/afk_timeout.rs b/twilight-model/src/guild/afk_timeout.rs index 866cc1fb7e0..08073ba6443 100644 --- a/twilight-model/src/guild/afk_timeout.rs +++ b/twilight-model/src/guild/afk_timeout.rs @@ -14,7 +14,7 @@ use std::time::Duration; /// ``` /// /// [`Guild::afk_timeout`]: super::Guild::afk_timeout -#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)] +#[derive(Clone, Copy, Deserialize, Eq, Hash, PartialEq, Serialize)] #[non_exhaustive] pub struct AfkTimeout(u16); @@ -34,23 +34,18 @@ impl AfkTimeout { /// AFK timeout of one hour. pub const ONE_HOUR: Self = Self(3600); - /// Retrieve the duration of the AFK timeout in seconds. + /// Name of the associated constant. /// - /// # 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) + /// Returns `None` if the value doesn't have a defined constant. + pub const fn name(self) -> Option<&'static str> { + Some(match self { + Self::ONE_MINUTE => "ONE_MINUTE", + Self::FIVE_MINUTES => "FIVE_MINUTES", + Self::FIFTEEN_MINUTES => "FIFTEEN_MINUTES", + Self::THIRTY_MINUTES => "THIRTY_MINUTES", + Self::ONE_HOUR => "ONE_HOUR", + _ => return None, + }) } } @@ -72,6 +67,8 @@ impl PartialEq for u16 { } } +impl_typed!(AfkTimeout, u16); + #[cfg(test)] mod tests { use super::AfkTimeout; diff --git a/twilight-model/src/guild/audit_log/change.rs b/twilight-model/src/guild/audit_log/change.rs index 2bed17463dd..4ea1971a6aa 100644 --- a/twilight-model/src/guild/audit_log/change.rs +++ b/twilight-model/src/guild/audit_log/change.rs @@ -732,92 +732,92 @@ impl AuditLogChange { /// old: Some(3), /// }; /// - /// assert_eq!(Some(AuditLogChangeKey::UserLimit), change.key()); + /// assert_eq!(Some(AuditLogChangeKey::USER_LIMIT), change.key()); /// ``` /// /// [`Other`]: Self::Other /// [`Uses`]: Self::Uses pub const fn key(&self) -> Option { Some(match self { - Self::AfkChannelId { .. } => AuditLogChangeKey::AfkChannelId, - Self::AfkTimeout { .. } => AuditLogChangeKey::AfkTimeout, - Self::Allow { .. } => AuditLogChangeKey::Allow, - Self::ApplicationId { .. } => AuditLogChangeKey::ApplicationId, - Self::Archived { .. } => AuditLogChangeKey::Archived, - Self::Asset { .. } => AuditLogChangeKey::Asset, - Self::AutoArchiveDuration { .. } => AuditLogChangeKey::AutoArchiveDuration, - Self::Available { .. } => AuditLogChangeKey::Available, - Self::AvatarHash { .. } => AuditLogChangeKey::AvatarHash, - Self::BannerHash { .. } => AuditLogChangeKey::BannerHash, - Self::Bitrate { .. } => AuditLogChangeKey::Bitrate, - Self::ChannelId { .. } => AuditLogChangeKey::ChannelId, - Self::Code { .. } => AuditLogChangeKey::Code, - Self::Color { .. } => AuditLogChangeKey::Color, - Self::CommandId { .. } => AuditLogChangeKey::CommandId, + Self::AfkChannelId { .. } => AuditLogChangeKey::AFK_CHANNEL_ID, + Self::AfkTimeout { .. } => AuditLogChangeKey::AFK_TIMEOUT, + Self::Allow { .. } => AuditLogChangeKey::ALLOW, + Self::ApplicationId { .. } => AuditLogChangeKey::APPLICATION_ID, + Self::Archived { .. } => AuditLogChangeKey::ARCHIVED, + Self::Asset { .. } => AuditLogChangeKey::ASSET, + Self::AutoArchiveDuration { .. } => AuditLogChangeKey::AUTO_ARCHIVE_DURATION, + Self::Available { .. } => AuditLogChangeKey::AVAILABLE, + Self::AvatarHash { .. } => AuditLogChangeKey::AVATAR_HASH, + Self::BannerHash { .. } => AuditLogChangeKey::BANNER_HASH, + Self::Bitrate { .. } => AuditLogChangeKey::BITRATE, + Self::ChannelId { .. } => AuditLogChangeKey::CHANNEL_ID, + Self::Code { .. } => AuditLogChangeKey::CODE, + Self::Color { .. } => AuditLogChangeKey::COLOR, + Self::CommandId { .. } => AuditLogChangeKey::COMMAND_ID, Self::CommunicationDisabledUntil { .. } => { - AuditLogChangeKey::CommunicationDisabledUntil + AuditLogChangeKey::COMMUNICATION_DISABLED_UNTIL } - Self::Deaf { .. } => AuditLogChangeKey::Deaf, + Self::Deaf { .. } => AuditLogChangeKey::DEAF, Self::DefaultAutoArchiveDuration { .. } => { - AuditLogChangeKey::DefaultAutoArchiveDuration + AuditLogChangeKey::DEFAULT_AUTO_ARCHIVE_DURATION } Self::DefaultMessageNotifications { .. } => { - AuditLogChangeKey::DefaultMessageNotifications + AuditLogChangeKey::DEFAULT_MESSAGE_NOTIFICATIONS } - Self::Deny { .. } => AuditLogChangeKey::Deny, - Self::Description { .. } => AuditLogChangeKey::Description, - Self::DiscoverySplashHash { .. } => AuditLogChangeKey::DiscoverySplashHash, - Self::EnableEmoticons { .. } => AuditLogChangeKey::EnableEmoticons, - Self::EntityType { .. } => AuditLogChangeKey::EntityType, - Self::ExpireBehavior { .. } => AuditLogChangeKey::ExpireBehavior, - Self::ExpireGracePeriod { .. } => AuditLogChangeKey::ExpireGracePeriod, - Self::ExplicitContentFilter { .. } => AuditLogChangeKey::ExplicitContentFilter, - Self::FormatType { .. } => AuditLogChangeKey::FormatType, - Self::GuildId { .. } => AuditLogChangeKey::GuildId, - Self::Hoist { .. } => AuditLogChangeKey::Hoist, - Self::IconHash { .. } => AuditLogChangeKey::IconHash, - Self::Id { .. } => AuditLogChangeKey::Id, - Self::ImageHash { .. } => AuditLogChangeKey::ImageHash, - Self::Invitable { .. } => AuditLogChangeKey::Invitable, - Self::InviterId { .. } => AuditLogChangeKey::InviterId, - Self::Location { .. } => AuditLogChangeKey::Location, - Self::Locked { .. } => AuditLogChangeKey::Locked, - Self::MaxAge { .. } => AuditLogChangeKey::MaxAge, - Self::MaxUses { .. } => AuditLogChangeKey::MaxUses, - Self::Mentionable { .. } => AuditLogChangeKey::Mentionable, - Self::MfaLevel { .. } => AuditLogChangeKey::MfaLevel, - Self::Mute { .. } => AuditLogChangeKey::Mute, - Self::Name { .. } => AuditLogChangeKey::Name, - Self::Nick { .. } => AuditLogChangeKey::Nick, - Self::Nsfw { .. } => AuditLogChangeKey::Nsfw, - Self::NsfwLevel { .. } => AuditLogChangeKey::NsfwLevel, - Self::OwnerId { .. } => AuditLogChangeKey::OwnerId, - Self::PermissionOverwrites { .. } => AuditLogChangeKey::PermissionOverwrites, - Self::Permissions { .. } => AuditLogChangeKey::Permissions, - Self::Position { .. } => AuditLogChangeKey::Position, - Self::PreferredLocale { .. } => AuditLogChangeKey::PreferredLocale, - Self::PrivacyLevel { .. } => AuditLogChangeKey::PrivacyLevel, - Self::PruneDeleteDays { .. } => AuditLogChangeKey::PruneDeleteDays, - Self::PublicUpdatesChannelId { .. } => AuditLogChangeKey::PublicUpdatesChannelId, - Self::RateLimitPerUser { .. } => AuditLogChangeKey::RateLimitPerUser, - Self::Region { .. } => AuditLogChangeKey::Region, - Self::RoleAdded { .. } => AuditLogChangeKey::RoleAdded, - Self::RoleRemoved { .. } => AuditLogChangeKey::RoleRemoved, - Self::RulesChannelId { .. } => AuditLogChangeKey::RulesChannelId, - Self::SplashHash { .. } => AuditLogChangeKey::SplashHash, - Self::Status { .. } => AuditLogChangeKey::Status, - Self::SystemChannelId { .. } => AuditLogChangeKey::SystemChannelId, - Self::Tags { .. } => AuditLogChangeKey::Tags, - Self::Temporary { .. } => AuditLogChangeKey::Temporary, - Self::Topic { .. } => AuditLogChangeKey::Topic, - Self::Type { .. } => AuditLogChangeKey::Type, - Self::UnicodeEmoji { .. } => AuditLogChangeKey::UnicodeEmoji, - Self::UserLimit { .. } => AuditLogChangeKey::UserLimit, - Self::Uses { .. } => AuditLogChangeKey::Uses, - Self::VanityUrlCode { .. } => AuditLogChangeKey::VanityUrlCode, - Self::VerificationLevel { .. } => AuditLogChangeKey::VerificationLevel, - Self::WidgetChannelId { .. } => AuditLogChangeKey::WidgetChannelId, - Self::WidgetEnabled { .. } => AuditLogChangeKey::WidgetEnabled, + Self::Deny { .. } => AuditLogChangeKey::DENY, + Self::Description { .. } => AuditLogChangeKey::DESCRIPTION, + Self::DiscoverySplashHash { .. } => AuditLogChangeKey::DISCOVERY_SPLASH_HASH, + Self::EnableEmoticons { .. } => AuditLogChangeKey::ENABLE_EMOTICONS, + Self::EntityType { .. } => AuditLogChangeKey::ENTITY_TYPE, + Self::ExpireBehavior { .. } => AuditLogChangeKey::EXPIRE_BEHAVIOR, + Self::ExpireGracePeriod { .. } => AuditLogChangeKey::EXPIRE_GRACE_PERIOD, + Self::ExplicitContentFilter { .. } => AuditLogChangeKey::EXPLICIT_CONTENT_FILTER, + Self::FormatType { .. } => AuditLogChangeKey::FORMAT_TYPE, + Self::GuildId { .. } => AuditLogChangeKey::GUILD_ID, + Self::Hoist { .. } => AuditLogChangeKey::HOIST, + Self::IconHash { .. } => AuditLogChangeKey::ICON_HASH, + Self::Id { .. } => AuditLogChangeKey::ID, + Self::ImageHash { .. } => AuditLogChangeKey::IMAGE_HASH, + Self::Invitable { .. } => AuditLogChangeKey::INVITABLE, + Self::InviterId { .. } => AuditLogChangeKey::INVITER_ID, + Self::Location { .. } => AuditLogChangeKey::LOCATION, + Self::Locked { .. } => AuditLogChangeKey::LOCKED, + Self::MaxAge { .. } => AuditLogChangeKey::MAX_AGE, + Self::MaxUses { .. } => AuditLogChangeKey::MAX_USES, + Self::Mentionable { .. } => AuditLogChangeKey::MENTIONABLE, + Self::MfaLevel { .. } => AuditLogChangeKey::MFA_LEVEL, + Self::Mute { .. } => AuditLogChangeKey::MUTE, + Self::Name { .. } => AuditLogChangeKey::NAME, + Self::Nick { .. } => AuditLogChangeKey::NICK, + Self::Nsfw { .. } => AuditLogChangeKey::NSFW, + Self::NsfwLevel { .. } => AuditLogChangeKey::NSFW_LEVEL, + Self::OwnerId { .. } => AuditLogChangeKey::OWNER_ID, + Self::PermissionOverwrites { .. } => AuditLogChangeKey::PERMISSION_OVERWRITES, + Self::Permissions { .. } => AuditLogChangeKey::PERMISSIONS, + Self::Position { .. } => AuditLogChangeKey::POSITION, + Self::PreferredLocale { .. } => AuditLogChangeKey::PREFERRED_LOCALE, + Self::PrivacyLevel { .. } => AuditLogChangeKey::PRIVACY_LEVEL, + Self::PruneDeleteDays { .. } => AuditLogChangeKey::PRUNE_DELETE_DAYS, + Self::PublicUpdatesChannelId { .. } => AuditLogChangeKey::PUBLIC_UPDATES_CHANNEL_ID, + Self::RateLimitPerUser { .. } => AuditLogChangeKey::RATE_LIMIT_PER_USER, + Self::Region { .. } => AuditLogChangeKey::REGION, + Self::RoleAdded { .. } => AuditLogChangeKey::ROLE_ADDED, + Self::RoleRemoved { .. } => AuditLogChangeKey::ROLE_REMOVED, + Self::RulesChannelId { .. } => AuditLogChangeKey::RULES_CHANNEL_ID, + Self::SplashHash { .. } => AuditLogChangeKey::SPLASH_HASH, + Self::Status { .. } => AuditLogChangeKey::STATUS, + Self::SystemChannelId { .. } => AuditLogChangeKey::SYSTEM_CHANNEL_ID, + Self::Tags { .. } => AuditLogChangeKey::TAGS, + Self::Temporary { .. } => AuditLogChangeKey::TEMPORARY, + Self::Topic { .. } => AuditLogChangeKey::TOPIC, + Self::Type { .. } => AuditLogChangeKey::TYPE, + Self::UnicodeEmoji { .. } => AuditLogChangeKey::UNICODE_EMOJI, + Self::UserLimit { .. } => AuditLogChangeKey::USER_LIMIT, + Self::Uses { .. } => AuditLogChangeKey::USES, + Self::VanityUrlCode { .. } => AuditLogChangeKey::VANITY_URL_CODE, + Self::VerificationLevel { .. } => AuditLogChangeKey::VERIFICATION_LEVEL, + Self::WidgetChannelId { .. } => AuditLogChangeKey::WIDGET_CHANNEL_ID, + Self::WidgetEnabled { .. } => AuditLogChangeKey::WIDGET_ENABLED, Self::Other => return None, }) } @@ -931,7 +931,7 @@ mod tests { old: None, }; - assert_eq!(Some(AuditLogChangeKey::AfkChannelId), value.key()); + assert_eq!(Some(AuditLogChangeKey::AFK_CHANNEL_ID), value.key()); serde_test::assert_tokens( &value, @@ -961,7 +961,7 @@ mod tests { old: Some(old), }; - assert_eq!(Some(AuditLogChangeKey::Permissions), value.key()); + assert_eq!(Some(AuditLogChangeKey::PERMISSIONS), value.key()); serde_test::assert_tokens( &value, @@ -987,7 +987,7 @@ mod tests { fn channel_type() { let value = AuditLogChange::Type { new: Some(AuditLogChangeTypeValue::Unsigned(u64::from(u8::from( - ChannelType::PrivateThread, + ChannelType::PRIVATE_THREAD, )))), old: None, }; @@ -1003,7 +1003,7 @@ mod tests { Token::Str("type"), Token::String("new_value"), Token::Some, - Token::U64(u64::from(u8::from(ChannelType::PrivateThread))), + Token::U64(u64::from(u8::from(ChannelType::PRIVATE_THREAD))), Token::StructEnd, ], ); diff --git a/twilight-model/src/guild/audit_log/change_key.rs b/twilight-model/src/guild/audit_log/change_key.rs index 8190036f87e..db082fddaaa 100644 --- a/twilight-model/src/guild/audit_log/change_key.rs +++ b/twilight-model/src/guild/audit_log/change_key.rs @@ -1,5 +1,5 @@ +use crate::util::known_string::KnownString; use serde::{Deserialize, Serialize}; -use std::fmt::{Display, Formatter, Result as FmtResult}; /// Type of [`AuditLogChange`]. /// @@ -7,264 +7,318 @@ use std::fmt::{Display, Formatter, Result as FmtResult}; /// /// [`AuditLogChange`]: super::AuditLogChange /// [1]: https://discord.com/developers/docs/resources/audit-log#audit-log-change-object-audit-log-change-key -#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, Ord, PartialEq, PartialOrd, Serialize)] -#[non_exhaustive] -#[serde(rename_all = "snake_case")] -pub enum AuditLogChangeKey { +#[derive(Clone, Copy, Deserialize, Eq, Hash, PartialEq, Serialize)] +pub struct AuditLogChangeKey(KnownString<64>); + +impl AuditLogChangeKey { /// AFK voice channel for a guild. - AfkChannelId, + pub const AFK_CHANNEL_ID: Self = Self::from_bytes(b"afk_channel_id"); + /// Timeout to cause a user to be moved to an AFK voice channel. - AfkTimeout, + pub const AFK_TIMEOUT: Self = Self::from_bytes(b"afk_timeout"); + /// Allowed permissions of a permission overwrite target. - Allow, + pub const ALLOW: Self = Self::from_bytes(b"allow"); + /// ID of an application. - ApplicationId, + pub const APPLICATION_ID: Self = Self::from_bytes(b"application_id"); + /// Thread was archived or unarchived. - Archived, + pub const ARCHIVED: Self = Self::from_bytes(b"archived"); + /// Asset of a sticker. /// /// Empty string. - Asset, + pub const ASSET: Self = Self::from_bytes(b"asset"); + /// Auto archive duration of a thread. - AutoArchiveDuration, + pub const AUTO_ARCHIVE_DURATION: Self = Self::from_bytes(b"auto_archive_duration"); + /// Availability of a sticker. - Available, + pub const AVAILABLE: Self = Self::from_bytes(b"available"); + /// Hash of an avatar. - AvatarHash, + pub const AVATAR_HASH: Self = Self::from_bytes(b"avatar_hash"); + /// Hash of a guild banner. - BannerHash, + pub const BANNER_HASH: Self = Self::from_bytes(b"banner_hash"); + /// Bitrate of an audio channel. - Bitrate, + pub const BITRATE: Self = Self::from_bytes(b"bitrate"); + /// Channel for an invite code. - ChannelId, + pub const CHANNEL_ID: Self = Self::from_bytes(b"channel_id"); + /// Code of an invite. - Code, + pub const CODE: Self = Self::from_bytes(b"code"); + /// Color of a role. - Color, + pub const COLOR: Self = Self::from_bytes(b"color"); + /// Permissions for a command were updated. - CommandId, + pub const COMMAND_ID: Self = Self::from_bytes(b"command_id"); + /// Member timeout state changed. - CommunicationDisabledUntil, + pub const COMMUNICATION_DISABLED_UNTIL: Self = + Self::from_bytes(b"communication_disabled_until"); + /// Whether a user is guild deafened. - Deaf, + pub const DEAF: Self = Self::from_bytes(b"deaf"); + /// Default auto archive duration for new threads. - DefaultAutoArchiveDuration, + pub const DEFAULT_AUTO_ARCHIVE_DURATION: Self = + Self::from_bytes(b"default_auto_archive_duration"); + /// Default message notification level for a guild. - DefaultMessageNotifications, + pub const DEFAULT_MESSAGE_NOTIFICATIONS: Self = + Self::from_bytes(b"default_message_notifications"); + /// Denied permissions of a permission overwrite target. - Deny, + pub const DENY: Self = Self::from_bytes(b"deny"); + /// Description of a guild. - Description, + pub const DESCRIPTION: Self = Self::from_bytes(b"description"); + /// Hash of a guild's discovery splash. - DiscoverySplashHash, + pub const DISCOVERY_SPLASH_HASH: Self = Self::from_bytes(b"discovery_splash_hash"); + /// Whether emoticons are enabled. - EnableEmoticons, + pub const ENABLE_EMOTICONS: Self = Self::from_bytes(b"enable_emoticons"); + /// Entity type of guild scheduled event was changed. - EntityType, + pub const ENTITY_TYPE: Self = Self::from_bytes(b"entity_type"); + /// Behavior of the expiration of an integration. - ExpireBehavior, + pub const EXPIRE_BEHAVIOR: Self = Self::from_bytes(b"expire_behavior"); + /// Grace period of the expiration of an integration. - ExpireGracePeriod, + pub const EXPIRE_GRACE_PERIOD: Self = Self::from_bytes(b"expire_grace_period"); + /// Explicit content filter level of a guild. - ExplicitContentFilter, + pub const EXPLICIT_CONTENT_FILTER: Self = Self::from_bytes(b"explicit_content_filter"); + /// Format type of a sticker. - FormatType, + pub const FORMAT_TYPE: Self = Self::from_bytes(b"format_type"); + /// Guild that a sticker is in. - GuildId, + pub const GUILD_ID: Self = Self::from_bytes(b"guild_id"); + /// Whether a role is hoisted. - Hoist, + pub const HOIST: Self = Self::from_bytes(b"hoist"); + /// Hash of a guild icon. - IconHash, + pub const ICON_HASH: Self = Self::from_bytes(b"icon_hash"); + /// ID of an entity. - Id, + pub const ID: Self = Self::from_bytes(b"id"); + /// Hash of a guild scheduled event cover. - ImageHash, + pub const IMAGE_HASH: Self = Self::from_bytes(b"image_hash"); + /// Invitable state of a private thread. - Invitable, + pub const INVITABLE: Self = Self::from_bytes(b"invitable"); + /// ID of the user who created an invite. - InviterId, + pub const INVITER_ID: Self = Self::from_bytes(b"inviter_id"); + /// Channel ID for a scheduled event changed. - Location, + pub const LOCATION: Self = Self::from_bytes(b"location"); + /// Thread was locked or unlocked. - Locked, + pub const LOCKED: Self = Self::from_bytes(b"locked"); + /// Maximum age of an invite. - MaxAge, + pub const MAX_AGE: Self = Self::from_bytes(b"max_age"); + /// Maximum uses of an invite. - MaxUses, + pub const MAX_USES: Self = Self::from_bytes(b"max_uses"); + /// Whether a role can be mentioned in a message. - Mentionable, + pub const MENTIONABLE: Self = Self::from_bytes(b"mentionable"); + /// Multi-Factor Authentication level required of a guild's moderators. - MfaLevel, + pub const MFA_LEVEL: Self = Self::from_bytes(b"mfa_level"); + /// Whether a user is guild muted. - Mute, + pub const MUTE: Self = Self::from_bytes(b"mute"); + /// Name of an entity such as a channel or role. - Name, + pub const NAME: Self = Self::from_bytes(b"name"); + /// Nickname of a member. - Nick, + pub const NICK: Self = Self::from_bytes(b"nick"); + /// Whether a channel is NSFW. - Nsfw, + pub const NSFW: Self = Self::from_bytes(b"nsfw"); + /// NSFW level of a guild. - NsfwLevel, + pub const NSFW_LEVEL: Self = Self::from_bytes(b"nsfw_level"); + /// ID of the owner of a guild. - OwnerId, + pub const OWNER_ID: Self = Self::from_bytes(b"owner_id"); + /// Permission overwrites on a channel changed. - PermissionOverwrites, + pub const PERMISSION_OVERWRITES: Self = Self::from_bytes(b"permission_overwrites"); + /// Default permissions of a role. - Permissions, + pub const PERMISSIONS: Self = Self::from_bytes(b"permissions"); + /// Position of an entity such as a channel or role. - Position, + pub const POSITION: Self = Self::from_bytes(b"position"); + /// Preferred locale of a guild. - PreferredLocale, + pub const PREFERRED_LOCALE: Self = Self::from_bytes(b"preferred_locale"); + /// Privacy level of a stage instance. - PrivacyLevel, + pub const PRIVACY_LEVEL: Self = Self::from_bytes(b"privacy_level"); + /// Number of days' worth of inactivity for a guild prune. - PruneDeleteDays, + pub const PRUNE_DELETE_DAYS: Self = Self::from_bytes(b"prune_delete_days"); + /// ID of a guild's public updates channel. - PublicUpdatesChannelId, + pub const PUBLIC_UPDATES_CHANNEL_ID: Self = Self::from_bytes(b"public_updates_channel_id"); + /// Ratelimit per user in a textual channel. - RateLimitPerUser, + pub const RATE_LIMIT_PER_USER: Self = Self::from_bytes(b"rate_limit_per_user"); + /// Region of a guild changed. - Region, + pub const REGION: Self = Self::from_bytes(b"region"); + /// Role added to a user. - #[serde(rename = "$add")] - RoleAdded, + pub const ROLE_ADDED: Self = Self::from_bytes(b"$add"); + /// Role removed from a user. - #[serde(rename = "$remove")] - RoleRemoved, + pub const ROLE_REMOVED: Self = Self::from_bytes(b"$remove"); + /// ID of a guild's rules channel. - RulesChannelId, + pub const RULES_CHANNEL_ID: Self = Self::from_bytes(b"rules_channel_id"); + /// Hash of a guild's splash. - SplashHash, + pub const SPLASH_HASH: Self = Self::from_bytes(b"splash_hash"); + /// Status of guild scheduled event was changed. - Status, + pub const STATUS: Self = Self::from_bytes(b"status"); + /// ID of a guild's system channel. - SystemChannelId, + pub const SYSTEM_CHANNEL_ID: Self = Self::from_bytes(b"system_channel_id"); + /// Related emoji of a sticker. - Tags, + pub const TAGS: Self = Self::from_bytes(b"tags"); + /// Whether an invite is temporary. - Temporary, + pub const TEMPORARY: Self = Self::from_bytes(b"temporary"); + /// Topic of a textual channel. - Topic, + pub const TOPIC: Self = Self::from_bytes(b"topic"); + /// Type of a created entity. - Type, + pub const TYPE: Self = Self::from_bytes(b"type"); + /// Role unicode emoji. - UnicodeEmoji, + pub const UNICODE_EMOJI: Self = Self::from_bytes(b"unicode_emoji"); + /// Maximum number of users in a voice channel. - UserLimit, + pub const USER_LIMIT: Self = Self::from_bytes(b"user_limit"); + /// Number of uses of an invite. - Uses, + pub const USES: Self = Self::from_bytes(b"uses"); + /// Code of a guild's vanity invite. - VanityUrlCode, + pub const VANITY_URL_CODE: Self = Self::from_bytes(b"vanity_url_code"); + /// Required verification level of new members in a guild. - VerificationLevel, + pub const VERIFICATION_LEVEL: Self = Self::from_bytes(b"verification_level"); + /// Channel ID of a widget. - WidgetChannelId, + pub const WIDGET_CHANNEL_ID: Self = Self::from_bytes(b"widget_channel_id"); + /// Whether a widget is enabled. - WidgetEnabled, -} + pub const WIDGET_ENABLED: Self = Self::from_bytes(b"widget_enabled"); -impl AuditLogChangeKey { - /// Raw name of the key. - /// - /// The raw names of keys are in `snake_case` form. - /// - /// # Examples - /// - /// Check the names of the [`Allow`] and [`BannerHash`] keys: - /// - /// ``` - /// use twilight_model::guild::audit_log::AuditLogChangeKey; - /// - /// assert_eq!("allow", AuditLogChangeKey::Allow.name()); - /// assert_eq!("banner_hash", AuditLogChangeKey::BannerHash.name()); - /// ``` + /// Name of the associated constant. /// - /// [`Allow`]: Self::Allow - /// [`BannerHash`]: Self::BannerHash - pub const fn name(self) -> &'static str { - match self { - Self::AfkChannelId => "afk_channel_id", - Self::AfkTimeout => "afk_timeout", - Self::Allow => "allow", - Self::ApplicationId => "application_id", - Self::Archived => "archived", - Self::Asset => "asset", - Self::AutoArchiveDuration => "auto_archive_duration", - Self::Available => "available", - Self::AvatarHash => "avatar_hash", - Self::BannerHash => "banner_hash", - Self::Bitrate => "bitrate", - Self::ChannelId => "channel_id", - Self::Code => "code", - Self::Color => "color", - Self::CommandId => "command_id", - Self::CommunicationDisabledUntil => "communication_disabled_until", - Self::Deaf => "deaf", - Self::DefaultAutoArchiveDuration => "default_auto_archive_duration", - Self::DefaultMessageNotifications => "default_message_notifications", - Self::Deny => "deny", - Self::Description => "description", - Self::DiscoverySplashHash => "discovery_splash_hash", - Self::EnableEmoticons => "enable_emoticons", - Self::EntityType => "entity_type", - Self::ExpireBehavior => "expire_behavior", - Self::ExpireGracePeriod => "expire_grace_period", - Self::ExplicitContentFilter => "explicit_content_filter", - Self::FormatType => "format_type", - Self::GuildId => "guild_id", - Self::Hoist => "hoist", - Self::IconHash => "icon_hash", - Self::Id => "id", - Self::ImageHash => "image_hash", - Self::Invitable => "invitable", - Self::InviterId => "inviter_id", - Self::Location => "location", - Self::Locked => "locked", - Self::MaxAge => "max_age", - Self::MaxUses => "max_uses", - Self::Mentionable => "mentionable", - Self::MfaLevel => "mfa_level", - Self::Mute => "mute", - Self::Name => "name", - Self::Nick => "nick", - Self::Nsfw => "nsfw", - Self::NsfwLevel => "nsfw_level", - Self::OwnerId => "owner_id", - Self::PermissionOverwrites => "permission_overwrites", - Self::Permissions => "permissions", - Self::Position => "position", - Self::PreferredLocale => "preferred_locale", - Self::PrivacyLevel => "privacy_level", - Self::PruneDeleteDays => "prune_delete_days", - Self::PublicUpdatesChannelId => "public_updates_channel_id", - Self::RateLimitPerUser => "rate_limit_per_user", - Self::Region => "region", - Self::RoleAdded => "$add", - Self::RoleRemoved => "$remove", - Self::RulesChannelId => "rules_channel_id", - Self::SplashHash => "splash_hash", - Self::Status => "status", - Self::SystemChannelId => "system_channel_id", - Self::Tags => "tags", - Self::Temporary => "temporary", - Self::Topic => "topic", - Self::Type => "type", - Self::UnicodeEmoji => "unicode_emoji", - Self::UserLimit => "user_limit", - Self::Uses => "uses", - Self::VanityUrlCode => "vanity_url_code", - Self::VerificationLevel => "verification_level", - Self::WidgetChannelId => "widget_channel_id", - Self::WidgetEnabled => "widget_enabled", - } + /// Returns `None` if the value doesn't have a defined constant. + pub const fn name(self) -> Option<&'static str> { + Some(match self { + Self::AFK_CHANNEL_ID => "AFK_CHANNEL_ID", + Self::AFK_TIMEOUT => "AFK_TIMEOUT", + Self::ALLOW => "ALLOW", + Self::APPLICATION_ID => "APPLICATION_ID", + Self::ARCHIVED => "ARCHIVED", + Self::ASSET => "ASSET", + Self::AUTO_ARCHIVE_DURATION => "AUTO_ARCHIVE_DURATION", + Self::AVAILABLE => "AVAILABLE", + Self::AVATAR_HASH => "AVATAR_HASH", + Self::BANNER_HASH => "BANNER_HASH", + Self::BITRATE => "BITRATE", + Self::CHANNEL_ID => "CHANNEL_ID", + Self::CODE => "CODE", + Self::COLOR => "COLOR", + Self::COMMAND_ID => "COMMAND_ID", + Self::COMMUNICATION_DISABLED_UNTIL => "COMMUNICATION_DISABLED_UNTIL", + Self::DEAF => "DEAF", + Self::DEFAULT_AUTO_ARCHIVE_DURATION => "DEFAULT_AUTO_ARCHIVE_DURATION", + Self::DEFAULT_MESSAGE_NOTIFICATIONS => "DEFAULT_MESSAGE_NOTIFICATIONS", + Self::DENY => "DENY", + Self::DESCRIPTION => "DESCRIPTION", + Self::DISCOVERY_SPLASH_HASH => "DISCOVERY_SPLASH_HASH", + Self::ENABLE_EMOTICONS => "ENABLE_EMOTICONS", + Self::ENTITY_TYPE => "ENTITY_TYPE", + Self::EXPIRE_BEHAVIOR => "EXPIRE_BEHAVIOR", + Self::EXPIRE_GRACE_PERIOD => "EXPIRE_GRACE_PERIOD", + Self::EXPLICIT_CONTENT_FILTER => "EXPLICIT_CONTENT_FILTER", + Self::FORMAT_TYPE => "FORMAT_TYPE", + Self::GUILD_ID => "GUILD_ID", + Self::HOIST => "HOIST", + Self::ICON_HASH => "ICON_HASH", + Self::ID => "ID", + Self::IMAGE_HASH => "IMAGE_HASH", + Self::INVITABLE => "INVITABLE", + Self::INVITER_ID => "INVITER_ID", + Self::LOCATION => "LOCATION", + Self::LOCKED => "LOCKED", + Self::MAX_AGE => "MAX_AGE", + Self::MAX_USES => "MAX_USES", + Self::MENTIONABLE => "MENTIONABLE", + Self::MFA_LEVEL => "MFA_LEVEL", + Self::MUTE => "MUTE", + Self::NAME => "NAME", + Self::NICK => "NICK", + Self::NSFW => "NSFW", + Self::NSFW_LEVEL => "NSFW_LEVEL", + Self::OWNER_ID => "OWNER_ID", + Self::PERMISSION_OVERWRITES => "PERMISSION_OVERWRITES", + Self::PERMISSIONS => "PERMISSIONS", + Self::POSITION => "POSITION", + Self::PREFERRED_LOCALE => "PREFERRED_LOCALE", + Self::PRIVACY_LEVEL => "PRIVACY_LEVEL", + Self::PRUNE_DELETE_DAYS => "PRUNE_DELETE_DAYS", + Self::PUBLIC_UPDATES_CHANNEL_ID => "PUBLIC_UPDATES_CHANNEL_ID", + Self::RATE_LIMIT_PER_USER => "RATE_LIMIT_PER_USER", + Self::REGION => "REGION", + Self::ROLE_ADDED => "ROLE_ADDED", + Self::ROLE_REMOVED => "ROLE_REMOVED", + Self::RULES_CHANNEL_ID => "RULES_CHANNEL_ID", + Self::SPLASH_HASH => "SPLASH_HASH", + Self::STATUS => "STATUS", + Self::SYSTEM_CHANNEL_ID => "SYSTEM_CHANNEL_ID", + Self::TAGS => "TAGS", + Self::TEMPORARY => "TEMPORARY", + Self::TOPIC => "TOPIC", + Self::TYPE => "TYPE", + Self::UNICODE_EMOJI => "UNICODE_EMOJI", + Self::USER_LIMIT => "USER_LIMIT", + Self::USES => "USES", + Self::VANITY_URL_CODE => "VANITY_URL_CODE", + Self::VERIFICATION_LEVEL => "VERIFICATION_LEVEL", + Self::WIDGET_CHANNEL_ID => "WIDGET_CHANNEL_ID", + Self::WIDGET_ENABLED => "WIDGET_ENABLED", + _ => return None, + }) } } -impl Display for AuditLogChangeKey { - fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult { - f.write_str(self.name()) - } -} +impl_typed!(AuditLogChangeKey, String); #[cfg(test)] mod tests { @@ -272,526 +326,643 @@ mod tests { use serde::{Deserialize, Serialize}; use serde_test::Token; use static_assertions::assert_impl_all; - use std::{ - fmt::{Debug, Display}, - hash::Hash, - }; + use std::{fmt::Debug, hash::Hash, str::FromStr}; assert_impl_all!( - AuditLogChangeKey: Clone, + AuditLogChangeKey: AsRef, + Clone, Copy, Debug, Deserialize<'static>, - Display, Eq, + FromStr, Hash, PartialEq, Send, Serialize, - Sync + Sync, + ToString, + TryFrom<&'static str>, ); #[test] fn name() { - assert_eq!("afk_channel_id", AuditLogChangeKey::AfkChannelId.name()); - assert_eq!("afk_timeout", AuditLogChangeKey::AfkTimeout.name()); - assert_eq!("allow", AuditLogChangeKey::Allow.name()); - assert_eq!("application_id", AuditLogChangeKey::ApplicationId.name()); - assert_eq!("avatar_hash", AuditLogChangeKey::AvatarHash.name()); - assert_eq!("banner_hash", AuditLogChangeKey::BannerHash.name()); - assert_eq!("bitrate", AuditLogChangeKey::Bitrate.name()); - assert_eq!("channel_id", AuditLogChangeKey::ChannelId.name()); - assert_eq!("code", AuditLogChangeKey::Code.name()); - assert_eq!("color", AuditLogChangeKey::Color.name()); - assert_eq!("command_id", AuditLogChangeKey::CommandId.name()); + assert_eq!("afk_channel_id", AuditLogChangeKey::AFK_CHANNEL_ID.get()); + assert_eq!("afk_timeout", AuditLogChangeKey::AFK_TIMEOUT.get()); + assert_eq!("allow", AuditLogChangeKey::ALLOW.get()); + assert_eq!("application_id", AuditLogChangeKey::APPLICATION_ID.get()); + assert_eq!("avatar_hash", AuditLogChangeKey::AVATAR_HASH.get()); + assert_eq!("banner_hash", AuditLogChangeKey::BANNER_HASH.get()); + assert_eq!("bitrate", AuditLogChangeKey::BITRATE.get()); + assert_eq!("channel_id", AuditLogChangeKey::CHANNEL_ID.get()); + assert_eq!("code", AuditLogChangeKey::CODE.get()); + assert_eq!("color", AuditLogChangeKey::COLOR.get()); + assert_eq!("command_id", AuditLogChangeKey::COMMAND_ID.get()); assert_eq!( "communication_disabled_until", - AuditLogChangeKey::CommunicationDisabledUntil.name() + AuditLogChangeKey::COMMUNICATION_DISABLED_UNTIL.get() ); - assert_eq!("deaf", AuditLogChangeKey::Deaf.name()); + assert_eq!("deaf", AuditLogChangeKey::DEAF.get()); assert_eq!( "default_message_notifications", - AuditLogChangeKey::DefaultMessageNotifications.name() + AuditLogChangeKey::DEFAULT_MESSAGE_NOTIFICATIONS.get() ); - assert_eq!("deny", AuditLogChangeKey::Deny.name()); - assert_eq!("description", AuditLogChangeKey::Description.name()); + assert_eq!("deny", AuditLogChangeKey::DENY.get()); + assert_eq!("description", AuditLogChangeKey::DESCRIPTION.get()); assert_eq!( "discovery_splash_hash", - AuditLogChangeKey::DiscoverySplashHash.name() + AuditLogChangeKey::DISCOVERY_SPLASH_HASH.get() ); assert_eq!( "enable_emoticons", - AuditLogChangeKey::EnableEmoticons.name() + AuditLogChangeKey::ENABLE_EMOTICONS.get() ); - assert_eq!("expire_behavior", AuditLogChangeKey::ExpireBehavior.name()); + assert_eq!("expire_behavior", AuditLogChangeKey::EXPIRE_BEHAVIOR.get()); assert_eq!( "expire_grace_period", - AuditLogChangeKey::ExpireGracePeriod.name() + AuditLogChangeKey::EXPIRE_GRACE_PERIOD.get() ); assert_eq!( "explicit_content_filter", - AuditLogChangeKey::ExplicitContentFilter.name() - ); - assert_eq!("hoist", AuditLogChangeKey::Hoist.name()); - assert_eq!("icon_hash", AuditLogChangeKey::IconHash.name()); - assert_eq!("id", AuditLogChangeKey::Id.name()); - assert_eq!("image_hash", AuditLogChangeKey::ImageHash.name()); - assert_eq!("invitable", AuditLogChangeKey::Invitable.name()); - assert_eq!("inviter_id", AuditLogChangeKey::InviterId.name()); - assert_eq!("max_age", AuditLogChangeKey::MaxAge.name()); - assert_eq!("max_uses", AuditLogChangeKey::MaxUses.name()); - assert_eq!("mentionable", AuditLogChangeKey::Mentionable.name()); - assert_eq!("mfa_level", AuditLogChangeKey::MfaLevel.name()); - assert_eq!("mute", AuditLogChangeKey::Mute.name()); - assert_eq!("name", AuditLogChangeKey::Name.name()); - assert_eq!("nick", AuditLogChangeKey::Nick.name()); - assert_eq!("nsfw_level", AuditLogChangeKey::NsfwLevel.name()); - assert_eq!("owner_id", AuditLogChangeKey::OwnerId.name()); - assert_eq!("permissions", AuditLogChangeKey::Permissions.name()); - assert_eq!("position", AuditLogChangeKey::Position.name()); + AuditLogChangeKey::EXPLICIT_CONTENT_FILTER.get() + ); + assert_eq!("hoist", AuditLogChangeKey::HOIST.get()); + assert_eq!("icon_hash", AuditLogChangeKey::ICON_HASH.get()); + assert_eq!("id", AuditLogChangeKey::ID.get()); + assert_eq!("image_hash", AuditLogChangeKey::IMAGE_HASH.get()); + assert_eq!("invitable", AuditLogChangeKey::INVITABLE.get()); + assert_eq!("inviter_id", AuditLogChangeKey::INVITER_ID.get()); + assert_eq!("max_age", AuditLogChangeKey::MAX_AGE.get()); + assert_eq!("max_uses", AuditLogChangeKey::MAX_USES.get()); + assert_eq!("mentionable", AuditLogChangeKey::MENTIONABLE.get()); + assert_eq!("mfa_level", AuditLogChangeKey::MFA_LEVEL.get()); + assert_eq!("mute", AuditLogChangeKey::MUTE.get()); + assert_eq!("name", AuditLogChangeKey::NAME.get()); + assert_eq!("nick", AuditLogChangeKey::NICK.get()); + assert_eq!("nsfw_level", AuditLogChangeKey::NSFW_LEVEL.get()); + assert_eq!("owner_id", AuditLogChangeKey::OWNER_ID.get()); + assert_eq!("permissions", AuditLogChangeKey::PERMISSIONS.get()); + assert_eq!("position", AuditLogChangeKey::POSITION.get()); assert_eq!( "preferred_locale", - AuditLogChangeKey::PreferredLocale.name() + AuditLogChangeKey::PREFERRED_LOCALE.get() ); - assert_eq!("privacy_level", AuditLogChangeKey::PrivacyLevel.name()); + assert_eq!("privacy_level", AuditLogChangeKey::PRIVACY_LEVEL.get()); assert_eq!( "prune_delete_days", - AuditLogChangeKey::PruneDeleteDays.name() + AuditLogChangeKey::PRUNE_DELETE_DAYS.get() ); assert_eq!( "public_updates_channel_id", - AuditLogChangeKey::PublicUpdatesChannelId.name() + AuditLogChangeKey::PUBLIC_UPDATES_CHANNEL_ID.get() ); assert_eq!( "rate_limit_per_user", - AuditLogChangeKey::RateLimitPerUser.name() + AuditLogChangeKey::RATE_LIMIT_PER_USER.get() + ); + assert_eq!("$add", AuditLogChangeKey::ROLE_ADDED.get()); + assert_eq!("$remove", AuditLogChangeKey::ROLE_REMOVED.get()); + assert_eq!( + "rules_channel_id", + AuditLogChangeKey::RULES_CHANNEL_ID.get() ); - assert_eq!("$add", AuditLogChangeKey::RoleAdded.name()); - assert_eq!("$remove", AuditLogChangeKey::RoleRemoved.name()); - assert_eq!("rules_channel_id", AuditLogChangeKey::RulesChannelId.name()); - assert_eq!("splash_hash", AuditLogChangeKey::SplashHash.name()); + assert_eq!("splash_hash", AuditLogChangeKey::SPLASH_HASH.get()); assert_eq!( "system_channel_id", - AuditLogChangeKey::SystemChannelId.name() - ); - assert_eq!("temporary", AuditLogChangeKey::Temporary.name()); - assert_eq!("topic", AuditLogChangeKey::Topic.name()); - assert_eq!("type", AuditLogChangeKey::Type.name()); - assert_eq!("user_limit", AuditLogChangeKey::UserLimit.name()); - assert_eq!("uses", AuditLogChangeKey::Uses.name()); - assert_eq!("vanity_url_code", AuditLogChangeKey::VanityUrlCode.name()); + AuditLogChangeKey::SYSTEM_CHANNEL_ID.get() + ); + assert_eq!("temporary", AuditLogChangeKey::TEMPORARY.get()); + assert_eq!("topic", AuditLogChangeKey::TOPIC.get()); + assert_eq!("type", AuditLogChangeKey::TYPE.get()); + assert_eq!("user_limit", AuditLogChangeKey::USER_LIMIT.get()); + assert_eq!("uses", AuditLogChangeKey::USES.get()); + assert_eq!("vanity_url_code", AuditLogChangeKey::VANITY_URL_CODE.get()); assert_eq!( "verification_level", - AuditLogChangeKey::VerificationLevel.name() + AuditLogChangeKey::VERIFICATION_LEVEL.get() ); assert_eq!( "widget_channel_id", - AuditLogChangeKey::WidgetChannelId.name() + AuditLogChangeKey::WIDGET_CHANNEL_ID.get() ); - assert_eq!("widget_enabled", AuditLogChangeKey::WidgetEnabled.name()); + assert_eq!("widget_enabled", AuditLogChangeKey::WIDGET_ENABLED.get()); } #[allow(clippy::too_many_lines)] #[test] fn serde() { serde_test::assert_tokens( - &AuditLogChangeKey::AfkChannelId, - &[Token::UnitVariant { - name: "AuditLogChangeKey", - variant: "afk_channel_id", - }], - ); - serde_test::assert_tokens( - &AuditLogChangeKey::AfkTimeout, - &[Token::UnitVariant { - name: "AuditLogChangeKey", - variant: "afk_timeout", - }], - ); - serde_test::assert_tokens( - &AuditLogChangeKey::Allow, - &[Token::UnitVariant { - name: "AuditLogChangeKey", - variant: "allow", - }], - ); - serde_test::assert_tokens( - &AuditLogChangeKey::ApplicationId, - &[Token::UnitVariant { - name: "AuditLogChangeKey", - variant: "application_id", - }], - ); - serde_test::assert_tokens( - &AuditLogChangeKey::AvatarHash, - &[Token::UnitVariant { - name: "AuditLogChangeKey", - variant: "avatar_hash", - }], - ); - serde_test::assert_tokens( - &AuditLogChangeKey::BannerHash, - &[Token::UnitVariant { - name: "AuditLogChangeKey", - variant: "banner_hash", - }], - ); - serde_test::assert_tokens( - &AuditLogChangeKey::Bitrate, - &[Token::UnitVariant { - name: "AuditLogChangeKey", - variant: "bitrate", - }], - ); - serde_test::assert_tokens( - &AuditLogChangeKey::ChannelId, - &[Token::UnitVariant { - name: "AuditLogChangeKey", - variant: "channel_id", - }], - ); - serde_test::assert_tokens( - &AuditLogChangeKey::Code, - &[Token::UnitVariant { - name: "AuditLogChangeKey", - variant: "code", - }], - ); - serde_test::assert_tokens( - &AuditLogChangeKey::Color, - &[Token::UnitVariant { - name: "AuditLogChangeKey", - variant: "color", - }], - ); - serde_test::assert_tokens( - &AuditLogChangeKey::CommandId, - &[Token::UnitVariant { - name: "AuditLogChangeKey", - variant: "command_id", - }], - ); - serde_test::assert_tokens( - &AuditLogChangeKey::CommunicationDisabledUntil, - &[Token::UnitVariant { - name: "AuditLogChangeKey", - variant: "communication_disabled_until", - }], - ); - serde_test::assert_tokens( - &AuditLogChangeKey::Deaf, - &[Token::UnitVariant { - name: "AuditLogChangeKey", - variant: "deaf", - }], - ); - serde_test::assert_tokens( - &AuditLogChangeKey::DefaultMessageNotifications, - &[Token::UnitVariant { - name: "AuditLogChangeKey", - variant: "default_message_notifications", - }], - ); - serde_test::assert_tokens( - &AuditLogChangeKey::Deny, - &[Token::UnitVariant { - name: "AuditLogChangeKey", - variant: "deny", - }], - ); - serde_test::assert_tokens( - &AuditLogChangeKey::Description, - &[Token::UnitVariant { - name: "AuditLogChangeKey", - variant: "description", - }], - ); - serde_test::assert_tokens( - &AuditLogChangeKey::DiscoverySplashHash, - &[Token::UnitVariant { - name: "AuditLogChangeKey", - variant: "discovery_splash_hash", - }], - ); - serde_test::assert_tokens( - &AuditLogChangeKey::EnableEmoticons, - &[Token::UnitVariant { - name: "AuditLogChangeKey", - variant: "enable_emoticons", - }], - ); - serde_test::assert_tokens( - &AuditLogChangeKey::ExpireBehavior, - &[Token::UnitVariant { - name: "AuditLogChangeKey", - variant: "expire_behavior", - }], - ); - serde_test::assert_tokens( - &AuditLogChangeKey::ExpireGracePeriod, - &[Token::UnitVariant { - name: "AuditLogChangeKey", - variant: "expire_grace_period", - }], - ); - serde_test::assert_tokens( - &AuditLogChangeKey::ExplicitContentFilter, - &[Token::UnitVariant { - name: "AuditLogChangeKey", - variant: "explicit_content_filter", - }], - ); - serde_test::assert_tokens( - &AuditLogChangeKey::Hoist, - &[Token::UnitVariant { - name: "AuditLogChangeKey", - variant: "hoist", - }], - ); - serde_test::assert_tokens( - &AuditLogChangeKey::IconHash, - &[Token::UnitVariant { - name: "AuditLogChangeKey", - variant: "icon_hash", - }], - ); - serde_test::assert_tokens( - &AuditLogChangeKey::Id, - &[Token::UnitVariant { - name: "AuditLogChangeKey", - variant: "id", - }], - ); - serde_test::assert_tokens( - &AuditLogChangeKey::ImageHash, - &[Token::UnitVariant { - name: "AuditLogChangeKey", - variant: "image_hash", - }], - ); - serde_test::assert_tokens( - &AuditLogChangeKey::Invitable, - &[Token::UnitVariant { - name: "AuditLogChangeKey", - variant: "invitable", - }], - ); - serde_test::assert_tokens( - &AuditLogChangeKey::InviterId, - &[Token::UnitVariant { - name: "AuditLogChangeKey", - variant: "inviter_id", - }], - ); - serde_test::assert_tokens( - &AuditLogChangeKey::MaxAge, - &[Token::UnitVariant { - name: "AuditLogChangeKey", - variant: "max_age", - }], - ); - serde_test::assert_tokens( - &AuditLogChangeKey::MaxUses, - &[Token::UnitVariant { - name: "AuditLogChangeKey", - variant: "max_uses", - }], - ); - serde_test::assert_tokens( - &AuditLogChangeKey::Mentionable, - &[Token::UnitVariant { - name: "AuditLogChangeKey", - variant: "mentionable", - }], - ); - serde_test::assert_tokens( - &AuditLogChangeKey::MfaLevel, - &[Token::UnitVariant { - name: "AuditLogChangeKey", - variant: "mfa_level", - }], - ); - serde_test::assert_tokens( - &AuditLogChangeKey::Mute, - &[Token::UnitVariant { - name: "AuditLogChangeKey", - variant: "mute", - }], - ); - serde_test::assert_tokens( - &AuditLogChangeKey::Name, - &[Token::UnitVariant { - name: "AuditLogChangeKey", - variant: "name", - }], - ); - serde_test::assert_tokens( - &AuditLogChangeKey::Nick, - &[Token::UnitVariant { - name: "AuditLogChangeKey", - variant: "nick", - }], - ); - serde_test::assert_tokens( - &AuditLogChangeKey::NsfwLevel, - &[Token::UnitVariant { - name: "AuditLogChangeKey", - variant: "nsfw_level", - }], - ); - serde_test::assert_tokens( - &AuditLogChangeKey::OwnerId, - &[Token::UnitVariant { - name: "AuditLogChangeKey", - variant: "owner_id", - }], - ); - serde_test::assert_tokens( - &AuditLogChangeKey::Permissions, - &[Token::UnitVariant { - name: "AuditLogChangeKey", - variant: "permissions", - }], - ); - serde_test::assert_tokens( - &AuditLogChangeKey::Position, - &[Token::UnitVariant { - name: "AuditLogChangeKey", - variant: "position", - }], - ); - serde_test::assert_tokens( - &AuditLogChangeKey::PreferredLocale, - &[Token::UnitVariant { - name: "AuditLogChangeKey", - variant: "preferred_locale", - }], - ); - serde_test::assert_tokens( - &AuditLogChangeKey::PrivacyLevel, - &[Token::UnitVariant { - name: "AuditLogChangeKey", - variant: "privacy_level", - }], - ); - serde_test::assert_tokens( - &AuditLogChangeKey::PruneDeleteDays, - &[Token::UnitVariant { - name: "AuditLogChangeKey", - variant: "prune_delete_days", - }], - ); - serde_test::assert_tokens( - &AuditLogChangeKey::PublicUpdatesChannelId, - &[Token::UnitVariant { - name: "AuditLogChangeKey", - variant: "public_updates_channel_id", - }], - ); - serde_test::assert_tokens( - &AuditLogChangeKey::RateLimitPerUser, - &[Token::UnitVariant { - name: "AuditLogChangeKey", - variant: "rate_limit_per_user", - }], - ); - serde_test::assert_tokens( - &AuditLogChangeKey::RoleAdded, - &[Token::UnitVariant { - name: "AuditLogChangeKey", - variant: "$add", - }], - ); - serde_test::assert_tokens( - &AuditLogChangeKey::RoleRemoved, - &[Token::UnitVariant { - name: "AuditLogChangeKey", - variant: "$remove", - }], - ); - serde_test::assert_tokens( - &AuditLogChangeKey::RulesChannelId, - &[Token::UnitVariant { - name: "AuditLogChangeKey", - variant: "rules_channel_id", - }], - ); - serde_test::assert_tokens( - &AuditLogChangeKey::SplashHash, - &[Token::UnitVariant { - name: "AuditLogChangeKey", - variant: "splash_hash", - }], - ); - serde_test::assert_tokens( - &AuditLogChangeKey::SystemChannelId, - &[Token::UnitVariant { - name: "AuditLogChangeKey", - variant: "system_channel_id", - }], - ); - serde_test::assert_tokens( - &AuditLogChangeKey::Temporary, - &[Token::UnitVariant { - name: "AuditLogChangeKey", - variant: "temporary", - }], - ); - serde_test::assert_tokens( - &AuditLogChangeKey::Topic, - &[Token::UnitVariant { - name: "AuditLogChangeKey", - variant: "topic", - }], - ); - serde_test::assert_tokens( - &AuditLogChangeKey::Type, - &[Token::UnitVariant { - name: "AuditLogChangeKey", - variant: "type", - }], - ); - serde_test::assert_tokens( - &AuditLogChangeKey::UserLimit, - &[Token::UnitVariant { - name: "AuditLogChangeKey", - variant: "user_limit", - }], - ); - serde_test::assert_tokens( - &AuditLogChangeKey::Uses, - &[Token::UnitVariant { - name: "AuditLogChangeKey", - variant: "uses", - }], - ); - serde_test::assert_tokens( - &AuditLogChangeKey::VanityUrlCode, - &[Token::UnitVariant { - name: "AuditLogChangeKey", - variant: "vanity_url_code", - }], - ); - serde_test::assert_tokens( - &AuditLogChangeKey::VerificationLevel, - &[Token::UnitVariant { - name: "AuditLogChangeKey", - variant: "verification_level", - }], - ); - serde_test::assert_tokens( - &AuditLogChangeKey::WidgetChannelId, - &[Token::UnitVariant { - name: "AuditLogChangeKey", - variant: "widget_channel_id", - }], - ); - serde_test::assert_tokens( - &AuditLogChangeKey::WidgetEnabled, - &[Token::UnitVariant { - name: "AuditLogChangeKey", - variant: "widget_enabled", - }], + &AuditLogChangeKey::AFK_CHANNEL_ID, + &[ + Token::NewtypeStruct { + name: "AuditLogChangeKey", + }, + Token::Str("afk_channel_id"), + ], + ); + serde_test::assert_tokens( + &AuditLogChangeKey::AFK_TIMEOUT, + &[ + Token::NewtypeStruct { + name: "AuditLogChangeKey", + }, + Token::Str("afk_timeout"), + ], + ); + serde_test::assert_tokens( + &AuditLogChangeKey::ALLOW, + &[ + Token::NewtypeStruct { + name: "AuditLogChangeKey", + }, + Token::Str("allow"), + ], + ); + serde_test::assert_tokens( + &AuditLogChangeKey::APPLICATION_ID, + &[ + Token::NewtypeStruct { + name: "AuditLogChangeKey", + }, + Token::Str("application_id"), + ], + ); + serde_test::assert_tokens( + &AuditLogChangeKey::AVATAR_HASH, + &[ + Token::NewtypeStruct { + name: "AuditLogChangeKey", + }, + Token::Str("avatar_hash"), + ], + ); + serde_test::assert_tokens( + &AuditLogChangeKey::BANNER_HASH, + &[ + Token::NewtypeStruct { + name: "AuditLogChangeKey", + }, + Token::Str("banner_hash"), + ], + ); + serde_test::assert_tokens( + &AuditLogChangeKey::BITRATE, + &[ + Token::NewtypeStruct { + name: "AuditLogChangeKey", + }, + Token::Str("bitrate"), + ], + ); + serde_test::assert_tokens( + &AuditLogChangeKey::CHANNEL_ID, + &[ + Token::NewtypeStruct { + name: "AuditLogChangeKey", + }, + Token::Str("channel_id"), + ], + ); + serde_test::assert_tokens( + &AuditLogChangeKey::CODE, + &[ + Token::NewtypeStruct { + name: "AuditLogChangeKey", + }, + Token::Str("code"), + ], + ); + serde_test::assert_tokens( + &AuditLogChangeKey::COLOR, + &[ + Token::NewtypeStruct { + name: "AuditLogChangeKey", + }, + Token::Str("color"), + ], + ); + serde_test::assert_tokens( + &AuditLogChangeKey::COMMAND_ID, + &[ + Token::NewtypeStruct { + name: "AuditLogChangeKey", + }, + Token::Str("command_id"), + ], + ); + serde_test::assert_tokens( + &AuditLogChangeKey::COMMUNICATION_DISABLED_UNTIL, + &[ + Token::NewtypeStruct { + name: "AuditLogChangeKey", + }, + Token::Str("communication_disabled_until"), + ], + ); + serde_test::assert_tokens( + &AuditLogChangeKey::DEAF, + &[ + Token::NewtypeStruct { + name: "AuditLogChangeKey", + }, + Token::Str("deaf"), + ], + ); + serde_test::assert_tokens( + &AuditLogChangeKey::DEFAULT_MESSAGE_NOTIFICATIONS, + &[ + Token::NewtypeStruct { + name: "AuditLogChangeKey", + }, + Token::Str("default_message_notifications"), + ], + ); + serde_test::assert_tokens( + &AuditLogChangeKey::DENY, + &[ + Token::NewtypeStruct { + name: "AuditLogChangeKey", + }, + Token::Str("deny"), + ], + ); + serde_test::assert_tokens( + &AuditLogChangeKey::DESCRIPTION, + &[ + Token::NewtypeStruct { + name: "AuditLogChangeKey", + }, + Token::Str("description"), + ], + ); + serde_test::assert_tokens( + &AuditLogChangeKey::DISCOVERY_SPLASH_HASH, + &[ + Token::NewtypeStruct { + name: "AuditLogChangeKey", + }, + Token::Str("discovery_splash_hash"), + ], + ); + serde_test::assert_tokens( + &AuditLogChangeKey::ENABLE_EMOTICONS, + &[ + Token::NewtypeStruct { + name: "AuditLogChangeKey", + }, + Token::Str("enable_emoticons"), + ], + ); + serde_test::assert_tokens( + &AuditLogChangeKey::EXPIRE_BEHAVIOR, + &[ + Token::NewtypeStruct { + name: "AuditLogChangeKey", + }, + Token::Str("expire_behavior"), + ], + ); + serde_test::assert_tokens( + &AuditLogChangeKey::EXPIRE_GRACE_PERIOD, + &[ + Token::NewtypeStruct { + name: "AuditLogChangeKey", + }, + Token::Str("expire_grace_period"), + ], + ); + serde_test::assert_tokens( + &AuditLogChangeKey::EXPLICIT_CONTENT_FILTER, + &[ + Token::NewtypeStruct { + name: "AuditLogChangeKey", + }, + Token::Str("explicit_content_filter"), + ], + ); + serde_test::assert_tokens( + &AuditLogChangeKey::HOIST, + &[ + Token::NewtypeStruct { + name: "AuditLogChangeKey", + }, + Token::Str("hoist"), + ], + ); + serde_test::assert_tokens( + &AuditLogChangeKey::ICON_HASH, + &[ + Token::NewtypeStruct { + name: "AuditLogChangeKey", + }, + Token::Str("icon_hash"), + ], + ); + serde_test::assert_tokens( + &AuditLogChangeKey::ID, + &[ + Token::NewtypeStruct { + name: "AuditLogChangeKey", + }, + Token::Str("id"), + ], + ); + serde_test::assert_tokens( + &AuditLogChangeKey::IMAGE_HASH, + &[ + Token::NewtypeStruct { + name: "AuditLogChangeKey", + }, + Token::Str("image_hash"), + ], + ); + serde_test::assert_tokens( + &AuditLogChangeKey::INVITABLE, + &[ + Token::NewtypeStruct { + name: "AuditLogChangeKey", + }, + Token::Str("invitable"), + ], + ); + serde_test::assert_tokens( + &AuditLogChangeKey::INVITER_ID, + &[ + Token::NewtypeStruct { + name: "AuditLogChangeKey", + }, + Token::Str("inviter_id"), + ], + ); + serde_test::assert_tokens( + &AuditLogChangeKey::MAX_AGE, + &[ + Token::NewtypeStruct { + name: "AuditLogChangeKey", + }, + Token::Str("max_age"), + ], + ); + serde_test::assert_tokens( + &AuditLogChangeKey::MAX_USES, + &[ + Token::NewtypeStruct { + name: "AuditLogChangeKey", + }, + Token::Str("max_uses"), + ], + ); + serde_test::assert_tokens( + &AuditLogChangeKey::MENTIONABLE, + &[ + Token::NewtypeStruct { + name: "AuditLogChangeKey", + }, + Token::Str("mentionable"), + ], + ); + serde_test::assert_tokens( + &AuditLogChangeKey::MFA_LEVEL, + &[ + Token::NewtypeStruct { + name: "AuditLogChangeKey", + }, + Token::Str("mfa_level"), + ], + ); + serde_test::assert_tokens( + &AuditLogChangeKey::MUTE, + &[ + Token::NewtypeStruct { + name: "AuditLogChangeKey", + }, + Token::Str("mute"), + ], + ); + serde_test::assert_tokens( + &AuditLogChangeKey::NAME, + &[ + Token::NewtypeStruct { + name: "AuditLogChangeKey", + }, + Token::Str("name"), + ], + ); + serde_test::assert_tokens( + &AuditLogChangeKey::NICK, + &[ + Token::NewtypeStruct { + name: "AuditLogChangeKey", + }, + Token::Str("nick"), + ], + ); + serde_test::assert_tokens( + &AuditLogChangeKey::NSFW_LEVEL, + &[ + Token::NewtypeStruct { + name: "AuditLogChangeKey", + }, + Token::Str("nsfw_level"), + ], + ); + serde_test::assert_tokens( + &AuditLogChangeKey::OWNER_ID, + &[ + Token::NewtypeStruct { + name: "AuditLogChangeKey", + }, + Token::Str("owner_id"), + ], + ); + serde_test::assert_tokens( + &AuditLogChangeKey::PERMISSIONS, + &[ + Token::NewtypeStruct { + name: "AuditLogChangeKey", + }, + Token::Str("permissions"), + ], + ); + serde_test::assert_tokens( + &AuditLogChangeKey::POSITION, + &[ + Token::NewtypeStruct { + name: "AuditLogChangeKey", + }, + Token::Str("position"), + ], + ); + serde_test::assert_tokens( + &AuditLogChangeKey::PREFERRED_LOCALE, + &[ + Token::NewtypeStruct { + name: "AuditLogChangeKey", + }, + Token::Str("preferred_locale"), + ], + ); + serde_test::assert_tokens( + &AuditLogChangeKey::PRIVACY_LEVEL, + &[ + Token::NewtypeStruct { + name: "AuditLogChangeKey", + }, + Token::Str("privacy_level"), + ], + ); + serde_test::assert_tokens( + &AuditLogChangeKey::PRUNE_DELETE_DAYS, + &[ + Token::NewtypeStruct { + name: "AuditLogChangeKey", + }, + Token::Str("prune_delete_days"), + ], + ); + serde_test::assert_tokens( + &AuditLogChangeKey::PUBLIC_UPDATES_CHANNEL_ID, + &[ + Token::NewtypeStruct { + name: "AuditLogChangeKey", + }, + Token::Str("public_updates_channel_id"), + ], + ); + serde_test::assert_tokens( + &AuditLogChangeKey::RATE_LIMIT_PER_USER, + &[ + Token::NewtypeStruct { + name: "AuditLogChangeKey", + }, + Token::Str("rate_limit_per_user"), + ], + ); + serde_test::assert_tokens( + &AuditLogChangeKey::ROLE_ADDED, + &[ + Token::NewtypeStruct { + name: "AuditLogChangeKey", + }, + Token::Str("$add"), + ], + ); + serde_test::assert_tokens( + &AuditLogChangeKey::ROLE_REMOVED, + &[ + Token::NewtypeStruct { + name: "AuditLogChangeKey", + }, + Token::Str("$remove"), + ], + ); + serde_test::assert_tokens( + &AuditLogChangeKey::RULES_CHANNEL_ID, + &[ + Token::NewtypeStruct { + name: "AuditLogChangeKey", + }, + Token::Str("rules_channel_id"), + ], + ); + serde_test::assert_tokens( + &AuditLogChangeKey::SPLASH_HASH, + &[ + Token::NewtypeStruct { + name: "AuditLogChangeKey", + }, + Token::Str("splash_hash"), + ], + ); + serde_test::assert_tokens( + &AuditLogChangeKey::SYSTEM_CHANNEL_ID, + &[ + Token::NewtypeStruct { + name: "AuditLogChangeKey", + }, + Token::Str("system_channel_id"), + ], + ); + serde_test::assert_tokens( + &AuditLogChangeKey::TEMPORARY, + &[ + Token::NewtypeStruct { + name: "AuditLogChangeKey", + }, + Token::Str("temporary"), + ], + ); + serde_test::assert_tokens( + &AuditLogChangeKey::TOPIC, + &[ + Token::NewtypeStruct { + name: "AuditLogChangeKey", + }, + Token::Str("topic"), + ], + ); + serde_test::assert_tokens( + &AuditLogChangeKey::TYPE, + &[ + Token::NewtypeStruct { + name: "AuditLogChangeKey", + }, + Token::Str("type"), + ], + ); + serde_test::assert_tokens( + &AuditLogChangeKey::USER_LIMIT, + &[ + Token::NewtypeStruct { + name: "AuditLogChangeKey", + }, + Token::Str("user_limit"), + ], + ); + serde_test::assert_tokens( + &AuditLogChangeKey::USES, + &[ + Token::NewtypeStruct { + name: "AuditLogChangeKey", + }, + Token::Str("uses"), + ], + ); + serde_test::assert_tokens( + &AuditLogChangeKey::VANITY_URL_CODE, + &[ + Token::NewtypeStruct { + name: "AuditLogChangeKey", + }, + Token::Str("vanity_url_code"), + ], + ); + serde_test::assert_tokens( + &AuditLogChangeKey::VERIFICATION_LEVEL, + &[ + Token::NewtypeStruct { + name: "AuditLogChangeKey", + }, + Token::Str("verification_level"), + ], + ); + serde_test::assert_tokens( + &AuditLogChangeKey::WIDGET_CHANNEL_ID, + &[ + Token::NewtypeStruct { + name: "AuditLogChangeKey", + }, + Token::Str("widget_channel_id"), + ], + ); + serde_test::assert_tokens( + &AuditLogChangeKey::WIDGET_ENABLED, + &[ + Token::NewtypeStruct { + name: "AuditLogChangeKey", + }, + Token::Str("widget_enabled"), + ], ); } } diff --git a/twilight-model/src/guild/audit_log/entry.rs b/twilight-model/src/guild/audit_log/entry.rs index f9dfe211f83..962945f3df8 100644 --- a/twilight-model/src/guild/audit_log/entry.rs +++ b/twilight-model/src/guild/audit_log/entry.rs @@ -77,7 +77,7 @@ mod tests { #[test] fn serde() { let value = AuditLogEntry { - action_type: AuditLogEventType::GuildUpdate, + action_type: AuditLogEventType::GUILD_UPDATE, changes: Vec::from([AuditLogChange::IconHash { new: None, old: Some(image_hash::ICON), @@ -98,7 +98,10 @@ mod tests { len: 6, }, Token::Str("action_type"), - Token::U16(AuditLogEventType::GuildUpdate.into()), + Token::NewtypeStruct { + name: "AuditLogEventType", + }, + Token::U16(AuditLogEventType::GUILD_UPDATE.get()), Token::Str("changes"), Token::Seq { len: Some(1) }, Token::Struct { diff --git a/twilight-model/src/guild/audit_log/event_type.rs b/twilight-model/src/guild/audit_log/event_type.rs index e6e2672abeb..88f1b1f13b5 100644 --- a/twilight-model/src/guild/audit_log/event_type.rs +++ b/twilight-model/src/guild/audit_log/event_type.rs @@ -3,357 +3,352 @@ use serde::{Deserialize, Serialize}; /// Action to cause an [`AuditLogEntry`]. /// /// [`AuditLogEntry`]: super::AuditLogEntry -#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)] -#[serde(from = "u16", into = "u16")] -pub enum AuditLogEventType { +#[derive(Clone, Copy, Deserialize, Eq, Hash, PartialEq, Serialize)] +pub struct AuditLogEventType(u16); + +impl AuditLogEventType { /// [Guild] was updated. /// /// [Guild]: crate::guild::Guild - GuildUpdate, + pub const GUILD_UPDATE: Self = Self::new(1); + /// [Channel] was created. /// /// [Channel]: crate::channel::Channel - ChannelCreate, + pub const CHANNEL_CREATE: Self = Self::new(10); + /// [Channel] was updated. /// /// [Channel]: crate::channel::Channel - ChannelUpdate, + pub const CHANNEL_UPDATE: Self = Self::new(11); + /// [Channel] was deleted. /// /// [Channel]: crate::channel::Channel - ChannelDelete, + pub const CHANNEL_DELETE: Self = Self::new(12); + /// [Permission overwrite] for a [channel] was created. /// /// [channel]: crate::channel::Channel /// [Permission overwrite]: crate::channel::permission_overwrite::PermissionOverwrite - ChannelOverwriteCreate, + pub const CHANNEL_OVERWRITE_CREATE: Self = Self::new(13); + /// [Permission overwrite] for a [channel] was updated. /// /// [channel]: crate::channel::Channel /// [Permission overwrite]: crate::channel::permission_overwrite::PermissionOverwrite - ChannelOverwriteUpdate, + pub const CHANNEL_OVERWRITE_UPDATE: Self = Self::new(14); + /// [Permission overwrite] for a [channel] was deleted. /// /// [channel]: crate::channel::Channel /// [Permission overwrite]: crate::channel::permission_overwrite::PermissionOverwrite - ChannelOverwriteDelete, + pub const CHANNEL_OVERWRITE_DELETE: Self = Self::new(15); + /// [Member] was kicked. /// /// [Member]: crate::guild::Member - MemberKick, + pub const MEMBER_KICK: Self = Self::new(20); + /// [Member] prune began. /// /// [Member]: crate::guild::Member - MemberPrune, + pub const MEMBER_PRUNE: Self = Self::new(21); + /// [Member] was banned. /// /// [Member]: crate::guild::Member - MemberBanAdd, + pub const MEMBER_BAN_ADD: Self = Self::new(22); + /// [Member]'s [ban] was removed. /// /// [ban]: crate::guild::Ban /// [Member]: crate::guild::Member - MemberBanRemove, + pub const MEMBER_BAN_REMOVE: Self = Self::new(23); + /// [Member] was updated. /// /// [Member]: crate::guild::Member - MemberUpdate, + pub const MEMBER_UPDATE: Self = Self::new(24); + /// [Member] either had a [role] attached or removed. /// /// [Member]: crate::guild::Member /// [role]: crate::guild::Role - MemberRoleUpdate, + pub const MEMBER_ROLE_UPDATE: Self = Self::new(25); + /// [Member] was moved between voice [channel]s. /// /// [Member]: crate::guild::Member /// [channel]: crate::channel::Channel - MemberMove, + pub const MEMBER_MOVE: Self = Self::new(26); + /// [Member] was disconnected from a voice [channel]. /// /// [Member]: crate::guild::Member /// [channel]: crate::channel::Channel - MemberDisconnect, + pub const MEMBER_DISCONNECT: Self = Self::new(27); + /// [Bot user] was added to a [guild]. /// /// [Bot user]: crate::user::User::bot /// [guild]: crate::guild::Guild - BotAdd, + pub const BOT_ADD: Self = Self::new(28); + /// [Role] was created. /// /// [Role]: crate::guild::Role - RoleCreate, + pub const ROLE_CREATE: Self = Self::new(30); + /// [Role] was updated. /// /// [Role]: crate::guild::Role - RoleUpdate, + pub const ROLE_UPDATE: Self = Self::new(31); + /// [Role] was deleted. /// /// [Role]: crate::guild::Role - RoleDelete, + pub const ROLE_DELETE: Self = Self::new(32); + /// [Invite] was created. /// /// [Invite]: crate::guild::invite::Invite - InviteCreate, + pub const INVITE_CREATE: Self = Self::new(40); + /// [Invite] was updated. /// /// [Invite]: crate::guild::invite::Invite - InviteUpdate, + pub const INVITE_UPDATE: Self = Self::new(41); + /// [Invite] was deleted. /// /// [Invite]: crate::guild::invite::Invite - InviteDelete, + pub const INVITE_DELETE: Self = Self::new(42); + /// [Webhook] was created. /// /// [Webhook]: crate::channel::webhook::Webhook - WebhookCreate, + pub const WEBHOOK_CREATE: Self = Self::new(50); + /// [Webhook] was updated. /// /// [Webhook]: crate::channel::webhook::Webhook - WebhookUpdate, + pub const WEBHOOK_UPDATE: Self = Self::new(51); + /// [Webhook] was deleted. /// /// [Webhook]: crate::channel::webhook::Webhook - WebhookDelete, + pub const WEBHOOK_DELETE: Self = Self::new(52); + /// [Emoji] was created. /// /// [Emoji]: crate::guild::Emoji - EmojiCreate, + pub const EMOJI_CREATE: Self = Self::new(60); + /// [Emoji] was updated. /// /// [Emoji]: crate::guild::Emoji - EmojiUpdate, + pub const EMOJI_UPDATE: Self = Self::new(61); + /// [Emoji] was deleted. /// /// [Emoji]: crate::guild::Emoji - EmojiDelete, + pub const EMOJI_DELETE: Self = Self::new(62); + /// [Message] was deleted. /// /// [Message]: crate::channel::message::Message - MessageDelete, + pub const MESSAGE_DELETE: Self = Self::new(72); + /// Multiple [messages] were deleted. /// /// [messages]: crate::channel::message::Message - MessageBulkDelete, + pub const MESSAGE_BULK_DELETE: Self = Self::new(73); + /// [Message] was pinned to a [channel]. /// /// [Message]: crate::channel::message::Message /// [channel]: crate::channel::Channel - MessagePin, + pub const MESSAGE_PIN: Self = Self::new(74); + /// [Message] was unpinned from a [channel]. /// /// [Message]: crate::channel::message::Message /// [channel]: crate::channel::Channel - MessageUnpin, + pub const MESSAGE_UNPIN: Self = Self::new(75); + /// [Integration] was created. /// /// [Integration]: crate::guild::GuildIntegration - IntegrationCreate, + pub const INTEGRATION_CREATE: Self = Self::new(80); + /// [Integration] was updated. /// /// [Integration]: crate::guild::GuildIntegration - IntegrationUpdate, + pub const INTEGRATION_UPDATE: Self = Self::new(81); + /// [Integration] was deleted. /// /// [Integration]: crate::guild::GuildIntegration - IntegrationDelete, + pub const INTEGRATION_DELETE: Self = Self::new(82); + /// [Stage instance] was created. /// /// [Stage instance]: crate::channel::stage_instance::StageInstance - StageInstanceCreate, + pub const STAGE_INSTANCE_CREATE: Self = Self::new(83); + /// [Stage instance] was updated. /// /// [Stage instance]: crate::channel::stage_instance::StageInstance - StageInstanceUpdate, + pub const STAGE_INSTANCE_UPDATE: Self = Self::new(84); + /// [Stage instance] was deleted. /// /// [Stage instance]: crate::channel::stage_instance::StageInstance - StageInstanceDelete, + pub const STAGE_INSTANCE_DELETE: Self = Self::new(85); + /// [Sticker] was created. /// /// [Sticker]: crate::channel::message::sticker::Sticker - StickerCreate, + pub const STICKER_CREATE: Self = Self::new(90); + /// [Sticker] was updated. /// /// [Sticker]: crate::channel::message::sticker::Sticker - StickerUpdate, + pub const STICKER_UPDATE: Self = Self::new(91); + /// [Sticker] was deleted. /// /// [Sticker]: crate::channel::message::sticker::Sticker - StickerDelete, + pub const STICKER_DELETE: Self = Self::new(92); + /// [`GuildScheduledEvent`] was created. /// /// [`GuildScheduledEvent`]: crate::guild::scheduled_event::GuildScheduledEvent - GuildScheduledEventCreate, + pub const GUILD_SCHEDULED_EVENT_CREATE: Self = Self::new(100); + /// [`GuildScheduledEvent`] was updated. /// /// [`GuildScheduledEvent`]: crate::guild::scheduled_event::GuildScheduledEvent - GuildScheduledEventUpdate, + pub const GUILD_SCHEDULED_EVENT_UPDATE: Self = Self::new(101); + /// [`GuildScheduledEvent`] was deleted. /// /// [`GuildScheduledEvent`]: crate::guild::scheduled_event::GuildScheduledEvent - GuildScheduledEventDelete, + pub const GUILD_SCHEDULED_EVENT_DELETE: Self = Self::new(102); + /// Thread [channel] was created. /// /// [channel]: crate::channel::Channel - ThreadCreate, + pub const THREAD_CREATE: Self = Self::new(110); + /// Thread [channel] was updated. /// /// [channel]: crate::channel::Channel - ThreadUpdate, + pub const THREAD_UPDATE: Self = Self::new(111); + /// Thread [channel] was deleted. /// /// [channel]: crate::channel::Channel - ThreadDelete, - /// A [GuildCommandPermissions] was updated. + pub const THREAD_DELETE: Self = Self::new(112); + + /// A [`GuildCommandPermissions`] was updated. /// - /// [GuildCommandPermissions]: crate::application::command::permissions::GuildCommandPermissions - ApplicationCommandPermissionUpdate, + /// [`GuildCommandPermissions`]: crate::application::command::permissions::GuildCommandPermissions + pub const APPLICATION_COMMAND_PERMISSION_UPDATE: Self = Self::new(121); + /// [`AutoModerationRule`] has been created. /// /// [`AutoModerationRule`]: crate::guild::auto_moderation::AutoModerationRule - AutoModerationRuleCreate, + pub const AUTO_MODERATION_RULE_CREATE: Self = Self::new(140); + /// [`AutoModerationRule`] has been updated. /// /// [`AutoModerationRule`]: crate::guild::auto_moderation::AutoModerationRule - AutoModerationRuleUpdate, + pub const AUTO_MODERATION_RULE_UPDATE: Self = Self::new(141); + /// [`AutoModerationRule`] has been deleted. /// /// [`AutoModerationRule`]: crate::guild::auto_moderation::AutoModerationRule - AutoModerationRuleDelete, - /// Message has been blocked by AutoMod. - AutoModerationBlockMessage, - /// Message has been flagged by AutoMod. - AutoModerationFlagToChannel, - /// A member has been timed out by AutoMod. - AutoModerationUserCommunicationDisabled, - /// Variant value is unknown to the library. - Unknown(u16), -} + pub const AUTO_MODERATION_RULE_DELETE: Self = Self::new(142); -impl From for AuditLogEventType { - fn from(value: u16) -> Self { - match value { - 1 => AuditLogEventType::GuildUpdate, - 10 => AuditLogEventType::ChannelCreate, - 11 => AuditLogEventType::ChannelUpdate, - 12 => AuditLogEventType::ChannelDelete, - 13 => AuditLogEventType::ChannelOverwriteCreate, - 14 => AuditLogEventType::ChannelOverwriteUpdate, - 15 => AuditLogEventType::ChannelOverwriteDelete, - 20 => AuditLogEventType::MemberKick, - 21 => AuditLogEventType::MemberPrune, - 22 => AuditLogEventType::MemberBanAdd, - 23 => AuditLogEventType::MemberBanRemove, - 24 => AuditLogEventType::MemberUpdate, - 25 => AuditLogEventType::MemberRoleUpdate, - 26 => AuditLogEventType::MemberMove, - 17 => AuditLogEventType::MemberDisconnect, - 28 => AuditLogEventType::BotAdd, - 30 => AuditLogEventType::RoleCreate, - 31 => AuditLogEventType::RoleUpdate, - 32 => AuditLogEventType::RoleDelete, - 40 => AuditLogEventType::InviteCreate, - 41 => AuditLogEventType::InviteUpdate, - 42 => AuditLogEventType::InviteDelete, - 50 => AuditLogEventType::WebhookCreate, - 51 => AuditLogEventType::WebhookUpdate, - 52 => AuditLogEventType::WebhookDelete, - 60 => AuditLogEventType::EmojiCreate, - 61 => AuditLogEventType::EmojiUpdate, - 62 => AuditLogEventType::EmojiDelete, - 72 => AuditLogEventType::MessageDelete, - 73 => AuditLogEventType::MessageBulkDelete, - 74 => AuditLogEventType::MessagePin, - 75 => AuditLogEventType::MessageUnpin, - 80 => AuditLogEventType::IntegrationCreate, - 81 => AuditLogEventType::IntegrationUpdate, - 82 => AuditLogEventType::IntegrationDelete, - 83 => AuditLogEventType::StageInstanceCreate, - 84 => AuditLogEventType::StageInstanceUpdate, - 85 => AuditLogEventType::StageInstanceDelete, - 90 => AuditLogEventType::StickerCreate, - 91 => AuditLogEventType::StickerUpdate, - 92 => AuditLogEventType::StickerDelete, - 100 => AuditLogEventType::GuildScheduledEventCreate, - 101 => AuditLogEventType::GuildScheduledEventUpdate, - 102 => AuditLogEventType::GuildScheduledEventDelete, - 110 => AuditLogEventType::ThreadCreate, - 111 => AuditLogEventType::ThreadUpdate, - 112 => AuditLogEventType::ThreadDelete, - 121 => AuditLogEventType::ApplicationCommandPermissionUpdate, - 140 => AuditLogEventType::AutoModerationRuleCreate, - 141 => AuditLogEventType::AutoModerationRuleUpdate, - 142 => AuditLogEventType::AutoModerationRuleDelete, - 143 => AuditLogEventType::AutoModerationBlockMessage, - 144 => AuditLogEventType::AutoModerationFlagToChannel, - 145 => AuditLogEventType::AutoModerationUserCommunicationDisabled, - unknown => AuditLogEventType::Unknown(unknown), - } - } -} + /// Message has been blocked by Automod. + pub const AUTO_MODERATION_BLOCK_MESSAGE: Self = Self::new(143); + + /// Message has been flagged by Automod. + pub const AUTO_MODERATION_FLAG_TO_CHANNEL: Self = Self::new(144); -impl From for u16 { - fn from(value: AuditLogEventType) -> Self { - match value { - AuditLogEventType::GuildUpdate => 1, - AuditLogEventType::ChannelCreate => 10, - AuditLogEventType::ChannelUpdate => 11, - AuditLogEventType::ChannelDelete => 12, - AuditLogEventType::ChannelOverwriteCreate => 13, - AuditLogEventType::ChannelOverwriteUpdate => 14, - AuditLogEventType::ChannelOverwriteDelete => 15, - AuditLogEventType::MemberKick => 20, - AuditLogEventType::MemberPrune => 21, - AuditLogEventType::MemberBanAdd => 22, - AuditLogEventType::MemberBanRemove => 23, - AuditLogEventType::MemberUpdate => 24, - AuditLogEventType::MemberRoleUpdate => 25, - AuditLogEventType::MemberMove => 26, - AuditLogEventType::MemberDisconnect => 27, - AuditLogEventType::BotAdd => 28, - AuditLogEventType::RoleCreate => 30, - AuditLogEventType::RoleUpdate => 31, - AuditLogEventType::RoleDelete => 32, - AuditLogEventType::InviteCreate => 40, - AuditLogEventType::InviteUpdate => 41, - AuditLogEventType::InviteDelete => 42, - AuditLogEventType::WebhookCreate => 50, - AuditLogEventType::WebhookUpdate => 51, - AuditLogEventType::WebhookDelete => 52, - AuditLogEventType::EmojiCreate => 60, - AuditLogEventType::EmojiUpdate => 61, - AuditLogEventType::EmojiDelete => 62, - AuditLogEventType::MessageDelete => 72, - AuditLogEventType::MessageBulkDelete => 73, - AuditLogEventType::MessagePin => 74, - AuditLogEventType::MessageUnpin => 75, - AuditLogEventType::IntegrationCreate => 80, - AuditLogEventType::IntegrationUpdate => 81, - AuditLogEventType::IntegrationDelete => 82, - AuditLogEventType::StageInstanceCreate => 83, - AuditLogEventType::StageInstanceUpdate => 84, - AuditLogEventType::StageInstanceDelete => 85, - AuditLogEventType::StickerCreate => 90, - AuditLogEventType::StickerUpdate => 91, - AuditLogEventType::StickerDelete => 92, - AuditLogEventType::GuildScheduledEventCreate => 100, - AuditLogEventType::GuildScheduledEventUpdate => 101, - AuditLogEventType::GuildScheduledEventDelete => 102, - AuditLogEventType::ThreadCreate => 110, - AuditLogEventType::ThreadUpdate => 111, - AuditLogEventType::ThreadDelete => 112, - AuditLogEventType::ApplicationCommandPermissionUpdate => 121, - AuditLogEventType::AutoModerationRuleCreate => 140, - AuditLogEventType::AutoModerationRuleUpdate => 141, - AuditLogEventType::AutoModerationRuleDelete => 142, - AuditLogEventType::AutoModerationBlockMessage => 143, - AuditLogEventType::AutoModerationFlagToChannel => 144, - AuditLogEventType::AutoModerationUserCommunicationDisabled => 145, - AuditLogEventType::Unknown(unknown) => unknown, - } + /// A member has been timed out by Automod. + pub const AUTO_MODERATION_USER_COMMUNICATION_DISABLED: Self = Self::new(145); + + /// Name of the associated constant. + /// + /// Returns `None` if the value doesn't have a defined constant. + pub const fn name(self) -> Option<&'static str> { + Some(match self { + Self::GUILD_UPDATE => "GUILD_UPDATE", + Self::CHANNEL_CREATE => "CHANNEL_CREATE", + Self::CHANNEL_UPDATE => "CHANNEL_UPDATE", + Self::CHANNEL_DELETE => "CHANNEL_DELETE", + Self::CHANNEL_OVERWRITE_CREATE => "CHANNEL_OVERWRITE_CREATE", + Self::CHANNEL_OVERWRITE_UPDATE => "CHANNEL_OVERWRITE_UPDATE", + Self::CHANNEL_OVERWRITE_DELETE => "CHANNEL_OVERWRITE_DELETE", + Self::MEMBER_KICK => "MEMBER_KICK", + Self::MEMBER_PRUNE => "MEMBER_PRUNE", + Self::MEMBER_BAN_ADD => "MEMBER_BAN_ADD", + Self::MEMBER_BAN_REMOVE => "MEMBER_BAN_REMOVE", + Self::MEMBER_UPDATE => "MEMBER_UPDATE", + Self::MEMBER_ROLE_UPDATE => "MEMBER_ROLE_UPDATE", + Self::MEMBER_MOVE => "MEMBER_MOVE", + Self::MEMBER_DISCONNECT => "MEMBER_DISCONNECT", + Self::BOT_ADD => "BOT_ADD", + Self::ROLE_CREATE => "ROLE_CREATE", + Self::ROLE_UPDATE => "ROLE_UPDATE", + Self::ROLE_DELETE => "ROLE_DELETE", + Self::INVITE_CREATE => "INVITE_CREATE", + Self::INVITE_UPDATE => "INVITE_UPDATE", + Self::INVITE_DELETE => "INVITE_DELETE", + Self::WEBHOOK_CREATE => "WEBHOOK_CREATE", + Self::WEBHOOK_UPDATE => "WEBHOOK_UPDATE", + Self::WEBHOOK_DELETE => "WEBHOOK_DELETE", + Self::EMOJI_CREATE => "EMOJI_CREATE", + Self::EMOJI_UPDATE => "EMOJI_UPDATE", + Self::EMOJI_DELETE => "EMOJI_DELETE", + Self::MESSAGE_DELETE => "MESSAGE_DELETE", + Self::MESSAGE_BULK_DELETE => "MESSAGE_BULK_DELETE", + Self::MESSAGE_PIN => "MESSAGE_PIN", + Self::MESSAGE_UNPIN => "MESSAGE_UNPIN", + Self::INTEGRATION_CREATE => "INTEGRATION_CREATE", + Self::INTEGRATION_UPDATE => "INTEGRATION_UPDATE", + Self::INTEGRATION_DELETE => "INTEGRATION_DELETE", + Self::STAGE_INSTANCE_CREATE => "STAGE_INSTANCE_CREATE", + Self::STAGE_INSTANCE_UPDATE => "STAGE_INSTANCE_UPDATE", + Self::STAGE_INSTANCE_DELETE => "STAGE_INSTANCE_DELETE", + Self::STICKER_CREATE => "STICKER_CREATE", + Self::STICKER_UPDATE => "STICKER_UPDATE", + Self::STICKER_DELETE => "STICKER_DELETE", + Self::GUILD_SCHEDULED_EVENT_CREATE => "GUILD_SCHEDULED_EVENT_CREATE", + Self::GUILD_SCHEDULED_EVENT_UPDATE => "GUILD_SCHEDULED_EVENT_UPDATE", + Self::GUILD_SCHEDULED_EVENT_DELETE => "GUILD_SCHEDULED_EVENT_DELETE", + Self::THREAD_CREATE => "THREAD_CREATE", + Self::THREAD_UPDATE => "THREAD_UPDATE", + Self::THREAD_DELETE => "THREAD_DELETE", + Self::APPLICATION_COMMAND_PERMISSION_UPDATE => "APPLICATION_COMMAND_PERMISSION_UPDATE", + Self::AUTO_MODERATION_RULE_CREATE => "AUTO_MODERATION_RULE_CREATE", + Self::AUTO_MODERATION_RULE_UPDATE => "AUTO_MODERATION_RULE_UPDATE", + Self::AUTO_MODERATION_RULE_DELETE => "AUTO_MODERATION_RULE_DELETE", + Self::AUTO_MODERATION_BLOCK_MESSAGE => "AUTO_MODERATION_BLOCK_MESSAGE", + Self::AUTO_MODERATION_FLAG_TO_CHANNEL => "AUTO_MODERATION_FLAG_TO_CHANNEL", + Self::AUTO_MODERATION_USER_COMMUNICATION_DISABLED => { + "AUTO_MODERATION_USER_COMMUNICATION_DISABLED" + } + _ => return None, + }) } } +impl_typed!(AuditLogEventType, u16); + #[cfg(test)] mod tests { use super::AuditLogEventType; @@ -376,71 +371,89 @@ mod tests { #[test] fn test_values() { - assert_eq!(1, u16::from(AuditLogEventType::GuildUpdate)); - assert_eq!(10, u16::from(AuditLogEventType::ChannelCreate)); - assert_eq!(11, u16::from(AuditLogEventType::ChannelUpdate)); - assert_eq!(12, u16::from(AuditLogEventType::ChannelDelete)); - assert_eq!(13, u16::from(AuditLogEventType::ChannelOverwriteCreate)); - assert_eq!(14, u16::from(AuditLogEventType::ChannelOverwriteUpdate)); - assert_eq!(15, u16::from(AuditLogEventType::ChannelOverwriteDelete)); - assert_eq!(20, u16::from(AuditLogEventType::MemberKick)); - assert_eq!(21, u16::from(AuditLogEventType::MemberPrune)); - assert_eq!(22, u16::from(AuditLogEventType::MemberBanAdd)); - assert_eq!(23, u16::from(AuditLogEventType::MemberBanRemove)); - assert_eq!(24, u16::from(AuditLogEventType::MemberUpdate)); - assert_eq!(25, u16::from(AuditLogEventType::MemberRoleUpdate)); - assert_eq!(26, u16::from(AuditLogEventType::MemberMove)); - assert_eq!(27, u16::from(AuditLogEventType::MemberDisconnect)); - assert_eq!(28, u16::from(AuditLogEventType::BotAdd)); - assert_eq!(30, u16::from(AuditLogEventType::RoleCreate)); - assert_eq!(31, u16::from(AuditLogEventType::RoleUpdate)); - assert_eq!(32, u16::from(AuditLogEventType::RoleDelete)); - assert_eq!(40, u16::from(AuditLogEventType::InviteCreate)); - assert_eq!(41, u16::from(AuditLogEventType::InviteUpdate)); - assert_eq!(42, u16::from(AuditLogEventType::InviteDelete)); - assert_eq!(50, u16::from(AuditLogEventType::WebhookCreate)); - assert_eq!(51, u16::from(AuditLogEventType::WebhookUpdate)); - assert_eq!(52, u16::from(AuditLogEventType::WebhookDelete)); - assert_eq!(60, u16::from(AuditLogEventType::EmojiCreate)); - assert_eq!(61, u16::from(AuditLogEventType::EmojiUpdate)); - assert_eq!(62, u16::from(AuditLogEventType::EmojiDelete)); - assert_eq!(72, u16::from(AuditLogEventType::MessageDelete)); - assert_eq!(73, u16::from(AuditLogEventType::MessageBulkDelete)); - assert_eq!(74, u16::from(AuditLogEventType::MessagePin)); - assert_eq!(75, u16::from(AuditLogEventType::MessageUnpin)); - assert_eq!(80, u16::from(AuditLogEventType::IntegrationCreate)); - assert_eq!(81, u16::from(AuditLogEventType::IntegrationUpdate)); - assert_eq!(82, u16::from(AuditLogEventType::IntegrationDelete)); - assert_eq!(83, u16::from(AuditLogEventType::StageInstanceCreate)); - assert_eq!(84, u16::from(AuditLogEventType::StageInstanceUpdate)); - assert_eq!(90, u16::from(AuditLogEventType::StickerCreate)); - assert_eq!(91, u16::from(AuditLogEventType::StickerUpdate)); - assert_eq!(92, u16::from(AuditLogEventType::StickerDelete)); - assert_eq!(100, u16::from(AuditLogEventType::GuildScheduledEventCreate)); - assert_eq!(101, u16::from(AuditLogEventType::GuildScheduledEventUpdate)); - assert_eq!(102, u16::from(AuditLogEventType::GuildScheduledEventDelete)); - assert_eq!(110, u16::from(AuditLogEventType::ThreadCreate)); - assert_eq!(111, u16::from(AuditLogEventType::ThreadUpdate)); - assert_eq!(112, u16::from(AuditLogEventType::ThreadDelete)); + assert_eq!(1, u16::from(AuditLogEventType::GUILD_UPDATE)); + assert_eq!(10, u16::from(AuditLogEventType::CHANNEL_CREATE)); + assert_eq!(11, u16::from(AuditLogEventType::CHANNEL_UPDATE)); + assert_eq!(12, u16::from(AuditLogEventType::CHANNEL_DELETE)); + assert_eq!(13, u16::from(AuditLogEventType::CHANNEL_OVERWRITE_CREATE)); + assert_eq!(14, u16::from(AuditLogEventType::CHANNEL_OVERWRITE_UPDATE)); + assert_eq!(15, u16::from(AuditLogEventType::CHANNEL_OVERWRITE_DELETE)); + assert_eq!(20, u16::from(AuditLogEventType::MEMBER_KICK)); + assert_eq!(21, u16::from(AuditLogEventType::MEMBER_PRUNE)); + assert_eq!(22, u16::from(AuditLogEventType::MEMBER_BAN_ADD)); + assert_eq!(23, u16::from(AuditLogEventType::MEMBER_BAN_REMOVE)); + assert_eq!(24, u16::from(AuditLogEventType::MEMBER_UPDATE)); + assert_eq!(25, u16::from(AuditLogEventType::MEMBER_ROLE_UPDATE)); + assert_eq!(26, u16::from(AuditLogEventType::MEMBER_MOVE)); + assert_eq!(27, u16::from(AuditLogEventType::MEMBER_DISCONNECT)); + assert_eq!(28, u16::from(AuditLogEventType::BOT_ADD)); + assert_eq!(30, u16::from(AuditLogEventType::ROLE_CREATE)); + assert_eq!(31, u16::from(AuditLogEventType::ROLE_UPDATE)); + assert_eq!(32, u16::from(AuditLogEventType::ROLE_DELETE)); + assert_eq!(40, u16::from(AuditLogEventType::INVITE_CREATE)); + assert_eq!(41, u16::from(AuditLogEventType::INVITE_UPDATE)); + assert_eq!(42, u16::from(AuditLogEventType::INVITE_DELETE)); + assert_eq!(50, u16::from(AuditLogEventType::WEBHOOK_CREATE)); + assert_eq!(51, u16::from(AuditLogEventType::WEBHOOK_UPDATE)); + assert_eq!(52, u16::from(AuditLogEventType::WEBHOOK_DELETE)); + assert_eq!(60, u16::from(AuditLogEventType::EMOJI_CREATE)); + assert_eq!(61, u16::from(AuditLogEventType::EMOJI_UPDATE)); + assert_eq!(62, u16::from(AuditLogEventType::EMOJI_DELETE)); + assert_eq!(72, u16::from(AuditLogEventType::MESSAGE_DELETE)); + assert_eq!(73, u16::from(AuditLogEventType::MESSAGE_BULK_DELETE)); + assert_eq!(74, u16::from(AuditLogEventType::MESSAGE_PIN)); + assert_eq!(75, u16::from(AuditLogEventType::MESSAGE_UNPIN)); + assert_eq!(80, u16::from(AuditLogEventType::INTEGRATION_CREATE)); + assert_eq!(81, u16::from(AuditLogEventType::INTEGRATION_UPDATE)); + assert_eq!(82, u16::from(AuditLogEventType::INTEGRATION_DELETE)); + assert_eq!(83, u16::from(AuditLogEventType::STAGE_INSTANCE_CREATE)); + assert_eq!(84, u16::from(AuditLogEventType::STAGE_INSTANCE_UPDATE)); + assert_eq!(90, u16::from(AuditLogEventType::STICKER_CREATE)); + assert_eq!(91, u16::from(AuditLogEventType::STICKER_UPDATE)); + assert_eq!(92, u16::from(AuditLogEventType::STICKER_DELETE)); + assert_eq!( + 100, + u16::from(AuditLogEventType::GUILD_SCHEDULED_EVENT_CREATE) + ); + assert_eq!( + 101, + u16::from(AuditLogEventType::GUILD_SCHEDULED_EVENT_UPDATE) + ); + assert_eq!( + 102, + u16::from(AuditLogEventType::GUILD_SCHEDULED_EVENT_DELETE) + ); + assert_eq!(110, u16::from(AuditLogEventType::THREAD_CREATE)); + assert_eq!(111, u16::from(AuditLogEventType::THREAD_UPDATE)); + assert_eq!(112, u16::from(AuditLogEventType::THREAD_DELETE)); assert_eq!( 121, - u16::from(AuditLogEventType::ApplicationCommandPermissionUpdate) + u16::from(AuditLogEventType::APPLICATION_COMMAND_PERMISSION_UPDATE) + ); + assert_eq!( + 140, + u16::from(AuditLogEventType::AUTO_MODERATION_RULE_CREATE) + ); + assert_eq!( + 141, + u16::from(AuditLogEventType::AUTO_MODERATION_RULE_UPDATE) + ); + assert_eq!( + 142, + u16::from(AuditLogEventType::AUTO_MODERATION_RULE_DELETE) ); - assert_eq!(140, u16::from(AuditLogEventType::AutoModerationRuleCreate)); - assert_eq!(141, u16::from(AuditLogEventType::AutoModerationRuleUpdate)); - assert_eq!(142, u16::from(AuditLogEventType::AutoModerationRuleDelete)); assert_eq!( 143, - u16::from(AuditLogEventType::AutoModerationBlockMessage) + u16::from(AuditLogEventType::AUTO_MODERATION_BLOCK_MESSAGE) ); assert_eq!( 144, - u16::from(AuditLogEventType::AutoModerationFlagToChannel) + u16::from(AuditLogEventType::AUTO_MODERATION_FLAG_TO_CHANNEL) ); assert_eq!( 145, - u16::from(AuditLogEventType::AutoModerationUserCommunicationDisabled) + u16::from(AuditLogEventType::AUTO_MODERATION_USER_COMMUNICATION_DISABLED) ); - assert_eq!(250, u16::from(AuditLogEventType::Unknown(250))); + assert_eq!(250, u16::from(AuditLogEventType::new(250))); } } diff --git a/twilight-model/src/guild/audit_log/optional_entry_info.rs b/twilight-model/src/guild/audit_log/optional_entry_info.rs index 96fa752edeb..a2a31357467 100644 --- a/twilight-model/src/guild/audit_log/optional_entry_info.rs +++ b/twilight-model/src/guild/audit_log/optional_entry_info.rs @@ -13,68 +13,68 @@ pub struct AuditLogOptionalEntryInfo { /// /// The following events have this option: /// - /// - [`AuditLogEventType::AutoModerationBlockMessage`] - /// - [`AuditLogEventType::AutoModerationFlagToChannel`] - /// - [`AuditLogEventType::AutoModerationUserCommunicationDisabled`] + /// - [`AuditLogEventType::AUTO_MODERATION_BLOCK_MESSAGE`] + /// - [`AuditLogEventType::AUTO_MODERATION_FLAG_TO_CHANNEL`] + /// - [`AuditLogEventType::AUTO_MODERATION_USER_COMMUNICATION_DISABLED`] /// - /// [`AuditLogEventType::AutoModerationBlockMessage`]: super::AuditLogEventType::AutoModerationBlockMessage - /// [`AuditLogEventType::AutoModerationFlagToChannel`]: super::AuditLogEventType::AutoModerationFlagToChannel - /// [`AuditLogEventType::AutoModerationUserCommunicationDisabled`]: super::AuditLogEventType::AutoModerationUserCommunicationDisabled + /// [`AuditLogEventType::AUTO_MODERATION_BLOCK_MESSAGE`]: super::AuditLogEventType::AUTO_MODERATION_BLOCK_MESSAGE + /// [`AuditLogEventType::AUTO_MODERATION_FLAG_TO_CHANNEL`]: super::AuditLogEventType::AUTO_MODERATION_FLAG_TO_CHANNEL + /// [`AuditLogEventType::AUTO_MODERATION_USER_COMMUNICATION_DISABLED`]: super::AuditLogEventType::AUTO_MODERATION_USER_COMMUNICATION_DISABLED #[serde(skip_serializing_if = "Option::is_none")] pub auto_moderation_rule_name: Option, /// Trigger type of the Auto Moderation rule that was triggered. /// /// The following events have this option: /// - /// - [`AuditLogEventType::AutoModerationBlockMessage`] - /// - [`AuditLogEventType::AutoModerationFlagToChannel`] - /// - [`AuditLogEventType::AutoModerationUserCommunicationDisabled`] + /// - [`AuditLogEventType::AUTO_MODERATION_BLOCK_MESSAGE`] + /// - [`AuditLogEventType::AUTO_MODERATION_FLAG_TO_CHANNEL`] + /// - [`AuditLogEventType::AUTO_MODERATION_USER_COMMUNICATION_DISABLED`] /// - /// [`AuditLogEventType::AutoModerationBlockMessage`]: super::AuditLogEventType::AutoModerationBlockMessage - /// [`AuditLogEventType::AutoModerationFlagToChannel`]: super::AuditLogEventType::AutoModerationFlagToChannel - /// [`AuditLogEventType::AutoModerationUserCommunicationDisabled`]: super::AuditLogEventType::AutoModerationUserCommunicationDisabled + /// [`AuditLogEventType::AUTO_MODERATION_BLOCK_MESSAGE`]: super::AuditLogEventType::AUTO_MODERATION_BLOCK_MESSAGE + /// [`AuditLogEventType::AUTO_MODERATION_FLAG_TO_CHANNEL`]: super::AuditLogEventType::AUTO_MODERATION_FLAG_TO_CHANNEL + /// [`AuditLogEventType::AUTO_MODERATION_USER_COMMUNICATION_DISABLED`]: super::AuditLogEventType::AUTO_MODERATION_USER_COMMUNICATION_DISABLED #[serde(skip_serializing_if = "Option::is_none")] pub auto_moderation_rule_trigger_type: Option, /// Channel in which the entities were targeted. /// /// The following events have this option: /// - /// - [`AuditLogEventType::AutoModerationBlockMessage`] - /// - [`AuditLogEventType::AutoModerationFlagToChannel`] - /// - [`AuditLogEventType::AutoModerationUserCommunicationDisabled`] - /// - [`AuditLogEventType::MemberMove`] - /// - [`AuditLogEventType::MessageDelete`] - /// - [`AuditLogEventType::MessagePin`] - /// - [`AuditLogEventType::MessageUnpin`] - /// - [`AuditLogEventType::StageInstanceCreate`] - /// - [`AuditLogEventType::StageInstanceDelete`] - /// - [`AuditLogEventType::StageInstanceUpdate`] - /// - /// [`AuditLogEventType::AutoModerationBlockMessage`]: super::AuditLogEventType::AutoModerationBlockMessage - /// [`AuditLogEventType::AutoModerationFlagToChannel`]: super::AuditLogEventType::AutoModerationFlagToChannel - /// [`AuditLogEventType::AutoModerationUserCommunicationDisabled`]: super::AuditLogEventType::AutoModerationUserCommunicationDisabled - /// [`AuditLogEventType::MemberMove`]: super::AuditLogEventType::MemberMove - /// [`AuditLogEventType::MessageDelete`]: super::AuditLogEventType::MessageDelete - /// [`AuditLogEventType::MessagePin`]: super::AuditLogEventType::MessagePin - /// [`AuditLogEventType::MessageUnpin`]: super::AuditLogEventType::MessageUnpin - /// [`AuditLogEventType::StageInstanceCreate`]: super::AuditLogEventType::StageInstanceCreate - /// [`AuditLogEventType::StageInstanceDelete`]: super::AuditLogEventType::StageInstanceDelete - /// [`AuditLogEventType::StageInstanceUpdate`]: super::AuditLogEventType::StageInstanceUpdate + /// - [`AuditLogEventType::AUTO_MODERATION_BLOCK_MESSAGE`] + /// - [`AuditLogEventType::AUTO_MODERATION_FLAG_TO_CHANNEL`] + /// - [`AuditLogEventType::AUTO_MODERATION_USER_COMMUNICATION_DISABLED`] + /// - [`AuditLogEventType::MEMBER_MOVE`] + /// - [`AuditLogEventType::MESSAGE_DELETE`] + /// - [`AuditLogEventType::MESSAGE_PIN`] + /// - [`AuditLogEventType::MESSAGE_UNPIN`] + /// - [`AuditLogEventType::STAGE_INSTANCE_CREATE`] + /// - [`AuditLogEventType::STAGE_INSTANCE_DELETE`] + /// - [`AuditLogEventType::STAGE_INSTANCE_UPDATE`] + /// + /// [`AuditLogEventType::AUTO_MODERATION_BLOCK_MESSAGE`]: super::AuditLogEventType::AUTO_MODERATION_BLOCK_MESSAGE + /// [`AuditLogEventType::AUTO_MODERATION_FLAG_TO_CHANNEL`]: super::AuditLogEventType::AUTO_MODERATION_FLAG_TO_CHANNEL + /// [`AuditLogEventType::AUTO_MODERATION_USER_COMMUNICATION_DISABLED`]: super::AuditLogEventType::AUTO_MODERATION_USER_COMMUNICATION_DISABLED + /// [`AuditLogEventType::MEMBER_MOVE`]: super::AuditLogEventType::MEMBER_MOVE + /// [`AuditLogEventType::MESSAGE_DELETE`]: super::AuditLogEventType::MESSAGE_DELETE + /// [`AuditLogEventType::MESSAGE_PIN`]: super::AuditLogEventType::MESSAGE_PIN + /// [`AuditLogEventType::MESSAGE_UNPIN`]: super::AuditLogEventType::MESSAGE_UNPIN + /// [`AuditLogEventType::STAGE_INSTANCE_CREATE`]: super::AuditLogEventType::STAGE_INSTANCE_CREATE + /// [`AuditLogEventType::STAGE_INSTANCE_DELETE`]: super::AuditLogEventType::STAGE_INSTANCE_DELETE + /// [`AuditLogEventType::STAGE_INSTANCE_UPDATE`]: super::AuditLogEventType::STAGE_INSTANCE_UPDATE #[serde(skip_serializing_if = "Option::is_none")] pub channel_id: Option>, /// Number of entities that were targeted. /// /// The following events have this option: /// - /// - [`AuditLogEventType::MemberDisconnect`] - /// - [`AuditLogEventType::MemberMove`] - /// - [`AuditLogEventType::MessageBulkDelete`] - /// - [`AuditLogEventType::MessageDelete`] + /// - [`AuditLogEventType::MEMBER_DISCONNECT`] + /// - [`AuditLogEventType::MEMBER_MOVE`] + /// - [`AuditLogEventType::MESSAGE_BULK_DELETE`] + /// - [`AuditLogEventType::MESSAGE_DELETE`] /// - /// [`AuditLogEventType::MemberDisconnect`]: super::AuditLogEventType::MemberDisconnect - /// [`AuditLogEventType::MemberMove`]: super::AuditLogEventType::MemberMove - /// [`AuditLogEventType::MessageBulkDelete`]: super::AuditLogEventType::MessageBulkDelete - /// [`AuditLogEventType::MessageDelete`]: super::AuditLogEventType::MessageDelete + /// [`AuditLogEventType::MEMBER_DISCONNECT`]: super::AuditLogEventType::MEMBER_DISCONNECT + /// [`AuditLogEventType::MEMBER_MOVE`]: super::AuditLogEventType::MEMBER_MOVE + /// [`AuditLogEventType::MESSAGE_BULK_DELETE`]: super::AuditLogEventType::MESSAGE_BULK_DELETE + /// [`AuditLogEventType::MESSAGE_DELETE`]: super::AuditLogEventType::MESSAGE_DELETE #[serde(skip_serializing_if = "Option::is_none")] pub count: Option, /// Specified number of days' worth of inactivity members must have in order @@ -82,67 +82,67 @@ pub struct AuditLogOptionalEntryInfo { /// /// The following events have this option: /// - /// - [`AuditLogEventType::MemberPrune`] + /// - [`AuditLogEventType::MEMBER_PRUNE`] /// - /// [`AuditLogEventType::MemberPrune`]: super::AuditLogEventType::MemberPrune + /// [`AuditLogEventType::MEMBER_PRUNE`]: super::AuditLogEventType::MEMBER_PRUNE #[serde(skip_serializing_if = "Option::is_none")] pub delete_member_days: Option, /// ID of overwritten entity. /// /// The following events have this option: /// - /// - [`AuditLogEventType::ChannelOverwriteCreate`] - /// - [`AuditLogEventType::ChannelOverwriteDelete`] - /// - [`AuditLogEventType::ChannelOverwriteUpdate`] + /// - [`AuditLogEventType::CHANNEL_OVERWRITE_CREATE`] + /// - [`AuditLogEventType::CHANNEL_OVERWRITE_DELETE`] + /// - [`AuditLogEventType::CHANNEL_OVERWRITE_UPDATE`] /// - /// [`AuditLogEventType::ChannelOverwriteCreate`]: super::AuditLogEventType::ChannelOverwriteCreate - /// [`AuditLogEventType::ChannelOverwriteDelete`]: super::AuditLogEventType::ChannelOverwriteDelete - /// [`AuditLogEventType::ChannelOverwriteUpdate`]: super::AuditLogEventType::ChannelOverwriteUpdate + /// [`AuditLogEventType::CHANNEL_OVERWRITE_CREATE`]: super::AuditLogEventType::CHANNEL_OVERWRITE_CREATE + /// [`AuditLogEventType::CHANNEL_OVERWRITE_DELETE`]: super::AuditLogEventType::CHANNEL_OVERWRITE_DELETE + /// [`AuditLogEventType::CHANNEL_OVERWRITE_UPDATE`]: super::AuditLogEventType::CHANNEL_OVERWRITE_UPDATE pub id: Option>, /// Type of overwritten entity. /// /// The following events have this option: /// - /// - [`AuditLogEventType::ChannelOverwriteCreate`] - /// - [`AuditLogEventType::ChannelOverwriteDelete`] - /// - [`AuditLogEventType::ChannelOverwriteUpdate`] + /// - [`AuditLogEventType::CHANNEL_OVERWRITE_CREATE`] + /// - [`AuditLogEventType::CHANNEL_OVERWRITE_DELETE`] + /// - [`AuditLogEventType::CHANNEL_OVERWRITE_UPDATE`] /// - /// [`AuditLogEventType::ChannelOverwriteCreate`]: super::AuditLogEventType::ChannelOverwriteCreate - /// [`AuditLogEventType::ChannelOverwriteDelete`]: super::AuditLogEventType::ChannelOverwriteDelete - /// [`AuditLogEventType::ChannelOverwriteUpdate`]: super::AuditLogEventType::ChannelOverwriteUpdate + /// [`AuditLogEventType::CHANNEL_OVERWRITE_CREATE`]: super::AuditLogEventType::CHANNEL_OVERWRITE_CREATE + /// [`AuditLogEventType::CHANNEL_OVERWRITE_DELETE`]: super::AuditLogEventType::CHANNEL_OVERWRITE_DELETE + /// [`AuditLogEventType::CHANNEL_OVERWRITE_UPDATE`]: super::AuditLogEventType::CHANNEL_OVERWRITE_UPDATE #[serde(rename = "type", skip_serializing_if = "Option::is_none")] pub kind: Option, /// Number of members removed from a change. /// /// The following events have this option: /// - /// - [`AuditLogEventType::MemberPrune`] + /// - [`AuditLogEventType::MEMBER_PRUNE`] /// - /// [`AuditLogEventType::MemberPrune`]: super::AuditLogEventType::MemberPrune + /// [`AuditLogEventType::MEMBER_PRUNE`]: super::AuditLogEventType::MEMBER_PRUNE #[serde(skip_serializing_if = "Option::is_none")] pub members_removed: Option, /// ID of the affected message. /// /// The following events have this option: /// - /// - [`AuditLogEventType::MessagePin`] - /// - [`AuditLogEventType::MessageUnpin`] + /// - [`AuditLogEventType::MESSAGE_PIN`] + /// - [`AuditLogEventType::MESSAGE_UNPIN`] /// - /// [`AuditLogEventType::MessagePin`]: super::AuditLogEventType::MessagePin - /// [`AuditLogEventType::MessageUnpin`]: super::AuditLogEventType::MessageUnpin + /// [`AuditLogEventType::MESSAGE_PIN`]: super::AuditLogEventType::MESSAGE_PIN + /// [`AuditLogEventType::MESSAGE_UNPIN`]: super::AuditLogEventType::MESSAGE_UNPIN #[serde(skip_serializing_if = "Option::is_none")] pub message_id: Option>, /// Name of a role. /// /// The following events have this option: /// - /// - [`AuditLogEventType::ChannelOverwriteCreate`] - /// - [`AuditLogEventType::ChannelOverwriteDelete`] - /// - [`AuditLogEventType::ChannelOverwriteUpdate`] + /// - [`AuditLogEventType::CHANNEL_OVERWRITE_CREATE`] + /// - [`AuditLogEventType::CHANNEL_OVERWRITE_DELETE`] + /// - [`AuditLogEventType::CHANNEL_OVERWRITE_UPDATE`] /// - /// [`AuditLogEventType::ChannelOverwriteCreate`]: super::AuditLogEventType::ChannelOverwriteCreate - /// [`AuditLogEventType::ChannelOverwriteDelete`]: super::AuditLogEventType::ChannelOverwriteDelete - /// [`AuditLogEventType::ChannelOverwriteUpdate`]: super::AuditLogEventType::ChannelOverwriteUpdate + /// [`AuditLogEventType::CHANNEL_OVERWRITE_CREATE`]: super::AuditLogEventType::CHANNEL_OVERWRITE_CREATE + /// [`AuditLogEventType::CHANNEL_OVERWRITE_DELETE`]: super::AuditLogEventType::CHANNEL_OVERWRITE_DELETE + /// [`AuditLogEventType::CHANNEL_OVERWRITE_UPDATE`]: super::AuditLogEventType::CHANNEL_OVERWRITE_UPDATE #[serde(skip_serializing_if = "Option::is_none")] pub role_name: Option, } diff --git a/twilight-model/src/guild/auto_moderation/action.rs b/twilight-model/src/guild/auto_moderation/action.rs index d40229d1c8f..866268cb2c8 100644 --- a/twilight-model/src/guild/auto_moderation/action.rs +++ b/twilight-model/src/guild/auto_moderation/action.rs @@ -28,47 +28,40 @@ pub struct AutoModerationActionMetadata { } /// Type of [`AutoModerationAction`]. -#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)] -#[serde(from = "u8", into = "u8")] -pub enum AutoModerationActionType { +#[derive(Clone, Copy, Deserialize, Eq, Hash, PartialEq, Serialize)] +pub struct AutoModerationActionType(u8); + +impl AutoModerationActionType { /// Blocks the content of a message according to the rule. - BlockMessage, + pub const BLOCK_MESSAGE: Self = Self::new(1); + /// Logs user content to a specified channel. - SendAlertMessage, + pub const SEND_ALERT_MESSAGE: Self = Self::new(2); + /// Timeout user for a specified duration. /// - /// A `Timeout` action can only be setup for [`Keyword`] rules. + /// A `Timeout` action can only be setup for [`KEYWORD`] rules. /// [`Permissions::MODERATE_MEMBERS`] is required to use the `Timeout` action /// type. /// - /// [`Keyword`]: super::AutoModerationTriggerType::Keyword + /// [`KEYWORD`]: super::AutoModerationTriggerType::KEYWORD /// [`Permissions::MODERATE_MEMBERS`]: crate::guild::Permissions::MODERATE_MEMBERS - Timeout, - /// Variant value is unknown to the library. - Unknown(u8), -} + pub const TIMEOUT: Self = Self::new(3); -impl From for AutoModerationActionType { - fn from(value: u8) -> Self { - match value { - 1 => Self::BlockMessage, - 2 => Self::SendAlertMessage, - 3 => Self::Timeout, - _ => Self::Unknown(value), - } + /// Name of the associated constant. + /// + /// Returns `None` if the value doesn't have a defined constant. + pub const fn name(self) -> Option<&'static str> { + Some(match self { + Self::BLOCK_MESSAGE => "BLOCK_MESSAGE", + Self::SEND_ALERT_MESSAGE => "SEND_ALERT_MESSAGE", + Self::TIMEOUT => "TIMEOUT", + _ => return None, + }) } } -impl From for u8 { - fn from(value: AutoModerationActionType) -> Self { - match value { - AutoModerationActionType::BlockMessage => 1, - AutoModerationActionType::SendAlertMessage => 2, - AutoModerationActionType::Timeout => 3, - AutoModerationActionType::Unknown(unknown) => unknown, - } - } -} +impl_typed!(AutoModerationActionType, u8); #[cfg(test)] mod tests { @@ -116,9 +109,9 @@ mod tests { #[test] fn values() { - assert_eq!(1, u8::from(AutoModerationActionType::BlockMessage)); - assert_eq!(2, u8::from(AutoModerationActionType::SendAlertMessage)); - assert_eq!(3, u8::from(AutoModerationActionType::Timeout)); - assert_eq!(250, u8::from(AutoModerationActionType::Unknown(250))); + assert_eq!(1, u8::from(AutoModerationActionType::BLOCK_MESSAGE)); + assert_eq!(2, u8::from(AutoModerationActionType::SEND_ALERT_MESSAGE)); + assert_eq!(3, u8::from(AutoModerationActionType::TIMEOUT)); + assert_eq!(250, u8::from(AutoModerationActionType::new(250))); } } diff --git a/twilight-model/src/guild/auto_moderation/event_type.rs b/twilight-model/src/guild/auto_moderation/event_type.rs index 294f3f7bef5..763893ea303 100644 --- a/twilight-model/src/guild/auto_moderation/event_type.rs +++ b/twilight-model/src/guild/auto_moderation/event_type.rs @@ -1,32 +1,25 @@ use serde::{Deserialize, Serialize}; /// Indicates in what event context a rule should be checked. -#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)] -#[serde(from = "u8", into = "u8")] -pub enum AutoModerationEventType { +#[derive(Clone, Copy, Deserialize, Eq, Hash, PartialEq, Serialize)] +pub struct AutoModerationEventType(u8); + +impl AutoModerationEventType { /// When a member sends or edits a message in a guild. - MessageSend, - /// Variant value is unknown to the library. - Unknown(u8), -} + pub const MESSAGE_SEND: Self = Self::new(1); -impl From for AutoModerationEventType { - fn from(value: u8) -> Self { - match value { - 1 => Self::MessageSend, - _ => Self::Unknown(value), - } + /// Name of the associated constant. + /// + /// Returns `None` if the value doesn't have a defined constant. + pub const fn name(self) -> Option<&'static str> { + Some(match self { + Self::MESSAGE_SEND => "MESSAGE_SEND", + _ => return None, + }) } } -impl From for u8 { - fn from(value: AutoModerationEventType) -> Self { - match value { - AutoModerationEventType::MessageSend => 1, - AutoModerationEventType::Unknown(unknown) => unknown, - } - } -} +impl_typed!(AutoModerationEventType, u8); #[cfg(test)] mod tests { @@ -50,7 +43,7 @@ mod tests { #[test] fn values() { - assert_eq!(1, u8::from(AutoModerationEventType::MessageSend)); - assert_eq!(250, u8::from(AutoModerationEventType::Unknown(250))); + assert_eq!(1, u8::from(AutoModerationEventType::MESSAGE_SEND)); + assert_eq!(250, u8::from(AutoModerationEventType::new(250))); } } diff --git a/twilight-model/src/guild/auto_moderation/mod.rs b/twilight-model/src/guild/auto_moderation/mod.rs index 55b8948078c..9bf60dbc9a9 100644 --- a/twilight-model/src/guild/auto_moderation/mod.rs +++ b/twilight-model/src/guild/auto_moderation/mod.rs @@ -115,18 +115,18 @@ mod tests { let value = AutoModerationRule { actions: Vec::from([ AutoModerationAction { - kind: AutoModerationActionType::BlockMessage, + kind: AutoModerationActionType::BLOCK_MESSAGE, metadata: None, }, AutoModerationAction { - kind: AutoModerationActionType::SendAlertMessage, + kind: AutoModerationActionType::SEND_ALERT_MESSAGE, metadata: Some(AutoModerationActionMetadata { channel_id: Some(ACTION_CHANNEL_ID), duration_seconds: None, }), }, AutoModerationAction { - kind: AutoModerationActionType::Timeout, + kind: AutoModerationActionType::TIMEOUT, metadata: Some(AutoModerationActionMetadata { channel_id: None, duration_seconds: Some(120), @@ -135,7 +135,7 @@ mod tests { ]), creator_id: CREATOR_ID, enabled: true, - event_type: AutoModerationEventType::MessageSend, + event_type: AutoModerationEventType::MESSAGE_SEND, exempt_channels: Vec::from([EXEMPT_CHANNEL_ID]), exempt_roles: Vec::from([EXEMPT_ROLE_ID]), guild_id: GUILD_ID, @@ -146,7 +146,7 @@ mod tests { keyword_filter: Some(Vec::from(["shoot".into(), "darn".into()])), presets: None, }, - trigger_type: AutoModerationTriggerType::Keyword, + trigger_type: AutoModerationTriggerType::KEYWORD, }; serde_test::assert_tokens( @@ -163,14 +163,20 @@ mod tests { len: 1, }, Token::Str("type"), - Token::U8(u8::from(AutoModerationActionType::BlockMessage)), + Token::NewtypeStruct { + name: "AutoModerationActionType", + }, + Token::U8(u8::from(AutoModerationActionType::BLOCK_MESSAGE)), Token::StructEnd, Token::Struct { name: "AutoModerationAction", len: 2, }, Token::Str("type"), - Token::U8(u8::from(AutoModerationActionType::SendAlertMessage)), + Token::NewtypeStruct { + name: "AutoModerationActionType", + }, + Token::U8(u8::from(AutoModerationActionType::SEND_ALERT_MESSAGE)), Token::Str("metadata"), Token::Some, Token::Struct { @@ -188,7 +194,10 @@ mod tests { len: 2, }, Token::Str("type"), - Token::U8(u8::from(AutoModerationActionType::Timeout)), + Token::NewtypeStruct { + name: "AutoModerationActionType", + }, + Token::U8(u8::from(AutoModerationActionType::TIMEOUT)), Token::Str("metadata"), Token::Some, Token::Struct { @@ -207,7 +216,10 @@ mod tests { Token::Str("enabled"), Token::Bool(true), Token::Str("event_type"), - Token::U8(u8::from(AutoModerationEventType::MessageSend)), + Token::NewtypeStruct { + name: "AutoModerationEventType", + }, + Token::U8(u8::from(AutoModerationEventType::MESSAGE_SEND)), Token::Str("exempt_channels"), Token::Seq { len: Some(1) }, Token::NewtypeStruct { name: "Id" }, @@ -239,7 +251,10 @@ mod tests { Token::SeqEnd, Token::StructEnd, Token::Str("trigger_type"), - Token::U8(u8::from(AutoModerationTriggerType::Keyword)), + Token::NewtypeStruct { + name: "AutoModerationTriggerType", + }, + Token::U8(u8::from(AutoModerationTriggerType::KEYWORD)), Token::StructEnd, ], ); diff --git a/twilight-model/src/guild/auto_moderation/preset_type.rs b/twilight-model/src/guild/auto_moderation/preset_type.rs index 84be71265d3..7fba53c73cb 100644 --- a/twilight-model/src/guild/auto_moderation/preset_type.rs +++ b/twilight-model/src/guild/auto_moderation/preset_type.rs @@ -1,40 +1,33 @@ use serde::{Deserialize, Serialize}; /// Internally pre-defined wordsets which will be searched for in content. -#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)] -#[serde(from = "u8", into = "u8")] -pub enum AutoModerationKeywordPresetType { +#[derive(Clone, Copy, Deserialize, Eq, Hash, PartialEq, Serialize)] +pub struct AutoModerationKeywordPresetType(u8); + +impl AutoModerationKeywordPresetType { /// Words that may be considered forms of swearing or cursing. - Profanity, + pub const PROFANITY: Self = Self::new(1); + /// Words that refer to sexually explicit behavior or activity. - SexualContent, + pub const SEXUAL_CONTENT: Self = Self::new(2); + /// Personal insults or words that may be considered hate speech. - Slurs, - /// Variant value is unknown to the library. - Unknown(u8), -} + pub const SLURS: Self = Self::new(3); -impl From for AutoModerationKeywordPresetType { - fn from(value: u8) -> Self { - match value { - 1 => Self::Profanity, - 2 => Self::SexualContent, - 3 => Self::Slurs, - _ => Self::Unknown(value), - } + /// Name of the associated constant. + /// + /// Returns `None` if the value doesn't have a defined constant. + pub const fn name(self) -> Option<&'static str> { + Some(match self { + Self::PROFANITY => "PROFANITY", + Self::SEXUAL_CONTENT => "SEXUAL_CONTENT", + Self::SLURS => "SLURS", + _ => return None, + }) } } -impl From for u8 { - fn from(value: AutoModerationKeywordPresetType) -> Self { - match value { - AutoModerationKeywordPresetType::Profanity => 1, - AutoModerationKeywordPresetType::SexualContent => 2, - AutoModerationKeywordPresetType::Slurs => 3, - AutoModerationKeywordPresetType::Unknown(unknown) => unknown, - } - } -} +impl_typed!(AutoModerationKeywordPresetType, u8); #[cfg(test)] mod tests { @@ -58,9 +51,9 @@ mod tests { #[test] fn values() { - assert_eq!(1, u8::from(AutoModerationKeywordPresetType::Profanity)); - assert_eq!(2, u8::from(AutoModerationKeywordPresetType::SexualContent)); - assert_eq!(3, u8::from(AutoModerationKeywordPresetType::Slurs)); - assert_eq!(250, u8::from(AutoModerationKeywordPresetType::Unknown(250))); + assert_eq!(1, u8::from(AutoModerationKeywordPresetType::PROFANITY)); + assert_eq!(2, u8::from(AutoModerationKeywordPresetType::SEXUAL_CONTENT)); + assert_eq!(3, u8::from(AutoModerationKeywordPresetType::SLURS)); + assert_eq!(250, u8::from(AutoModerationKeywordPresetType::new(250))); } } diff --git a/twilight-model/src/guild/auto_moderation/trigger_metadata.rs b/twilight-model/src/guild/auto_moderation/trigger_metadata.rs index ae9aabb7649..4067259ff0e 100644 --- a/twilight-model/src/guild/auto_moderation/trigger_metadata.rs +++ b/twilight-model/src/guild/auto_moderation/trigger_metadata.rs @@ -52,9 +52,9 @@ mod tests { allow_list: Some(Vec::from(["heck".into()])), keyword_filter: Some(Vec::from(["shoot".into(), "darn".into()])), presets: Some(Vec::from([ - AutoModerationKeywordPresetType::Profanity, - AutoModerationKeywordPresetType::SexualContent, - AutoModerationKeywordPresetType::Slurs, + AutoModerationKeywordPresetType::PROFANITY, + AutoModerationKeywordPresetType::SEXUAL_CONTENT, + AutoModerationKeywordPresetType::SLURS, ])), }; @@ -79,9 +79,18 @@ mod tests { Token::Str("presets"), Token::Some, Token::Seq { len: Some(3) }, - Token::U8(u8::from(AutoModerationKeywordPresetType::Profanity)), - Token::U8(u8::from(AutoModerationKeywordPresetType::SexualContent)), - Token::U8(u8::from(AutoModerationKeywordPresetType::Slurs)), + Token::NewtypeStruct { + name: "AutoModerationKeywordPresetType", + }, + Token::U8(u8::from(AutoModerationKeywordPresetType::PROFANITY)), + Token::NewtypeStruct { + name: "AutoModerationKeywordPresetType", + }, + Token::U8(u8::from(AutoModerationKeywordPresetType::SEXUAL_CONTENT)), + Token::NewtypeStruct { + name: "AutoModerationKeywordPresetType", + }, + Token::U8(u8::from(AutoModerationKeywordPresetType::SLURS)), Token::SeqEnd, Token::StructEnd, ], diff --git a/twilight-model/src/guild/auto_moderation/trigger_type.rs b/twilight-model/src/guild/auto_moderation/trigger_type.rs index 1ba72b9daaa..4aa552eadab 100644 --- a/twilight-model/src/guild/auto_moderation/trigger_type.rs +++ b/twilight-model/src/guild/auto_moderation/trigger_type.rs @@ -1,50 +1,43 @@ use serde::{Deserialize, Serialize}; /// Characterizes the type of content which can trigger the rule. -#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)] -#[serde(from = "u8", into = "u8")] -pub enum AutoModerationTriggerType { +#[derive(Clone, Copy, Deserialize, Eq, Hash, PartialEq, Serialize)] +pub struct AutoModerationTriggerType(u8); + +impl AutoModerationTriggerType { /// Check if content contains words from a user defined list of keywords. /// /// Maximum of 5 per guild. - Keyword, + pub const KEYWORD: Self = Self::new(1); + /// Check if content represents generic spam. /// /// Currently unreleased. Maximum of 1 per guild. - Spam, + pub const SPAM: Self = Self::new(3); + /// Check if content contains words from internal pre-defined wordsets. /// /// Maximum of 1 per guild. - KeywordPreset, + pub const KEYWORD_PRESET: Self = Self::new(4); + /// Check if content contains more unique mentions than allowed. - MentionSpam, - /// Variant value is unknown to the library. - Unknown(u8), -} + pub const MENTION_SPAM: Self = Self::new(5); -impl From for AutoModerationTriggerType { - fn from(value: u8) -> Self { - match value { - 1 => Self::Keyword, - 3 => Self::Spam, - 4 => Self::KeywordPreset, - 5 => Self::MentionSpam, - _ => Self::Unknown(value), - } + /// Name of the associated constant. + /// + /// Returns `None` if the value doesn't have a defined constant. + pub const fn name(self) -> Option<&'static str> { + Some(match self { + Self::KEYWORD => "KEYWORD", + Self::KEYWORD_PRESET => "KEYWORD_PRESET", + Self::MENTION_SPAM => "MENTION_SPAM", + Self::SPAM => "SPAM", + _ => return None, + }) } } -impl From for u8 { - fn from(value: AutoModerationTriggerType) -> Self { - match value { - AutoModerationTriggerType::Keyword => 1, - AutoModerationTriggerType::Spam => 3, - AutoModerationTriggerType::KeywordPreset => 4, - AutoModerationTriggerType::MentionSpam => 5, - AutoModerationTriggerType::Unknown(unknown) => unknown, - } - } -} +impl_typed!(AutoModerationTriggerType, u8); #[cfg(test)] mod tests { @@ -68,10 +61,10 @@ mod tests { #[test] fn values() { - assert_eq!(1, u8::from(AutoModerationTriggerType::Keyword)); - assert_eq!(3, u8::from(AutoModerationTriggerType::Spam)); - assert_eq!(4, u8::from(AutoModerationTriggerType::KeywordPreset)); - assert_eq!(5, u8::from(AutoModerationTriggerType::MentionSpam)); - assert_eq!(250, u8::from(AutoModerationTriggerType::Unknown(250))); + assert_eq!(1, u8::from(AutoModerationTriggerType::KEYWORD)); + assert_eq!(3, u8::from(AutoModerationTriggerType::SPAM)); + assert_eq!(4, u8::from(AutoModerationTriggerType::KEYWORD_PRESET)); + assert_eq!(5, u8::from(AutoModerationTriggerType::MENTION_SPAM)); + assert_eq!(250, u8::from(AutoModerationTriggerType::new(250))); } } diff --git a/twilight-model/src/guild/default_message_notification_level.rs b/twilight-model/src/guild/default_message_notification_level.rs index 178d6fc6f72..c6e78e1ae5a 100644 --- a/twilight-model/src/guild/default_message_notification_level.rs +++ b/twilight-model/src/guild/default_message_notification_level.rs @@ -1,47 +1,51 @@ use serde::{Deserialize, Serialize}; -#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)] -#[non_exhaustive] -#[serde(from = "u8", into = "u8")] -pub enum DefaultMessageNotificationLevel { - All, - Mentions, - /// Variant value is unknown to the library. - Unknown(u8), -} +#[derive(Clone, Copy, Deserialize, Eq, Hash, PartialEq, Serialize)] +pub struct DefaultMessageNotificationLevel(u8); -impl From for DefaultMessageNotificationLevel { - fn from(value: u8) -> Self { - match value { - 0 => DefaultMessageNotificationLevel::All, - 1 => DefaultMessageNotificationLevel::Mentions, - unknown => DefaultMessageNotificationLevel::Unknown(unknown), - } - } -} +impl DefaultMessageNotificationLevel { + pub const ALL: Self = Self::new(0); -impl From for u8 { - fn from(value: DefaultMessageNotificationLevel) -> Self { - match value { - DefaultMessageNotificationLevel::All => 0, - DefaultMessageNotificationLevel::Mentions => 1, - DefaultMessageNotificationLevel::Unknown(unknown) => unknown, - } + pub const MENTIONS: Self = Self::new(1); + + /// Name of the associated constant. + /// + /// Returns `None` if the value doesn't have a defined constant. + pub const fn name(self) -> Option<&'static str> { + Some(match self { + Self::ALL => "ALL", + Self::MENTIONS => "MENTIONS", + _ => return None, + }) } } +impl_typed!(DefaultMessageNotificationLevel, u8); + #[cfg(test)] mod tests { use super::DefaultMessageNotificationLevel; use serde_test::Token; + const MAP: &[(DefaultMessageNotificationLevel, u8)] = &[ + (DefaultMessageNotificationLevel::ALL, 0), + (DefaultMessageNotificationLevel::MENTIONS, 1), + ]; + #[test] fn variants() { - serde_test::assert_tokens(&DefaultMessageNotificationLevel::All, &[Token::U8(0)]); - serde_test::assert_tokens(&DefaultMessageNotificationLevel::Mentions, &[Token::U8(1)]); - serde_test::assert_tokens( - &DefaultMessageNotificationLevel::Unknown(99), - &[Token::U8(99)], - ); + for (kind, num) in MAP { + serde_test::assert_tokens( + kind, + &[ + Token::NewtypeStruct { + name: "DefaultMessageNotificationLevel", + }, + Token::U8(*num), + ], + ); + assert_eq!(*kind, DefaultMessageNotificationLevel::from(*num)); + assert_eq!(*num, kind.get()); + } } } diff --git a/twilight-model/src/guild/explicit_content_filter.rs b/twilight-model/src/guild/explicit_content_filter.rs index eb944b6f5e8..f96179e629d 100644 --- a/twilight-model/src/guild/explicit_content_filter.rs +++ b/twilight-model/src/guild/explicit_content_filter.rs @@ -1,48 +1,55 @@ use serde::{Deserialize, Serialize}; -#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)] -#[non_exhaustive] -#[serde(from = "u8", into = "u8")] -pub enum ExplicitContentFilter { - None, - MembersWithoutRole, - AllMembers, - /// Variant value is unknown to the library. - Unknown(u8), -} +#[derive(Clone, Copy, Deserialize, Eq, Hash, PartialEq, Serialize)] +pub struct ExplicitContentFilter(u8); -impl From for ExplicitContentFilter { - fn from(value: u8) -> Self { - match value { - 0 => ExplicitContentFilter::None, - 1 => ExplicitContentFilter::MembersWithoutRole, - 2 => ExplicitContentFilter::AllMembers, - unknown => ExplicitContentFilter::Unknown(unknown), - } - } -} +impl ExplicitContentFilter { + pub const NONE: Self = Self::new(0); -impl From for u8 { - fn from(value: ExplicitContentFilter) -> Self { - match value { - ExplicitContentFilter::None => 0, - ExplicitContentFilter::MembersWithoutRole => 1, - ExplicitContentFilter::AllMembers => 2, - ExplicitContentFilter::Unknown(unknown) => unknown, - } + pub const MEMBERS_WITHOUT_ROLE: Self = Self::new(1); + + pub const ALL_MEMBERS: Self = Self::new(2); + + /// Name of the associated constant. + /// + /// Returns `None` if the value doesn't have a defined constant. + pub const fn name(self) -> Option<&'static str> { + Some(match self { + Self::ALL_MEMBERS => "ALL_MEMBERS", + Self::MEMBERS_WITHOUT_ROLE => "MEMBERS_WITHOUT_ROLE", + Self::NONE => "NONE", + _ => return None, + }) } } +impl_typed!(ExplicitContentFilter, u8); + #[cfg(test)] mod tests { use super::ExplicitContentFilter; use serde_test::Token; + const MAP: &[(ExplicitContentFilter, u8)] = &[ + (ExplicitContentFilter::NONE, 0), + (ExplicitContentFilter::MEMBERS_WITHOUT_ROLE, 1), + (ExplicitContentFilter::ALL_MEMBERS, 2), + ]; + #[test] fn variants() { - serde_test::assert_tokens(&ExplicitContentFilter::None, &[Token::U8(0)]); - serde_test::assert_tokens(&ExplicitContentFilter::MembersWithoutRole, &[Token::U8(1)]); - serde_test::assert_tokens(&ExplicitContentFilter::AllMembers, &[Token::U8(2)]); - serde_test::assert_tokens(&ExplicitContentFilter::Unknown(99), &[Token::U8(99)]); + for (kind, num) in MAP { + serde_test::assert_tokens( + kind, + &[ + Token::NewtypeStruct { + name: "ExplicitContentFilter", + }, + Token::U8(*num), + ], + ); + assert_eq!(*kind, ExplicitContentFilter::from(*num)); + assert_eq!(*num, kind.get()); + } } } diff --git a/twilight-model/src/guild/feature.rs b/twilight-model/src/guild/feature.rs index 18f8b74c99d..42e23bc4483 100644 --- a/twilight-model/src/guild/feature.rs +++ b/twilight-model/src/guild/feature.rs @@ -1,6 +1,4 @@ -#![allow(deprecated)] -use std::borrow::Cow; - +use crate::util::known_string::KnownString; use serde::{Deserialize, Serialize}; /// Special and optional guild features. @@ -8,231 +6,207 @@ use serde::{Deserialize, Serialize}; /// See [Discord Docs/Guild Features]. /// /// [Discord Docs/Guild Features]: https://discord.com/developers/docs/resources/guild#guild-object-guild-features -#[derive(Clone, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)] -#[non_exhaustive] -#[serde(from = "String", into = "Cow<'static, str>")] -pub enum GuildFeature { +#[derive(Clone, Copy, Deserialize, Eq, Hash, PartialEq, Serialize)] +pub struct GuildFeature(KnownString<64>); + +impl GuildFeature { /// Has access to set an animated guild banner image. - AnimatedBanner, + pub const ANIMATED_BANNER: Self = Self::from_bytes(b"ANIMATED_BANNER"); + /// Has access to set an animated guild icon. - AnimatedIcon, + pub const ANIMATED_ICON: Self = Self::from_bytes(b"ANIMATED_ICON"); + /// Has set up auto moderation rules. - AutoModeration, + pub const AUTO_MODERATION: Self = Self::from_bytes(b"AUTO_MODERATION"); + /// Has access to set a guild banner image. - Banner, + pub const BANNER: Self = Self::from_bytes(b"BANNER"); + /// Has access to use commerce features (create store channels). #[deprecated] - Commerce, + pub const COMMERCE: Self = Self::from_bytes(b"COMMERCE"); + /// Can enable welcome screen, membership screening, stage channels, /// discovery, and receives community updates. - Community, - /// Guild has enabled monetization. - CreatorMonetizableProvisional, - /// Guild has enabled the role subscription promotional page. - CreatorStorePage, + pub const COMMUNITY: Self = Self::from_bytes(b"COMMUNITY"); + /// Guild has been set as a support server on the App Directory. - DeveloperSupportServer, + pub const DEVELOPER_SUPPORT_SERVER: Self = Self::from_bytes(b"DEVELOPER_SUPPORT_SERVER"); + /// Is able to be discovered in the directory. - Discoverable, + pub const DISCOVERABLE: Self = Self::from_bytes(b"DISCOVERABLE"); + /// Is able to be featured in the directory. - Featurable, + pub const FEATURABLE: Self = Self::from_bytes(b"FEATURABLE"); + /// Invites have been paused, this prevents new users from joining. - InvitesDisabled, + pub const INVITES_DISABLED: Self = Self::from_bytes(b"INVITES_DISABLED"); + /// Has access to set an invite splash background. - InviteSplash, + pub const INVITE_SPLASH: Self = Self::from_bytes(b"INVITE_SPLASH"); + /// Has enabled membership screening. - MemberVerificationGateEnabled, + pub const MEMBER_VERIFICATION_GATE_ENABLED: Self = + Self::from_bytes(b"MEMBER_VERIFICATION_GATE_ENABLED"); + /// Has enabled monetization. - #[deprecated(since = "0.14.1", note = "not in active use by discord")] - MonetizationEnabled, + pub const MONETIZATION_ENABLED: Self = Self::from_bytes(b"MONETIZATION_ENABLED"); + /// Has increased custom sticker slots. - MoreStickers, + pub const MORE_STICKERS: Self = Self::from_bytes(b"MORE_STICKERS"); + /// Has access to create news channels. - News, + pub const NEWS: Self = Self::from_bytes(b"NEWS"); + /// Is partnered. - Partnered, - /// Can be previewed before joining via membership screening or the directory. - PreviewEnabled, + pub const PARTNERED: Self = Self::from_bytes(b"PARTNERED"); + + /// Can be previewed before joining via membership screening or the + /// directory. + pub const PREVIEW_ENABLED: Self = Self::from_bytes(b"PREVIEW_ENABLED"); + /// Has access to create private threads. - PrivateThreads, + pub const PRIVATE_THREADS: Self = Self::from_bytes(b"PRIVATE_THREADS"); + /// Is able to set role icons. - RoleIcons, - /// Guild has role subscriptions that can be purchased. - RoleSubscriptionsAvailableForPurchase, - /// Guild has enabled role subscriptions. - RoleSubscriptionsEnabled, + pub const ROLE_ICONS: Self = Self::from_bytes(b"ROLE_ICONS"); + /// Has enabled ticketed events. - TicketedEventsEnabled, + pub const TICKETED_EVENTS_ENABLED: Self = Self::from_bytes(b"TICKETED_EVENTS_ENABLED"); + /// Has access to set a vanity URL. - VanityUrl, + pub const VANITY_URL: Self = Self::from_bytes(b"VANITY_URL"); + /// Is verified. - Verified, - /// Has access to set 384kps bitrate in voice (previously VIP voice servers). - VipRegions, + pub const VERIFIED: Self = Self::from_bytes(b"VERIFIED"); + + /// Has access to set 384kps bitrate in voice (previously VIP voice + /// servers). + pub const VIP_REGIONS: Self = Self::from_bytes(b"VIP_REGIONS"); + /// Has enabled the welcome screen. - WelcomeScreenEnabled, - /// Variant value is unknown to the library. - Unknown(String), -} + pub const WELCOME_SCREEN_ENABLED: Self = Self::from_bytes(b"WELCOME_SCREEN_ENABLED"); -impl From for Cow<'static, str> { - fn from(value: GuildFeature) -> Self { - match value { - GuildFeature::AnimatedBanner => "ANIMATED_BANNER".into(), - GuildFeature::AnimatedIcon => "ANIMATED_ICON".into(), - GuildFeature::AutoModeration => "AUTO_MODERATION".into(), - GuildFeature::Banner => "BANNER".into(), - GuildFeature::Commerce => "COMMERCE".into(), - GuildFeature::Community => "COMMUNITY".into(), - GuildFeature::CreatorMonetizableProvisional => "CREATOR_MONETIZABLE_PROVISIONAL".into(), - GuildFeature::CreatorStorePage => "CREATOR_STORE_PAGE".into(), - GuildFeature::DeveloperSupportServer => "DEVELOPER_SUPPORT_SERVER".into(), - GuildFeature::Discoverable => "DISCOVERABLE".into(), - GuildFeature::Featurable => "FEATURABLE".into(), - GuildFeature::InvitesDisabled => "INVITES_DISABLED".into(), - GuildFeature::InviteSplash => "INVITE_SPLASH".into(), - GuildFeature::MemberVerificationGateEnabled => { - "MEMBER_VERIFICATION_GATE_ENABLED".into() - } - GuildFeature::MonetizationEnabled => "MONETIZATION_ENABLED".into(), - GuildFeature::MoreStickers => "MORE_STICKERS".into(), - GuildFeature::News => "NEWS".into(), - GuildFeature::Partnered => "PARTNERED".into(), - GuildFeature::PreviewEnabled => "PREVIEW_ENABLED".into(), - GuildFeature::PrivateThreads => "PRIVATE_THREADS".into(), - GuildFeature::RoleIcons => "ROLE_ICONS".into(), - GuildFeature::RoleSubscriptionsAvailableForPurchase => { - "ROLE_SUBSCRIPTIONS_AVAILABLE_FOR_PURCHASE".into() - } - GuildFeature::RoleSubscriptionsEnabled => "ROLE_SUBSCRIPTIONS_ENABLED".into(), - GuildFeature::TicketedEventsEnabled => "TICKETED_EVENTS_ENABLED".into(), - GuildFeature::VanityUrl => "VANITY_URL".into(), - GuildFeature::Verified => "VERIFIED".into(), - GuildFeature::VipRegions => "VIP_REGIONS".into(), - GuildFeature::WelcomeScreenEnabled => "WELCOME_SCREEN_ENABLED".into(), - GuildFeature::Unknown(unknown) => unknown.into(), - } + /// Name of the associated constant. + /// + /// Returns `None` if the value doesn't have a defined constant. + pub const fn name(self) -> Option<&'static str> { + Some(match self { + Self::ANIMATED_BANNER => "ANIMATED_BANNER", + Self::ANIMATED_ICON => "ANIMATED_ICON", + Self::AUTO_MODERATION => "AUTO_MODERATION", + Self::BANNER => "BANNER", + #[allow(deprecated)] + Self::COMMERCE => "COMMERCE", + Self::COMMUNITY => "COMMUNITY", + Self::DEVELOPER_SUPPORT_SERVER => "DEVELOPER_SUPPORT_SERVER", + Self::DISCOVERABLE => "DISCOVERABLE", + Self::FEATURABLE => "FEATURABLE", + Self::INVITES_DISABLED => "INVITES_DISABLED", + Self::INVITE_SPLASH => "INVITE_SPLASH", + Self::MEMBER_VERIFICATION_GATE_ENABLED => "MEMBER_VERIFICATION_GATE_ENABLED", + Self::MONETIZATION_ENABLED => "MONETIZATION_ENABLED", + Self::MORE_STICKERS => "MORE_STICKERS", + Self::NEWS => "NEWS", + Self::PARTNERED => "PARTNERED", + Self::PREVIEW_ENABLED => "PREVIEW_ENABLED", + Self::PRIVATE_THREADS => "PRIVATE_THREADS", + Self::ROLE_ICONS => "ROLE_ICONS", + Self::TICKETED_EVENTS_ENABLED => "TICKETED_EVENTS_ENABLED", + Self::VANITY_URL => "VANITY_URL", + Self::VERIFIED => "VERIFIED", + Self::VIP_REGIONS => "VIP_REGIONS", + Self::WELCOME_SCREEN_ENABLED => "WELCOME_SCREEN_ENABLED", + _ => return None, + }) } } -impl From for GuildFeature { - fn from(value: String) -> Self { - match value.as_str() { - "ANIMATED_BANNER" => Self::AnimatedBanner, - "ANIMATED_ICON" => Self::AnimatedIcon, - "AUTO_MODERATION" => Self::AutoModeration, - "BANNER" => Self::Banner, - "COMMERCE" => Self::Commerce, - "COMMUNITY" => Self::Community, - "CREATOR_MONETIZABLE_PROVISIONAL" => GuildFeature::CreatorMonetizableProvisional, - "CREATOR_STORE_PAGE" => GuildFeature::CreatorStorePage, - "DEVELOPER_SUPPORT_SERVER" => Self::DeveloperSupportServer, - "DISCOVERABLE" => Self::Discoverable, - "FEATURABLE" => Self::Featurable, - "INVITES_DISABLED" => Self::InvitesDisabled, - "INVITE_SPLASH" => Self::InviteSplash, - "MEMBER_VERIFICATION_GATE_ENABLED" => Self::MemberVerificationGateEnabled, - "MONETIZATION_ENABLED" => Self::MonetizationEnabled, - "MORE_STICKERS" => Self::MoreStickers, - "NEWS" => Self::News, - "PARTNERED" => Self::Partnered, - "PREVIEW_ENABLED" => Self::PreviewEnabled, - "PRIVATE_THREADS" => Self::PrivateThreads, - "ROLE_ICONS" => Self::RoleIcons, - "ROLE_SUBSCRIPTIONS_AVAILABLE_FOR_PURCHASE" => { - GuildFeature::RoleSubscriptionsAvailableForPurchase - } - "ROLE_SUBSCRIPTIONS_ENABLED" => GuildFeature::RoleSubscriptionsEnabled, - "TICKETED_EVENTS_ENABLED" => Self::TicketedEventsEnabled, - "VANITY_URL" => Self::VanityUrl, - "VERIFIED" => Self::Verified, - "VIP_REGIONS" => Self::VipRegions, - "WELCOME_SCREEN_ENABLED" => Self::WelcomeScreenEnabled, - _ => Self::Unknown(value), - } - } -} +impl_typed!(GuildFeature, String); #[cfg(test)] mod tests { use super::GuildFeature; + use serde::{Deserialize, Serialize}; use serde_test::Token; + use static_assertions::assert_impl_all; + use std::{fmt::Debug, hash::Hash, str::FromStr, string::ToString}; + + assert_impl_all!( + GuildFeature: AsRef, + Clone, + Copy, + Debug, + Deserialize<'static>, + Eq, + FromStr, + Hash, + PartialEq, + Send, + Serialize, + Sync, + ToString, + TryFrom<&'static str>, + ); + + const MAP: &[(GuildFeature, &str)] = &[ + (GuildFeature::ANIMATED_BANNER, "ANIMATED_BANNER"), + (GuildFeature::ANIMATED_ICON, "ANIMATED_ICON"), + (GuildFeature::AUTO_MODERATION, "AUTO_MODERATION"), + (GuildFeature::BANNER, "BANNER"), + #[allow(deprecated)] + (GuildFeature::COMMERCE, "COMMERCE"), + (GuildFeature::COMMUNITY, "COMMUNITY"), + ( + GuildFeature::DEVELOPER_SUPPORT_SERVER, + "DEVELOPER_SUPPORT_SERVER", + ), + (GuildFeature::DISCOVERABLE, "DISCOVERABLE"), + (GuildFeature::FEATURABLE, "FEATURABLE"), + (GuildFeature::INVITES_DISABLED, "INVITES_DISABLED"), + (GuildFeature::INVITE_SPLASH, "INVITE_SPLASH"), + ( + GuildFeature::MEMBER_VERIFICATION_GATE_ENABLED, + "MEMBER_VERIFICATION_GATE_ENABLED", + ), + (GuildFeature::MONETIZATION_ENABLED, "MONETIZATION_ENABLED"), + (GuildFeature::MORE_STICKERS, "MORE_STICKERS"), + (GuildFeature::NEWS, "NEWS"), + (GuildFeature::PARTNERED, "PARTNERED"), + (GuildFeature::PREVIEW_ENABLED, "PREVIEW_ENABLED"), + (GuildFeature::PRIVATE_THREADS, "PRIVATE_THREADS"), + (GuildFeature::ROLE_ICONS, "ROLE_ICONS"), + ( + GuildFeature::TICKETED_EVENTS_ENABLED, + "TICKETED_EVENTS_ENABLED", + ), + (GuildFeature::VANITY_URL, "VANITY_URL"), + (GuildFeature::VERIFIED, "VERIFIED"), + (GuildFeature::VIP_REGIONS, "VIP_REGIONS"), + ( + GuildFeature::WELCOME_SCREEN_ENABLED, + "WELCOME_SCREEN_ENABLED", + ), + ]; #[test] fn variants() { - serde_test::assert_tokens( - &GuildFeature::AnimatedBanner, - &[Token::Str("ANIMATED_BANNER")], - ); - serde_test::assert_tokens(&GuildFeature::AnimatedIcon, &[Token::Str("ANIMATED_ICON")]); - serde_test::assert_tokens( - &GuildFeature::AutoModeration, - &[Token::Str("AUTO_MODERATION")], - ); - serde_test::assert_tokens(&GuildFeature::Banner, &[Token::Str("BANNER")]); - serde_test::assert_tokens(&GuildFeature::Commerce, &[Token::Str("COMMERCE")]); - serde_test::assert_tokens(&GuildFeature::Community, &[Token::Str("COMMUNITY")]); - serde_test::assert_tokens( - &GuildFeature::CreatorMonetizableProvisional, - &[Token::Str("CREATOR_MONETIZABLE_PROVISIONAL")], - ); - serde_test::assert_tokens( - &GuildFeature::CreatorStorePage, - &[Token::Str("CREATOR_STORE_PAGE")], - ); - serde_test::assert_tokens( - &GuildFeature::DeveloperSupportServer, - &[Token::Str("DEVELOPER_SUPPORT_SERVER")], - ); - serde_test::assert_tokens(&GuildFeature::Discoverable, &[Token::Str("DISCOVERABLE")]); - serde_test::assert_tokens(&GuildFeature::Featurable, &[Token::Str("FEATURABLE")]); - serde_test::assert_tokens( - &GuildFeature::InvitesDisabled, - &[Token::Str("INVITES_DISABLED")], - ); - serde_test::assert_tokens(&GuildFeature::InviteSplash, &[Token::Str("INVITE_SPLASH")]); - serde_test::assert_tokens( - &GuildFeature::MemberVerificationGateEnabled, - &[Token::Str("MEMBER_VERIFICATION_GATE_ENABLED")], - ); - serde_test::assert_tokens( - &GuildFeature::MonetizationEnabled, - &[Token::Str("MONETIZATION_ENABLED")], - ); - serde_test::assert_tokens(&GuildFeature::MoreStickers, &[Token::Str("MORE_STICKERS")]); - serde_test::assert_tokens(&GuildFeature::News, &[Token::Str("NEWS")]); - serde_test::assert_tokens(&GuildFeature::Partnered, &[Token::Str("PARTNERED")]); - serde_test::assert_tokens( - &GuildFeature::PreviewEnabled, - &[Token::Str("PREVIEW_ENABLED")], - ); - serde_test::assert_tokens( - &GuildFeature::PrivateThreads, - &[Token::Str("PRIVATE_THREADS")], - ); - serde_test::assert_tokens(&GuildFeature::RoleIcons, &[Token::Str("ROLE_ICONS")]); - serde_test::assert_tokens( - &GuildFeature::RoleSubscriptionsAvailableForPurchase, - &[Token::Str("ROLE_SUBSCRIPTIONS_AVAILABLE_FOR_PURCHASE")], - ); - serde_test::assert_tokens( - &GuildFeature::RoleSubscriptionsEnabled, - &[Token::Str("ROLE_SUBSCRIPTIONS_ENABLED")], - ); - serde_test::assert_tokens( - &GuildFeature::TicketedEventsEnabled, - &[Token::Str("TICKETED_EVENTS_ENABLED")], - ); - serde_test::assert_tokens(&GuildFeature::VanityUrl, &[Token::Str("VANITY_URL")]); - serde_test::assert_tokens(&GuildFeature::Verified, &[Token::Str("VERIFIED")]); - serde_test::assert_tokens(&GuildFeature::VipRegions, &[Token::Str("VIP_REGIONS")]); - serde_test::assert_tokens( - &GuildFeature::WelcomeScreenEnabled, - &[Token::Str("WELCOME_SCREEN_ENABLED")], - ); - serde_test::assert_tokens( - &GuildFeature::Unknown("UNKNOWN".to_owned()), - &[Token::Str("UNKNOWN")], - ); + for (kind, name) in MAP { + serde_test::assert_tokens( + kind, + &[ + Token::NewtypeStruct { + name: "GuildFeature", + }, + Token::Str(name), + ], + ); + assert_eq!(Some(*kind), GuildFeature::new(name)); + assert_eq!(*name, kind.as_ref()); + assert_eq!(Ok(*kind), GuildFeature::from_str(name)); + assert_eq!(Ok(*kind), GuildFeature::try_from(*name)); + assert_eq!(name, &kind.to_string()); + assert_eq!(*name, kind.get()); + } } } diff --git a/twilight-model/src/guild/integration.rs b/twilight-model/src/guild/integration.rs index 17135507c31..f90800f2474 100644 --- a/twilight-model/src/guild/integration.rs +++ b/twilight-model/src/guild/integration.rs @@ -6,6 +6,7 @@ use crate::{ marker::{GuildMarker, IntegrationMarker, RoleMarker}, Id, }, + oauth::Scope, user::User, util::Timestamp, }; @@ -38,9 +39,9 @@ pub struct GuildIntegration { pub role_id: Option>, /// An array of [OAuth2 scopes] which the application has been authorized for. /// - /// [OAuth2 scopes]: crate::oauth::scope + /// [OAuth2 scopes]: crate::oauth::Scope #[serde(skip_serializing_if = "Option::is_none")] - pub scopes: Option>, + pub scopes: Option>, #[serde(skip_serializing_if = "Option::is_none")] pub subscriber_count: Option, #[serde(skip_serializing_if = "Option::is_none")] @@ -60,7 +61,7 @@ mod tests { use crate::{ guild::GuildIntegrationType, id::Id, - oauth::scope, + oauth::Scope, test::image_hash, util::datetime::{Timestamp, TimestampParseError}, }; @@ -80,18 +81,15 @@ mod tests { application: None, enable_emoticons: Some(true), enabled: Some(true), - expire_behavior: Some(IntegrationExpireBehavior::Kick), + expire_behavior: Some(IntegrationExpireBehavior::KICK), expire_grace_period: Some(3_600), guild_id: None, id: Id::new(2), - kind: GuildIntegrationType::Discord, + kind: GuildIntegrationType::DISCORD, name: "integration name".to_owned(), revoked: Some(false), role_id: Some(Id::new(3)), - scopes: Some(Vec::from([ - scope::APPLICATIONS_COMMANDS.to_owned(), - scope::BOT.to_owned(), - ])), + scopes: Some(Vec::from([Scope::APPLICATIONS_COMMANDS, Scope::BOT])), subscriber_count: Some(1337), synced_at: Some(synced_at), syncing: Some(false), @@ -139,6 +137,9 @@ mod tests { Token::Bool(true), Token::Str("expire_behavior"), Token::Some, + Token::NewtypeStruct { + name: "IntegrationExpireBehavior", + }, Token::U8(1), Token::Str("expire_grace_period"), Token::Some, @@ -147,6 +148,9 @@ mod tests { Token::NewtypeStruct { name: "Id" }, Token::Str("2"), Token::Str("type"), + Token::NewtypeStruct { + name: "GuildIntegrationType", + }, Token::Str("discord"), Token::Str("name"), Token::Str("integration name"), @@ -160,8 +164,10 @@ mod tests { Token::Str("scopes"), Token::Some, Token::Seq { len: Some(2) }, - Token::Str(scope::APPLICATIONS_COMMANDS), - Token::Str(scope::BOT), + Token::NewtypeStruct { name: "Scope" }, + Token::Str(Scope::APPLICATIONS_COMMANDS.get()), + Token::NewtypeStruct { name: "Scope" }, + Token::Str(Scope::BOT.get()), Token::SeqEnd, Token::Str("subscriber_count"), Token::Some, @@ -221,18 +227,15 @@ mod tests { }), enable_emoticons: Some(true), enabled: None, - expire_behavior: Some(IntegrationExpireBehavior::Kick), + expire_behavior: Some(IntegrationExpireBehavior::KICK), expire_grace_period: Some(3_600), guild_id: None, id: Id::new(2), - kind: GuildIntegrationType::Discord, + kind: GuildIntegrationType::DISCORD, name: "integration name".to_owned(), revoked: Some(false), role_id: Some(Id::new(3)), - scopes: Some(Vec::from([ - scope::APPLICATIONS_COMMANDS.to_owned(), - scope::BOT.to_owned(), - ])), + scopes: Some(Vec::from([Scope::APPLICATIONS_COMMANDS, Scope::BOT])), subscriber_count: Some(1337), synced_at: Some(synced_at), syncing: Some(false), @@ -295,6 +298,9 @@ mod tests { Token::None, Token::Str("expire_behavior"), Token::Some, + Token::NewtypeStruct { + name: "IntegrationExpireBehavior", + }, Token::U8(1), Token::Str("expire_grace_period"), Token::Some, @@ -303,6 +309,9 @@ mod tests { Token::NewtypeStruct { name: "Id" }, Token::Str("2"), Token::Str("type"), + Token::NewtypeStruct { + name: "GuildIntegrationType", + }, Token::Str("discord"), Token::Str("name"), Token::Str("integration name"), @@ -316,8 +325,10 @@ mod tests { Token::Str("scopes"), Token::Some, Token::Seq { len: Some(2) }, - Token::Str(scope::APPLICATIONS_COMMANDS), - Token::Str(scope::BOT), + Token::NewtypeStruct { name: "Scope" }, + Token::Str("applications.commands"), + Token::NewtypeStruct { name: "Scope" }, + Token::Str("bot"), Token::SeqEnd, Token::Str("subscriber_count"), Token::Some, diff --git a/twilight-model/src/guild/integration_expire_behavior.rs b/twilight-model/src/guild/integration_expire_behavior.rs index e13b69068e9..068d38e5374 100644 --- a/twilight-model/src/guild/integration_expire_behavior.rs +++ b/twilight-model/src/guild/integration_expire_behavior.rs @@ -1,47 +1,54 @@ use serde::{Deserialize, Serialize}; /// Behavior to perform when the user's integration expires. -#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)] -#[non_exhaustive] -#[serde(from = "u8", into = "u8")] -pub enum IntegrationExpireBehavior { +#[derive(Clone, Copy, Deserialize, Eq, Hash, PartialEq, Serialize)] +pub struct IntegrationExpireBehavior(u8); + +impl IntegrationExpireBehavior { /// Remove the role when the integration expires. - RemoveRole, + pub const REMOVE_ROLE: Self = Self::new(0); + /// Kick the user when the integration expires. - Kick, - /// Variant value is unknown to the library. - Unknown(u8), -} + pub const KICK: Self = Self::new(1); -impl From for IntegrationExpireBehavior { - fn from(value: u8) -> Self { - match value { - 0 => IntegrationExpireBehavior::RemoveRole, - 1 => IntegrationExpireBehavior::Kick, - unknown => IntegrationExpireBehavior::Unknown(unknown), - } + /// Name of the associated constant. + /// + /// Returns `None` if the value doesn't have a defined constant. + pub const fn name(self) -> Option<&'static str> { + Some(match self { + Self::KICK => "KICK", + Self::REMOVE_ROLE => "REMOVE_ROLE", + _ => return None, + }) } } -impl From for u8 { - fn from(value: IntegrationExpireBehavior) -> Self { - match value { - IntegrationExpireBehavior::RemoveRole => 0, - IntegrationExpireBehavior::Kick => 1, - IntegrationExpireBehavior::Unknown(unknown) => unknown, - } - } -} +impl_typed!(IntegrationExpireBehavior, u8); #[cfg(test)] mod tests { use super::IntegrationExpireBehavior; use serde_test::Token; + const MAP: &[(IntegrationExpireBehavior, u8)] = &[ + (IntegrationExpireBehavior::REMOVE_ROLE, 0), + (IntegrationExpireBehavior::KICK, 1), + ]; + #[test] - fn integration_expire_behavior() { - serde_test::assert_tokens(&IntegrationExpireBehavior::RemoveRole, &[Token::U8(0)]); - serde_test::assert_tokens(&IntegrationExpireBehavior::Kick, &[Token::U8(1)]); - serde_test::assert_tokens(&IntegrationExpireBehavior::Unknown(99), &[Token::U8(99)]); + fn variants() { + for (kind, num) in MAP { + serde_test::assert_tokens( + kind, + &[ + Token::NewtypeStruct { + name: "IntegrationExpireBehavior", + }, + Token::U8(*num), + ], + ); + assert_eq!(*kind, IntegrationExpireBehavior::from(*num)); + assert_eq!(*num, kind.get()); + } } } diff --git a/twilight-model/src/guild/integration_type.rs b/twilight-model/src/guild/integration_type.rs index a34274dfdf3..96f5dbb1399 100644 --- a/twilight-model/src/guild/integration_type.rs +++ b/twilight-model/src/guild/integration_type.rs @@ -1,6 +1,4 @@ -#![allow(deprecated)] -use std::borrow::Cow; - +use crate::util::known_string::KnownString; use serde::{Deserialize, Serialize}; /// Special and optional guild features. @@ -8,57 +6,55 @@ use serde::{Deserialize, Serialize}; /// See [Discord Docs/Guild Features]. /// /// [Discord Docs/Guild Features]: https://discord.com/developers/docs/resources/guild#guild-object-guild-features -#[derive(Clone, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)] -#[non_exhaustive] -#[serde(from = "String", into = "Cow<'static, str>")] -pub enum GuildIntegrationType { +#[derive(Clone, Copy, Deserialize, Eq, Hash, PartialEq, Serialize)] +pub struct GuildIntegrationType(KnownString<32>); + +impl GuildIntegrationType { /// Integration is a Discord application. - Discord, + pub const DISCORD: Self = Self::from_bytes(b"discord"); + /// Integration is a Twitch connection. - Twitch, - /// Integration is a YouTube connection. - YouTube, - /// Variant value is unknown to the library. - Unknown(String), -} + pub const TWITCH: Self = Self::from_bytes(b"twitch"); -impl From for Cow<'static, str> { - fn from(value: GuildIntegrationType) -> Self { - match value { - GuildIntegrationType::Discord => "discord".into(), - GuildIntegrationType::Twitch => "twitch".into(), - GuildIntegrationType::YouTube => "youtube".into(), - GuildIntegrationType::Unknown(unknown) => unknown.into(), - } - } -} + /// Integration is a Youtube connection. + pub const YOUTUBE: Self = Self::from_bytes(b"youtube"); -impl From for GuildIntegrationType { - fn from(value: String) -> Self { - match value.as_str() { - "discord" => Self::Discord, - "twitch" => Self::Twitch, - "youtube" => Self::YouTube, - _ => Self::Unknown(value), - } + /// Name of the associated constant. + /// + /// Returns `None` if the value doesn't have a defined constant. + pub const fn name(self) -> Option<&'static str> { + Some(match self { + Self::DISCORD => "DISCORD", + Self::TWITCH => "TWITCH", + Self::YOUTUBE => "YOUTUBE", + _ => return None, + }) } } +impl_typed!(GuildIntegrationType, String); + #[cfg(test)] mod tests { use super::GuildIntegrationType; - use serde_test::Token; + use serde::{Deserialize, Serialize}; + use static_assertions::assert_impl_all; + use std::{fmt::Debug, hash::Hash, str::FromStr, string::ToString}; - #[test] - fn variants() { - const MAP: &[(GuildIntegrationType, &str)] = &[ - (GuildIntegrationType::Discord, "discord"), - (GuildIntegrationType::Twitch, "twitch"), - (GuildIntegrationType::YouTube, "youtube"), - ]; - - for (integration_type, value) in MAP { - serde_test::assert_tokens(integration_type, &[Token::Str(value)]); - } - } + assert_impl_all!( + GuildIntegrationType: AsRef, + Clone, + Copy, + Debug, + Deserialize<'static>, + Eq, + FromStr, + Hash, + PartialEq, + Send, + Serialize, + Sync, + ToString, + TryFrom<&'static str>, + ); } diff --git a/twilight-model/src/guild/invite/channel.rs b/twilight-model/src/guild/invite/channel.rs index de75d2761d0..64ac5914ec9 100644 --- a/twilight-model/src/guild/invite/channel.rs +++ b/twilight-model/src/guild/invite/channel.rs @@ -27,7 +27,7 @@ mod tests { let value = InviteChannel { id: Id::new(1), name: Some("channel name".to_owned()), - kind: ChannelType::GuildText, + kind: ChannelType::GUILD_TEXT, }; serde_test::assert_tokens( @@ -44,6 +44,9 @@ mod tests { Token::Some, Token::Str("channel name"), Token::Str("type"), + Token::NewtypeStruct { + name: "ChannelType", + }, Token::U8(0), Token::StructEnd, ], diff --git a/twilight-model/src/guild/invite/guild.rs b/twilight-model/src/guild/invite/guild.rs index 704d983669b..a7f0d93f18d 100644 --- a/twilight-model/src/guild/invite/guild.rs +++ b/twilight-model/src/guild/invite/guild.rs @@ -50,14 +50,14 @@ mod tests { let value = InviteGuild { banner: Some(image_hash::BANNER), description: Some("a description".to_owned()), - features: Vec::from([GuildFeature::Community]), + features: Vec::from([GuildFeature::COMMUNITY]), icon: Some(image_hash::ICON), id: Id::new(1), name: "guild name".to_owned(), premium_subscription_count: Some(14), splash: Some(image_hash::SPLASH), vanity_url_code: Some("twilight".to_owned()), - verification_level: VerificationLevel::Medium, + verification_level: VerificationLevel::MEDIUM, welcome_screen: Some(WelcomeScreen { description: Some("welcome description".to_owned()), welcome_channels: vec![ @@ -92,7 +92,10 @@ mod tests { Token::Str("a description"), Token::Str("features"), Token::Seq { len: Some(1) }, - Token::Str("COMMUNITY"), + Token::NewtypeStruct { + name: "GuildFeature", + }, + Token::Str(GuildFeature::COMMUNITY.get()), Token::SeqEnd, Token::Str("icon"), Token::Some, @@ -112,6 +115,9 @@ mod tests { Token::Some, Token::Str("twilight"), Token::Str("verification_level"), + Token::NewtypeStruct { + name: "VerificationLevel", + }, Token::U8(2), Token::Str("welcome_screen"), Token::Some, diff --git a/twilight-model/src/guild/invite/mod.rs b/twilight-model/src/guild/invite/mod.rs index 389a98d2644..7d2bd68e5c2 100644 --- a/twilight-model/src/guild/invite/mod.rs +++ b/twilight-model/src/guild/invite/mod.rs @@ -96,7 +96,7 @@ mod tests { approximate_presence_count: Some(7), channel: Some(InviteChannel { id: Id::new(2), - kind: ChannelType::Group, + kind: ChannelType::GROUP, name: None, }), code: "uniquecode".to_owned(), @@ -106,7 +106,7 @@ mod tests { inviter: None, max_age: None, max_uses: None, - target_type: Some(TargetType::Stream), + target_type: Some(TargetType::STREAM), target_user: None, temporary: None, uses: None, @@ -135,12 +135,16 @@ mod tests { Token::NewtypeStruct { name: "Id" }, Token::Str("2"), Token::Str("type"), + Token::NewtypeStruct { + name: "ChannelType", + }, Token::U8(3), Token::StructEnd, Token::Str("code"), Token::Str("uniquecode"), Token::Str("target_type"), Token::Some, + Token::NewtypeStruct { name: "TargetType" }, Token::U8(1), Token::StructEnd, ], @@ -158,7 +162,7 @@ mod tests { approximate_presence_count: Some(7), channel: Some(InviteChannel { id: Id::new(2), - kind: ChannelType::Group, + kind: ChannelType::GROUP, name: None, }), code: "uniquecode".to_owned(), @@ -167,14 +171,14 @@ mod tests { guild: Some(InviteGuild { banner: Some(image_hash::BANNER), description: Some("a description".to_owned()), - features: Vec::from([GuildFeature::Discoverable]), + features: Vec::from([GuildFeature::DISCOVERABLE]), icon: Some(image_hash::ICON), id: Id::new(1), name: "guild name".to_owned(), premium_subscription_count: None, splash: Some(image_hash::SPLASH), vanity_url_code: Some("twilight".to_owned()), - verification_level: VerificationLevel::Medium, + verification_level: VerificationLevel::MEDIUM, welcome_screen: Some(WelcomeScreen { description: Some("welcome description".to_owned()), welcome_channels: vec![ @@ -212,7 +216,7 @@ mod tests { }), max_age: Some(86_400), max_uses: Some(10), - target_type: Some(TargetType::Stream), + target_type: Some(TargetType::STREAM), target_user: Some(User { accent_color: None, avatar: None, @@ -257,6 +261,9 @@ mod tests { Token::NewtypeStruct { name: "Id" }, Token::Str("2"), Token::Str("type"), + Token::NewtypeStruct { + name: "ChannelType", + }, Token::U8(3), Token::StructEnd, Token::Str("code"), @@ -281,7 +288,10 @@ mod tests { Token::Str("a description"), Token::Str("features"), Token::Seq { len: Some(1) }, - Token::Str("DISCOVERABLE"), + Token::NewtypeStruct { + name: "GuildFeature", + }, + Token::Str(GuildFeature::DISCOVERABLE.get()), Token::SeqEnd, Token::Str("icon"), Token::Some, @@ -300,6 +310,9 @@ mod tests { Token::Some, Token::Str("twilight"), Token::Str("verification_level"), + Token::NewtypeStruct { + name: "VerificationLevel", + }, Token::U8(2), Token::Str("welcome_screen"), Token::Some, @@ -377,6 +390,7 @@ mod tests { Token::U64(10), Token::Str("target_type"), Token::Some, + Token::NewtypeStruct { name: "TargetType" }, Token::U8(1), Token::Str("target_user"), Token::Some, diff --git a/twilight-model/src/guild/invite/target_type.rs b/twilight-model/src/guild/invite/target_type.rs index a8ce2a9accf..bba6b76f607 100644 --- a/twilight-model/src/guild/invite/target_type.rs +++ b/twilight-model/src/guild/invite/target_type.rs @@ -1,43 +1,46 @@ use serde::{Deserialize, Serialize}; -#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)] -#[non_exhaustive] -#[serde(from = "u8", into = "u8")] -pub enum TargetType { - Stream, - EmbeddedApplication, - Unknown(u8), -} +#[derive(Clone, Copy, Deserialize, Eq, Hash, PartialEq, Serialize)] +pub struct TargetType(u8); -impl From for TargetType { - fn from(value: u8) -> Self { - match value { - 1 => TargetType::Stream, - 2 => TargetType::EmbeddedApplication, - unknown => TargetType::Unknown(unknown), - } - } -} +impl TargetType { + pub const STREAM: Self = Self::new(1); -impl From for u8 { - fn from(value: TargetType) -> Self { - match value { - TargetType::Stream => 1, - TargetType::EmbeddedApplication => 2, - TargetType::Unknown(unknown) => unknown, - } + pub const EMBEDDED_APPLICATION: Self = Self::new(2); + + /// Name of the associated constant. + /// + /// Returns `None` if the value doesn't have a defined constant. + pub const fn name(self) -> Option<&'static str> { + Some(match self { + Self::EMBEDDED_APPLICATION => "EMBEDDED_APPLICATION", + Self::STREAM => "STREAM", + _ => return None, + }) } } +impl_typed!(TargetType, u8); + #[cfg(test)] mod tests { use super::TargetType; use serde_test::Token; + const MAP: &[(TargetType, u8)] = &[ + (TargetType::STREAM, 1), + (TargetType::EMBEDDED_APPLICATION, 2), + ]; + #[test] fn variants() { - serde_test::assert_tokens(&TargetType::Stream, &[Token::U8(1)]); - serde_test::assert_tokens(&TargetType::EmbeddedApplication, &[Token::U8(2)]); - serde_test::assert_tokens(&TargetType::Unknown(99), &[Token::U8(99)]); + for (kind, num) in MAP { + serde_test::assert_tokens( + kind, + &[Token::NewtypeStruct { name: "TargetType" }, Token::U8(*num)], + ); + assert_eq!(*kind, TargetType::from(*num)); + assert_eq!(*num, kind.get()); + } } } diff --git a/twilight-model/src/guild/mfa_level.rs b/twilight-model/src/guild/mfa_level.rs index 0155f8fb230..17ef81b7956 100644 --- a/twilight-model/src/guild/mfa_level.rs +++ b/twilight-model/src/guild/mfa_level.rs @@ -1,44 +1,43 @@ use serde::{Deserialize, Serialize}; -#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)] -#[non_exhaustive] -#[serde(from = "u8", into = "u8")] -pub enum MfaLevel { - None, - Elevated, - /// Variant value is unknown to the library. - Unknown(u8), -} +#[derive(Clone, Copy, Deserialize, Eq, Hash, PartialEq, Serialize)] +pub struct MfaLevel(u8); -impl From for MfaLevel { - fn from(value: u8) -> Self { - match value { - 0 => MfaLevel::None, - 1 => MfaLevel::Elevated, - unknown => MfaLevel::Unknown(unknown), - } - } -} +impl MfaLevel { + pub const NONE: Self = Self::new(0); -impl From for u8 { - fn from(value: MfaLevel) -> Self { - match value { - MfaLevel::None => 0, - MfaLevel::Elevated => 1, - MfaLevel::Unknown(unknown) => unknown, - } + pub const ELEVATED: Self = Self::new(1); + + /// Name of the associated constant. + /// + /// Returns `None` if the value doesn't have a defined constant. + pub const fn name(self) -> Option<&'static str> { + Some(match self { + Self::ELEVATED => "ELEVATED", + Self::NONE => "NONE", + _ => return None, + }) } } +impl_typed!(MfaLevel, u8); + #[cfg(test)] mod tests { use super::MfaLevel; use serde_test::Token; + const MAP: &[(MfaLevel, u8)] = &[(MfaLevel::NONE, 0), (MfaLevel::ELEVATED, 1)]; + #[test] fn variants() { - serde_test::assert_tokens(&MfaLevel::None, &[Token::U8(0)]); - serde_test::assert_tokens(&MfaLevel::Elevated, &[Token::U8(1)]); - serde_test::assert_tokens(&MfaLevel::Unknown(99), &[Token::U8(99)]); + for (kind, num) in MAP { + serde_test::assert_tokens( + kind, + &[Token::NewtypeStruct { name: "MfaLevel" }, Token::U8(*num)], + ); + assert_eq!(*kind, MfaLevel::from(*num)); + assert_eq!(*num, kind.get()); + } } } diff --git a/twilight-model/src/guild/mod.rs b/twilight-model/src/guild/mod.rs index 65f0aa570f7..93e7e64c7af 100644 --- a/twilight-model/src/guild/mod.rs +++ b/twilight-model/src/guild/mod.rs @@ -897,12 +897,12 @@ mod tests { approximate_presence_count: Some(900), banner: Some(image_hash::BANNER), channels: Vec::new(), - default_message_notifications: DefaultMessageNotificationLevel::Mentions, + default_message_notifications: DefaultMessageNotificationLevel::MENTIONS, description: Some("a description".to_owned()), discovery_splash: Some(image_hash::SPLASH), emojis: Vec::new(), - explicit_content_filter: ExplicitContentFilter::MembersWithoutRole, - features: Vec::from([GuildFeature::Banner]), + explicit_content_filter: ExplicitContentFilter::MEMBERS_WITHOUT_ROLE, + features: Vec::from([GuildFeature::BANNER]), icon: Some(image_hash::ICON), id: Id::new(1), joined_at: Some(joined_at), @@ -912,16 +912,16 @@ mod tests { max_video_channel_users: Some(10), member_count: Some(12_000), members: Vec::new(), - mfa_level: MfaLevel::Elevated, + mfa_level: MfaLevel::ELEVATED, name: "the name".to_owned(), - nsfw_level: NSFWLevel::Default, + nsfw_level: NSFWLevel::DEFAULT, owner_id: Id::new(5), owner: Some(false), permissions: Some(Permissions::SEND_MESSAGES), preferred_locale: "en-us".to_owned(), premium_progress_bar_enabled: false, premium_subscription_count: Some(3), - premium_tier: PremiumTier::Tier1, + premium_tier: PremiumTier::TIER_1, presences: Vec::new(), public_updates_channel_id: None, roles: Vec::new(), @@ -934,7 +934,7 @@ mod tests { threads: Vec::new(), unavailable: false, vanity_url_code: Some("twilight".to_owned()), - verification_level: VerificationLevel::Medium, + verification_level: VerificationLevel::MEDIUM, voice_states: Vec::new(), widget_channel_id: Some(Id::new(8)), widget_enabled: Some(true), @@ -971,6 +971,9 @@ mod tests { Token::Seq { len: Some(0) }, Token::SeqEnd, Token::Str("default_message_notifications"), + Token::NewtypeStruct { + name: "DefaultMessageNotificationLevel", + }, Token::U8(1), Token::Str("description"), Token::Some, @@ -982,9 +985,15 @@ mod tests { Token::Seq { len: Some(0) }, Token::SeqEnd, Token::Str("explicit_content_filter"), + Token::NewtypeStruct { + name: "ExplicitContentFilter", + }, Token::U8(1), Token::Str("features"), Token::Seq { len: Some(1) }, + Token::NewtypeStruct { + name: "GuildFeature", + }, Token::Str("BANNER"), Token::SeqEnd, Token::Str("icon"), @@ -1014,10 +1023,12 @@ mod tests { Token::Seq { len: Some(0) }, Token::SeqEnd, Token::Str("mfa_level"), + Token::NewtypeStruct { name: "MfaLevel" }, Token::U8(1), Token::Str("name"), Token::Str("the name"), Token::Str("nsfw_level"), + Token::NewtypeStruct { name: "NSFWLevel" }, Token::U8(0), Token::Str("owner_id"), Token::NewtypeStruct { name: "Id" }, @@ -1036,6 +1047,9 @@ mod tests { Token::Some, Token::U64(3), Token::Str("premium_tier"), + Token::NewtypeStruct { + name: "PremiumTier", + }, Token::U8(1), Token::Str("presences"), Token::Seq { len: Some(0) }, @@ -1067,6 +1081,9 @@ mod tests { Token::Some, Token::Str("twilight"), Token::Str("verification_level"), + Token::NewtypeStruct { + name: "VerificationLevel", + }, Token::U8(2), Token::Str("voice_states"), Token::Seq { len: Some(0) }, diff --git a/twilight-model/src/guild/nsfw_level.rs b/twilight-model/src/guild/nsfw_level.rs index 9406ab7eed1..2eaf31eef32 100644 --- a/twilight-model/src/guild/nsfw_level.rs +++ b/twilight-model/src/guild/nsfw_level.rs @@ -1,51 +1,54 @@ use serde::{Deserialize, Serialize}; -#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)] -#[non_exhaustive] -#[serde(from = "u8", into = "u8")] -pub enum NSFWLevel { - Default, - Explicit, - Safe, - AgeRestricted, - Unknown(u8), -} +#[derive(Clone, Copy, Deserialize, Eq, Hash, PartialEq, Serialize)] +pub struct NSFWLevel(u8); -impl From for NSFWLevel { - fn from(value: u8) -> Self { - match value { - 0 => NSFWLevel::Default, - 1 => NSFWLevel::Explicit, - 2 => NSFWLevel::Safe, - 3 => NSFWLevel::AgeRestricted, - unknown => NSFWLevel::Unknown(unknown), - } - } -} +impl NSFWLevel { + pub const DEFAULT: Self = Self::new(0); -impl From for u8 { - fn from(value: NSFWLevel) -> Self { - match value { - NSFWLevel::Default => 0, - NSFWLevel::Explicit => 1, - NSFWLevel::Safe => 2, - NSFWLevel::AgeRestricted => 3, - NSFWLevel::Unknown(unknown) => unknown, - } + pub const EXPLICIT: Self = Self::new(1); + + pub const SAFE: Self = Self::new(2); + + pub const AGE_RESTRICTED: Self = Self::new(3); + + /// Name of the associated constant. + /// + /// Returns `None` if the value doesn't have a defined constant. + pub const fn name(self) -> Option<&'static str> { + Some(match self { + Self::AGE_RESTRICTED => "AGE_RESTRICTED", + Self::DEFAULT => "DEFAULT", + Self::EXPLICIT => "EXPLICIT", + Self::SAFE => "SAFE", + _ => return None, + }) } } +impl_typed!(NSFWLevel, u8); + #[cfg(test)] mod tests { use super::NSFWLevel; use serde_test::Token; + const MAP: &[(NSFWLevel, u8)] = &[ + (NSFWLevel::DEFAULT, 0), + (NSFWLevel::EXPLICIT, 1), + (NSFWLevel::SAFE, 2), + (NSFWLevel::AGE_RESTRICTED, 3), + ]; + #[test] fn variants() { - serde_test::assert_tokens(&NSFWLevel::Default, &[Token::U8(0)]); - serde_test::assert_tokens(&NSFWLevel::Explicit, &[Token::U8(1)]); - serde_test::assert_tokens(&NSFWLevel::Safe, &[Token::U8(2)]); - serde_test::assert_tokens(&NSFWLevel::AgeRestricted, &[Token::U8(3)]); - serde_test::assert_tokens(&NSFWLevel::Unknown(99), &[Token::U8(99)]); + for (kind, num) in MAP { + serde_test::assert_tokens( + kind, + &[Token::NewtypeStruct { name: "NSFWLevel" }, Token::U8(*num)], + ); + assert_eq!(*kind, NSFWLevel::from(*num)); + assert_eq!(*num, kind.get()); + } } } diff --git a/twilight-model/src/guild/partial_guild.rs b/twilight-model/src/guild/partial_guild.rs index 1d5124a1d6d..b7c80fd343a 100644 --- a/twilight-model/src/guild/partial_guild.rs +++ b/twilight-model/src/guild/partial_guild.rs @@ -83,34 +83,34 @@ mod tests { afk_timeout: AfkTimeout::FIFTEEN_MINUTES, application_id: Some(Id::new(3)), banner: Some(image_hash::BANNER), - default_message_notifications: DefaultMessageNotificationLevel::Mentions, + default_message_notifications: DefaultMessageNotificationLevel::MENTIONS, description: Some("a description".to_owned()), discovery_splash: Some(image_hash::SPLASH), emojis: Vec::new(), - explicit_content_filter: ExplicitContentFilter::MembersWithoutRole, - features: Vec::from([GuildFeature::AnimatedBanner]), + explicit_content_filter: ExplicitContentFilter::MEMBERS_WITHOUT_ROLE, + features: Vec::from([GuildFeature::ANIMATED_BANNER]), icon: Some(image_hash::ICON), id: Id::new(1), max_members: Some(25_000), max_presences: Some(10_000), member_count: Some(12_000), - mfa_level: MfaLevel::Elevated, + mfa_level: MfaLevel::ELEVATED, name: "the name".to_owned(), - nsfw_level: NSFWLevel::Default, + nsfw_level: NSFWLevel::DEFAULT, owner_id: Id::new(5), owner: Some(false), permissions: Some(Permissions::SEND_MESSAGES), preferred_locale: "en-us".to_owned(), premium_progress_bar_enabled: true, premium_subscription_count: Some(3), - premium_tier: PremiumTier::Tier1, + premium_tier: PremiumTier::TIER_1, public_updates_channel_id: None, roles: Vec::new(), rules_channel_id: Some(Id::new(6)), splash: Some(image_hash::SPLASH), system_channel_flags: SystemChannelFlags::SUPPRESS_PREMIUM_SUBSCRIPTIONS, system_channel_id: Some(Id::new(7)), - verification_level: VerificationLevel::Medium, + verification_level: VerificationLevel::MEDIUM, vanity_url_code: Some("twilight".to_owned()), widget_channel_id: Some(Id::new(8)), widget_enabled: Some(true), @@ -138,6 +138,9 @@ mod tests { Token::Some, Token::Str(image_hash::BANNER_INPUT), Token::Str("default_message_notifications"), + Token::NewtypeStruct { + name: "DefaultMessageNotificationLevel", + }, Token::U8(1), Token::Str("description"), Token::Some, @@ -149,9 +152,15 @@ mod tests { Token::Seq { len: Some(0) }, Token::SeqEnd, Token::Str("explicit_content_filter"), + Token::NewtypeStruct { + name: "ExplicitContentFilter", + }, Token::U8(1), Token::Str("features"), Token::Seq { len: Some(1) }, + Token::NewtypeStruct { + name: "GuildFeature", + }, Token::Str("ANIMATED_BANNER"), Token::SeqEnd, Token::Str("icon"), @@ -170,10 +179,12 @@ mod tests { Token::Some, Token::U64(12_000), Token::Str("mfa_level"), + Token::NewtypeStruct { name: "MfaLevel" }, Token::U8(1), Token::Str("name"), Token::Str("the name"), Token::Str("nsfw_level"), + Token::NewtypeStruct { name: "NSFWLevel" }, Token::U8(0), Token::Str("owner_id"), Token::NewtypeStruct { name: "Id" }, @@ -192,6 +203,9 @@ mod tests { Token::Some, Token::U64(3), Token::Str("premium_tier"), + Token::NewtypeStruct { + name: "PremiumTier", + }, Token::U8(1), Token::Str("public_updates_channel_id"), Token::None, @@ -212,6 +226,9 @@ mod tests { Token::NewtypeStruct { name: "Id" }, Token::Str("7"), Token::Str("verification_level"), + Token::NewtypeStruct { + name: "VerificationLevel", + }, Token::U8(2), Token::Str("vanity_url_code"), Token::Some, diff --git a/twilight-model/src/guild/premium_tier.rs b/twilight-model/src/guild/premium_tier.rs index 530aeba17f8..fca35a855b2 100644 --- a/twilight-model/src/guild/premium_tier.rs +++ b/twilight-model/src/guild/premium_tier.rs @@ -1,57 +1,65 @@ use serde::{Deserialize, Serialize}; -#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)] -#[non_exhaustive] -#[serde(from = "u8", into = "u8")] -pub enum PremiumTier { - None, - Tier1, - Tier2, - Tier3, - Other(u8), -} +#[derive(Clone, Copy, Deserialize, Eq, Hash, PartialEq, Serialize)] +pub struct PremiumTier(u8); -impl From for PremiumTier { - fn from(value: u8) -> Self { - match value { - 0 => PremiumTier::None, - 1 => PremiumTier::Tier1, - 2 => PremiumTier::Tier2, - 3 => PremiumTier::Tier3, - other => PremiumTier::Other(other), - } - } -} +impl PremiumTier { + pub const NONE: Self = Self::new(0); -impl From for u8 { - fn from(value: PremiumTier) -> Self { - match value { - PremiumTier::None => 0, - PremiumTier::Tier1 => 1, - PremiumTier::Tier2 => 2, - PremiumTier::Tier3 => 3, - PremiumTier::Other(other) => other, - } + pub const TIER_1: Self = Self::new(1); + + pub const TIER_2: Self = Self::new(2); + + pub const TIER_3: Self = Self::new(3); + + /// Name of the associated constant. + /// + /// Returns `None` if the value doesn't have a defined constant. + pub const fn name(self) -> Option<&'static str> { + Some(match self { + Self::NONE => "NONE", + Self::TIER_1 => "TIER_1", + Self::TIER_2 => "TIER_2", + Self::TIER_3 => "TIER_3", + _ => return None, + }) } } impl Default for PremiumTier { fn default() -> Self { - Self::None + Self::NONE } } +impl_typed!(PremiumTier, u8); + #[cfg(test)] mod tests { use super::PremiumTier; use serde_test::Token; + const MAP: &[(PremiumTier, u8)] = &[ + (PremiumTier::NONE, 0), + (PremiumTier::TIER_1, 1), + (PremiumTier::TIER_2, 2), + (PremiumTier::TIER_3, 3), + ]; + #[test] fn variants() { - serde_test::assert_tokens(&PremiumTier::None, &[Token::U8(0)]); - serde_test::assert_tokens(&PremiumTier::Tier1, &[Token::U8(1)]); - serde_test::assert_tokens(&PremiumTier::Tier2, &[Token::U8(2)]); - serde_test::assert_tokens(&PremiumTier::Tier3, &[Token::U8(3)]); - serde_test::assert_tokens(&PremiumTier::Other(99), &[Token::U8(99)]); + for (kind, num) in MAP { + serde_test::assert_tokens( + kind, + &[ + Token::NewtypeStruct { + name: "PremiumTier", + }, + Token::U8(*num), + ], + ); + assert_eq!(*kind, PremiumTier::from(*num)); + assert_eq!(*num, kind.get()); + } } } diff --git a/twilight-model/src/guild/scheduled_event/mod.rs b/twilight-model/src/guild/scheduled_event/mod.rs index 8112825c84a..19cbbe41846 100644 --- a/twilight-model/src/guild/scheduled_event/mod.rs +++ b/twilight-model/src/guild/scheduled_event/mod.rs @@ -28,8 +28,8 @@ use serde::{Deserialize, Serialize}; pub struct GuildScheduledEvent { /// ID of the stage or voice channel if there is one. /// - /// Present on events of [`EntityType::StageInstance`] and - /// [`EntityType::Voice`]. + /// Present on events of [`EntityType::STAGE_INSTANCE`] and + /// [`EntityType::VOICE`]. #[serde(skip_serializing_if = "Option::is_none")] pub channel_id: Option>, /// User object of the event's creator. @@ -50,7 +50,7 @@ pub struct GuildScheduledEvent { pub entity_id: Option>, /// Metadata of an entity, if it exists. /// - /// Currently, only present on events of [`EntityType::External`]. + /// Currently, only present on events of [`EntityType::EXTERNAL`]. #[serde(skip_serializing_if = "Option::is_none")] pub entity_metadata: Option, /// Type of entity associated with the event. @@ -68,7 +68,7 @@ pub struct GuildScheduledEvent { pub privacy_level: PrivacyLevel, /// Scheduled end time of the event. /// - /// Required on events of type [`EntityType::External`]. It also may be + /// Required on events of type [`EntityType::EXTERNAL`]. It also may be /// present in other event types. #[serde(skip_serializing_if = "Option::is_none")] pub scheduled_end_time: Option, @@ -84,120 +84,96 @@ pub struct GuildScheduledEvent { /// Metadata associated with an event. #[derive(Clone, Debug, Default, Deserialize, Eq, Hash, PartialEq, Serialize)] pub struct EntityMetadata { - /// Physical location of an event with type [`EntityType::External`]. + /// Physical location of an event with type [`EntityType::EXTERNAL`]. pub location: Option, } /// Type of event. -#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)] -#[non_exhaustive] -#[serde(from = "u8", into = "u8")] -pub enum EntityType { +#[derive(Clone, Copy, Deserialize, Eq, Hash, PartialEq, Serialize)] +pub struct EntityType(u8); + +impl EntityType { /// Event takes place in a stage instance. - StageInstance, + pub const STAGE_INSTANCE: Self = Self::new(1); + /// Event takes place in a voice channel. - Voice, + pub const VOICE: Self = Self::new(2); + /// Event takes place outside of Discord. - External, - /// Variant value is unknown to the library. - Unknown(u8), -} + pub const EXTERNAL: Self = Self::new(3); -impl From for EntityType { - fn from(value: u8) -> Self { - match value { - 1 => EntityType::StageInstance, - 2 => EntityType::Voice, - 3 => EntityType::External, - unknown => EntityType::Unknown(unknown), - } + /// Name of the associated constant. + /// + /// Returns `None` if the value doesn't have a defined constant. + pub const fn name(self) -> Option<&'static str> { + Some(match self { + Self::EXTERNAL => "EXTERNAL", + Self::STAGE_INSTANCE => "STAGE_INSTANCE", + Self::VOICE => "VOICE", + _ => return None, + }) } } -impl From for u8 { - fn from(value: EntityType) -> Self { - match value { - EntityType::StageInstance => 1, - EntityType::Voice => 2, - EntityType::External => 3, - EntityType::Unknown(unknown) => unknown, - } - } -} +impl_typed!(EntityType, u8); /// Privacy level of an event. -#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)] -#[non_exhaustive] -#[serde(from = "u8", into = "u8")] -pub enum PrivacyLevel { +#[derive(Clone, Copy, Deserialize, Eq, Hash, PartialEq, Serialize)] +pub struct PrivacyLevel(u8); + +impl PrivacyLevel { /// Event is only accessible to guild members. - GuildOnly, - /// Variant value is unknown to the library. - Unknown(u8), -} + pub const GUILD_ONLY: Self = Self::new(2); -impl From for PrivacyLevel { - fn from(value: u8) -> Self { - match value { - 2 => PrivacyLevel::GuildOnly, - unknown => PrivacyLevel::Unknown(unknown), - } + /// Name of the associated constant. + /// + /// Returns `None` if the value doesn't have a defined constant. + pub const fn name(self) -> Option<&'static str> { + Some(match self { + Self::GUILD_ONLY => "GUILD_ONLY", + _ => return None, + }) } } -impl From for u8 { - fn from(value: PrivacyLevel) -> Self { - match value { - PrivacyLevel::GuildOnly => 2, - PrivacyLevel::Unknown(unknown) => unknown, - } - } -} +impl_typed!(PrivacyLevel, u8); /// Status of an event. -#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)] -#[non_exhaustive] -#[serde(from = "u8", into = "u8")] -pub enum Status { +#[derive(Clone, Copy, Deserialize, Eq, Hash, PartialEq, Serialize)] +pub struct Status(u8); + +impl Status { /// Event is scheduled. /// /// With this status, the event can either be made active or cancelled. - Scheduled, + pub const SCHEDULED: Self = Self::new(1); + /// Event is active. /// /// With this status, the event can only be made complete. - Active, + pub const ACTIVE: Self = Self::new(2); + /// Event is complete. - Completed, + pub const COMPLETED: Self = Self::new(3); + /// Event is cancelled. - Cancelled, - /// Variant value is unknown to the library. - Unknown(u8), -} + pub const CANCELLED: Self = Self::new(4); -impl From for Status { - fn from(value: u8) -> Self { - match value { - 1 => Status::Scheduled, - 2 => Status::Active, - 3 => Status::Completed, - 4 => Status::Cancelled, - unknown => Status::Unknown(unknown), - } + /// Name of the associated constant. + /// + /// Returns `None` if the value doesn't have a defined constant. + pub const fn name(self) -> Option<&'static str> { + Some(match self { + Self::ACTIVE => "ACTIVE", + Self::SCHEDULED => "SCHEDULED", + Self::COMPLETED => "COMPLETED", + Self::CANCELLED => "CANCELLED", + _ => return None, + }) } } -impl From for u8 { - fn from(value: Status) -> Self { - match value { - Status::Scheduled => 1, - Status::Active => 2, - Status::Completed => 3, - Status::Cancelled => 4, - Status::Unknown(unknown) => unknown, - } - } -} +impl_typed!(Status, u8); #[cfg(test)] mod tests { @@ -217,15 +193,15 @@ mod tests { description: Some("this is a dance party for garfield lovers".into()), entity_id: Some(Id::new(2)), entity_metadata: None, - entity_type: EntityType::StageInstance, + entity_type: EntityType::STAGE_INSTANCE, guild_id: Id::new(3), id: Id::new(4), image: Some(COVER), name: "garfield dance party".into(), - privacy_level: PrivacyLevel::GuildOnly, + privacy_level: PrivacyLevel::GUILD_ONLY, scheduled_end_time: None, scheduled_start_time, - status: Status::Completed, + status: Status::COMPLETED, user_count: Some(1), }; @@ -248,6 +224,7 @@ mod tests { Token::NewtypeStruct { name: "Id" }, Token::Str("2"), Token::Str("entity_type"), + Token::NewtypeStruct { name: "EntityType" }, Token::U8(1), Token::Str("guild_id"), Token::NewtypeStruct { name: "Id" }, @@ -261,10 +238,14 @@ mod tests { Token::Str("name"), Token::Str("garfield dance party"), Token::Str("privacy_level"), + Token::NewtypeStruct { + name: "PrivacyLevel", + }, Token::U8(2), Token::Str("scheduled_start_time"), Token::Str("2022-01-01T00:00:00.000000+00:00"), Token::Str("status"), + Token::NewtypeStruct { name: "Status" }, Token::U8(3), Token::Str("user_count"), Token::Some, diff --git a/twilight-model/src/guild/template/mod.rs b/twilight-model/src/guild/template/mod.rs index d0b2e6bfada..d730fb34021 100644 --- a/twilight-model/src/guild/template/mod.rs +++ b/twilight-model/src/guild/template/mod.rs @@ -218,7 +218,7 @@ mod tests { icon: None, id: Id::new(1), invitable: None, - kind: ChannelType::GuildCategory, + kind: ChannelType::GUILD_CATEGORY, last_message_id: None, last_pin_timestamp: None, member: None, @@ -254,7 +254,7 @@ mod tests { icon: None, id: Id::new(2), invitable: None, - kind: ChannelType::GuildText, + kind: ChannelType::GUILD_TEXT, last_message_id: None, last_pin_timestamp: None, member: None, @@ -270,13 +270,13 @@ mod tests { allow: Permissions::from_bits(0).unwrap(), deny: Permissions::from_bits(2048).unwrap(), id: Id::new(1), - kind: PermissionOverwriteType::Role, + kind: PermissionOverwriteType::ROLE, }, PermissionOverwrite { allow: Permissions::from_bits(2048).unwrap(), deny: Permissions::from_bits(0).unwrap(), id: Id::new(2), - kind: PermissionOverwriteType::Role, + kind: PermissionOverwriteType::ROLE, }, ]), position: Some(0), @@ -303,7 +303,7 @@ mod tests { icon: None, id: Id::new(3), invitable: None, - kind: ChannelType::GuildCategory, + kind: ChannelType::GUILD_CATEGORY, last_message_id: None, last_pin_timestamp: None, member: None, @@ -339,7 +339,7 @@ mod tests { icon: None, id: Id::new(4), invitable: None, - kind: ChannelType::GuildVoice, + kind: ChannelType::GUILD_VOICE, last_message_id: None, last_pin_timestamp: None, member: None, @@ -361,9 +361,9 @@ mod tests { video_quality_mode: None, }, ]), - default_message_notifications: DefaultMessageNotificationLevel::All, + default_message_notifications: DefaultMessageNotificationLevel::ALL, description: None, - explicit_content_filter: ExplicitContentFilter::None, + explicit_content_filter: ExplicitContentFilter::NONE, icon_hash: None, name: "server name".into(), preferred_locale: "en-US".into(), @@ -415,7 +415,7 @@ mod tests { ], system_channel_flags: SystemChannelFlags::empty(), system_channel_id: Some(Id::new(2)), - verification_level: VerificationLevel::None, + verification_level: VerificationLevel::NONE, }, source_guild_id: Id::new(200), updated_at, @@ -493,6 +493,9 @@ mod tests { Token::NewtypeStruct { name: "Id" }, Token::Str("1"), Token::Str("type"), + Token::NewtypeStruct { + name: "ChannelType", + }, Token::U8(4), Token::Str("name"), Token::Some, @@ -516,6 +519,9 @@ mod tests { Token::NewtypeStruct { name: "Id" }, Token::Str("2"), Token::Str("type"), + Token::NewtypeStruct { + name: "ChannelType", + }, Token::U8(0), Token::Str("name"), Token::Some, @@ -542,6 +548,9 @@ mod tests { Token::NewtypeStruct { name: "Id" }, Token::Str("1"), Token::Str("type"), + Token::NewtypeStruct { + name: "PermissionOverwriteType", + }, Token::U8(0), Token::StructEnd, Token::Struct { @@ -556,6 +565,9 @@ mod tests { Token::NewtypeStruct { name: "Id" }, Token::Str("2"), Token::Str("type"), + Token::NewtypeStruct { + name: "PermissionOverwriteType", + }, Token::U8(0), Token::StructEnd, Token::SeqEnd, @@ -574,6 +586,9 @@ mod tests { Token::NewtypeStruct { name: "Id" }, Token::Str("3"), Token::Str("type"), + Token::NewtypeStruct { + name: "ChannelType", + }, Token::U8(4), Token::Str("name"), Token::Some, @@ -600,6 +615,9 @@ mod tests { Token::NewtypeStruct { name: "Id" }, Token::Str("4"), Token::Str("type"), + Token::NewtypeStruct { + name: "ChannelType", + }, Token::U8(2), Token::Str("name"), Token::Some, @@ -624,10 +642,16 @@ mod tests { Token::StructEnd, Token::SeqEnd, Token::Str("default_message_notifications"), + Token::NewtypeStruct { + name: "DefaultMessageNotificationLevel", + }, Token::U8(0), Token::Str("description"), Token::None, Token::Str("explicit_content_filter"), + Token::NewtypeStruct { + name: "ExplicitContentFilter", + }, Token::U8(0), Token::Str("icon_hash"), Token::None, @@ -681,6 +705,9 @@ mod tests { Token::NewtypeStruct { name: "Id" }, Token::Str("2"), Token::Str("verification_level"), + Token::NewtypeStruct { + name: "VerificationLevel", + }, Token::U8(0), Token::StructEnd, Token::Str("source_guild_id"), diff --git a/twilight-model/src/guild/verification_level.rs b/twilight-model/src/guild/verification_level.rs index cb343eb07dc..b17da4622e3 100644 --- a/twilight-model/src/guild/verification_level.rs +++ b/twilight-model/src/guild/verification_level.rs @@ -1,56 +1,63 @@ use serde::{Deserialize, Serialize}; -#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, Ord, PartialEq, PartialOrd, Serialize)] -#[non_exhaustive] -#[serde(from = "u8", into = "u8")] -pub enum VerificationLevel { - None, - Low, - Medium, - High, - VeryHigh, - /// Variant value is unknown to the library. - Unknown(u8), -} +#[derive(Clone, Copy, Deserialize, Eq, Hash, Ord, PartialEq, PartialOrd, Serialize)] +pub struct VerificationLevel(u8); -impl From for VerificationLevel { - fn from(value: u8) -> Self { - match value { - 0 => VerificationLevel::None, - 1 => VerificationLevel::Low, - 2 => VerificationLevel::Medium, - 3 => VerificationLevel::High, - 4 => VerificationLevel::VeryHigh, - unknown => VerificationLevel::Unknown(unknown), - } - } -} +impl VerificationLevel { + pub const NONE: Self = Self::new(0); -impl From for u8 { - fn from(value: VerificationLevel) -> Self { - match value { - VerificationLevel::None => 0, - VerificationLevel::Low => 1, - VerificationLevel::Medium => 2, - VerificationLevel::High => 3, - VerificationLevel::VeryHigh => 4, - VerificationLevel::Unknown(unknown) => unknown, - } + pub const LOW: Self = Self::new(1); + + pub const MEDIUM: Self = Self::new(2); + + pub const HIGH: Self = Self::new(3); + + pub const VERY_HIGH: Self = Self::new(4); + + /// Name of the associated constant. + /// + /// Returns `None` if the value doesn't have a defined constant. + pub const fn name(self) -> Option<&'static str> { + Some(match self { + Self::NONE => "NONE", + Self::LOW => "LOW", + Self::MEDIUM => "MEDIUM", + Self::HIGH => "HIGH", + Self::VERY_HIGH => "VERY_HIGH", + _ => return None, + }) } } +impl_typed!(VerificationLevel, u8); + #[cfg(test)] mod tests { use super::VerificationLevel; use serde_test::Token; + const MAP: &[(VerificationLevel, u8)] = &[ + (VerificationLevel::NONE, 0), + (VerificationLevel::LOW, 1), + (VerificationLevel::MEDIUM, 2), + (VerificationLevel::HIGH, 3), + (VerificationLevel::VERY_HIGH, 4), + ]; + #[test] fn variants() { - serde_test::assert_tokens(&VerificationLevel::None, &[Token::U8(0)]); - serde_test::assert_tokens(&VerificationLevel::Low, &[Token::U8(1)]); - serde_test::assert_tokens(&VerificationLevel::Medium, &[Token::U8(2)]); - serde_test::assert_tokens(&VerificationLevel::High, &[Token::U8(3)]); - serde_test::assert_tokens(&VerificationLevel::VeryHigh, &[Token::U8(4)]); - serde_test::assert_tokens(&VerificationLevel::Unknown(99), &[Token::U8(99)]); + for (kind, num) in MAP { + serde_test::assert_tokens( + kind, + &[ + Token::NewtypeStruct { + name: "VerificationLevel", + }, + Token::U8(*num), + ], + ); + assert_eq!(*kind, VerificationLevel::from(*num)); + assert_eq!(*num, kind.get()); + } } } diff --git a/twilight-model/src/guild/widget/mod.rs b/twilight-model/src/guild/widget/mod.rs index b2ec1de5c6e..7bf00bb12f1 100644 --- a/twilight-model/src/guild/widget/mod.rs +++ b/twilight-model/src/guild/widget/mod.rs @@ -128,7 +128,7 @@ mod tests { discriminator: 1, id: AnonymizableId::Anonymized, name: "Foo".to_string(), - status: Status::Online, + status: Status::ONLINE, }]), name: "Twilight".to_string(), presence_count: 15, @@ -183,10 +183,8 @@ mod tests { Token::Str("username"), Token::Str("Foo"), Token::Str("status"), - Token::UnitVariant { - name: "Status", - variant: "online", - }, + Token::NewtypeStruct { name: "Status" }, + Token::Str("online"), Token::StructEnd, Token::SeqEnd, Token::Str("name"), diff --git a/twilight-model/src/http/interaction.rs b/twilight-model/src/http/interaction.rs index ce05c35bbc9..fc10cc246a8 100644 --- a/twilight-model/src/http/interaction.rs +++ b/twilight-model/src/http/interaction.rs @@ -6,7 +6,6 @@ use crate::{ channel::message::{AllowedMentions, Component, Embed, MessageFlags}, }; use serde::{Deserialize, Serialize}; -use serde_repr::{Deserialize_repr, Serialize_repr}; /// Interaction response sent to Discord. /// @@ -21,15 +20,15 @@ pub struct InteractionResponse { /// Data of the response. /// /// This is required if the type is any of the following: - /// - [`ChannelMessageWithSource`] - /// - [`UpdateMessage`] - /// - [`Modal`] - /// - [`ApplicationCommandAutocompleteResult`] + /// - [`CHANNEL_MESSAGE_WITH_SOURCE`] + /// - [`UPDATE_MESSAGE`] + /// - [`MODAL`] + /// - [`APPLICATION_COMMAND_AUTOCOMPLETE_RESULT`] /// - /// [`ApplicationCommandAutocompleteResult`]: InteractionResponseType::ApplicationCommandAutocompleteResult - /// [`ChannelMessageWithSource`]: InteractionResponseType::ChannelMessageWithSource - /// [`Modal`]: InteractionResponseType::Modal - /// [`UpdateMessage`]: InteractionResponseType::UpdateMessage + /// [`APPLICATION_COMMAND_AUTOCOMPLETE_RESULT`]: InteractionResponseType::APPLICATION_COMMAND_AUTOCOMPLETE_RESULT + /// [`CHANNEL_MESSAGE_WITH_SOURCE`]: InteractionResponseType::CHANNEL_MESSAGE_WITH_SOURCE + /// [`MODAL`]: InteractionResponseType::MODAL + /// [`UPDATE_MESSAGE`]: InteractionResponseType::UPDATE_MESSAGE #[serde(skip_serializing_if = "Option::is_none")] pub data: Option, } @@ -46,7 +45,7 @@ pub struct InteractionResponseData { /// List of autocomplete alternatives. /// /// Can only be used with - /// [`InteractionResponseType::ApplicationCommandAutocompleteResult`]. + /// [`InteractionResponseType::APPLICATION_COMMAND_AUTOCOMPLETE_RESULT`]. #[serde(skip_serializing_if = "Option::is_none")] pub choices: Option>, /// List of components on the response. @@ -55,7 +54,7 @@ pub struct InteractionResponseData { /// Content of the response. #[serde(skip_serializing_if = "Option::is_none")] pub content: Option, - /// For [`InteractionResponseType::Modal`], user defined identifier. + /// For [`InteractionResponseType::MODAL`], user defined identifier. #[serde(skip_serializing_if = "Option::is_none")] pub custom_id: Option, /// Embeds of the response. @@ -67,7 +66,7 @@ pub struct InteractionResponseData { /// [`MessageFlags::EPHEMERAL`]. #[serde(skip_serializing_if = "Option::is_none")] pub flags: Option, - /// For [`InteractionResponseType::Modal`], title of the modal. + /// For [`InteractionResponseType::MODAL`], title of the modal. #[serde(skip_serializing_if = "Option::is_none")] pub title: Option, /// Whether the response is TTS. @@ -76,32 +75,58 @@ pub struct InteractionResponseData { } /// Type of interaction response. -#[derive(Clone, Copy, Debug, Deserialize_repr, Eq, Hash, PartialEq, Serialize_repr)] -#[non_exhaustive] -#[repr(u8)] -pub enum InteractionResponseType { +#[derive(Clone, Copy, Deserialize, Eq, Hash, PartialEq, Serialize)] +pub struct InteractionResponseType(u8); + +impl InteractionResponseType { /// Used when responding to a Ping from Discord. - Pong = 1, + pub const PONG: Self = Self::new(1); + /// Responds to an interaction with a message. - ChannelMessageWithSource = 4, + pub const CHANNEL_MESSAGE_WITH_SOURCE: Self = Self::new(4); + /// Acknowledges an interaction, showing a loading state, and allowing for /// the message to be edited later. - DeferredChannelMessageWithSource = 5, + pub const DEFERRED_CHANNEL_MESSAGE_WITH_SOURCE: Self = Self::new(5); + /// Acknowledges a component interaction, allowing for the message to be /// edited later. /// /// This is only valid for components and modal submits. - DeferredUpdateMessage = 6, + pub const DEFERRED_UPDATE_MESSAGE: Self = Self::new(6); + /// Acknowledges a component interaction and edits the message. /// /// This is only valid for components and modal submits. - UpdateMessage = 7, + pub const UPDATE_MESSAGE: Self = Self::new(7); + /// Respond to an autocomplete interaction with suggested choices. - ApplicationCommandAutocompleteResult = 8, + pub const APPLICATION_COMMAND_AUTOCOMPLETE_RESULT: Self = Self::new(8); + /// Respond to an interaction with a popup modal. - Modal = 9, + pub const MODAL: Self = Self::new(9); + + /// Name of the associated constant. + /// + /// Returns `None` if the value doesn't have a defined constant. + pub const fn name(self) -> Option<&'static str> { + Some(match self { + Self::APPLICATION_COMMAND_AUTOCOMPLETE_RESULT => { + "Self::APPLICATION_COMMAND_AUTOCOMPLETE_RESULT" + } + Self::CHANNEL_MESSAGE_WITH_SOURCE => "CHANNEL_MESSAGE_WITH_SOURCE", + Self::DEFERRED_CHANNEL_MESSAGE_WITH_SOURCE => "DEFERRED_CHANNEL_MESSAGE_WITH_SOURCE", + Self::DEFERRED_UPDATE_MESSAGE => "DEFERRED_UPDATE_MESSAGE", + Self::MODAL => "MODAL", + Self::PONG => "PONG", + Self::UPDATE_MESSAGE => "UPDATE_MESSAGE", + _ => return None, + }) + } } +impl_typed!(InteractionResponseType, u8); + #[cfg(test)] mod tests { use crate::{ @@ -138,7 +163,7 @@ mod tests { #[test] fn interaction_response() { let value = InteractionResponse { - kind: InteractionResponseType::ChannelMessageWithSource, + kind: InteractionResponseType::CHANNEL_MESSAGE_WITH_SOURCE, data: Some(InteractionResponseData { allowed_mentions: None, attachments: None, @@ -161,6 +186,9 @@ mod tests { len: 2, }, Token::Str("type"), + Token::NewtypeStruct { + name: "InteractionResponseType", + }, Token::U8(4), Token::Str("data"), Token::Some, @@ -183,7 +211,7 @@ mod tests { #[test] fn interaction_response_with_attachments() { let value = InteractionResponse { - kind: InteractionResponseType::ChannelMessageWithSource, + kind: InteractionResponseType::CHANNEL_MESSAGE_WITH_SOURCE, data: Some(InteractionResponseData { attachments: Some(Vec::from([Attachment { description: None, @@ -203,7 +231,10 @@ mod tests { len: 2, }, Token::Str("type"), - Token::U8(InteractionResponseType::ChannelMessageWithSource as u8), + Token::NewtypeStruct { + name: "InteractionResponseType", + }, + Token::U8(InteractionResponseType::CHANNEL_MESSAGE_WITH_SOURCE.get()), Token::Str("data"), Token::Some, Token::Struct { diff --git a/twilight-model/src/http/permission_overwrite.rs b/twilight-model/src/http/permission_overwrite.rs index ea46908bf5e..93402104423 100644 --- a/twilight-model/src/http/permission_overwrite.rs +++ b/twilight-model/src/http/permission_overwrite.rs @@ -5,7 +5,6 @@ use crate::{ id::{marker::GenericMarker, Id}, }; use serde::{Deserialize, Serialize}; -use serde_repr::{Deserialize_repr, Serialize_repr}; /// Permission overwrite data for a role or member. #[derive(Clone, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)] @@ -20,24 +19,37 @@ pub struct PermissionOverwrite { } /// Type of a permission overwrite target. -#[derive(Clone, Copy, Debug, Deserialize_repr, Eq, Hash, PartialEq, Serialize_repr)] -#[non_exhaustive] -#[repr(u8)] -#[serde(rename_all = "snake_case")] -pub enum PermissionOverwriteType { +#[derive(Clone, Copy, Deserialize, Eq, Hash, PartialEq, Serialize)] +pub struct PermissionOverwriteType(u8); + +impl PermissionOverwriteType { /// Permission overwrite targets an individual member. - Member = 1, + pub const MEMBER: Self = Self::new(1); + /// Permission overwrite targets an individual role. - Role = 0, + pub const ROLE: Self = Self::new(0); + + /// Name of the associated constant. + /// + /// Returns `None` if the value doesn't have a defined constant. + pub const fn name(self) -> Option<&'static str> { + Some(match self { + Self::MEMBER => "MEMBER", + Self::ROLE => "ROLE", + _ => return None, + }) + } } +impl_typed!(PermissionOverwriteType, u8); + #[cfg(test)] mod tests { use super::{PermissionOverwrite, PermissionOverwriteType, Permissions}; use crate::id::Id; use serde::{Deserialize, Serialize}; use serde_test::Token; - use static_assertions::{assert_fields, assert_impl_all, const_assert_eq}; + use static_assertions::{assert_fields, assert_impl_all}; use std::{fmt::Debug, hash::Hash}; assert_fields!(PermissionOverwrite: allow, deny, kind); @@ -62,8 +74,6 @@ mod tests { Send, Sync ); - const_assert_eq!(0, PermissionOverwriteType::Role as u8); - const_assert_eq!(1, PermissionOverwriteType::Member as u8); #[test] fn overwrite() { @@ -71,7 +81,7 @@ mod tests { allow: Some(Permissions::CREATE_INVITE), deny: Some(Permissions::KICK_MEMBERS), id: Id::new(12_345_678), - kind: PermissionOverwriteType::Member, + kind: PermissionOverwriteType::MEMBER, }; serde_test::assert_tokens( @@ -91,7 +101,10 @@ mod tests { Token::NewtypeStruct { name: "Id" }, Token::Str("12345678"), Token::Str("type"), - Token::U8(PermissionOverwriteType::Member as u8), + Token::NewtypeStruct { + name: "PermissionOverwriteType", + }, + Token::U8(PermissionOverwriteType::MEMBER.get()), Token::StructEnd, ], ); @@ -111,7 +124,7 @@ mod tests { allow: Some(Permissions::CREATE_INVITE), deny: Some(Permissions::KICK_MEMBERS), id: Id::new(1), - kind: PermissionOverwriteType::Member, + kind: PermissionOverwriteType::MEMBER, }; let deserialized = serde_json::from_str::(raw).unwrap(); @@ -135,6 +148,9 @@ mod tests { Token::NewtypeStruct { name: "Id" }, Token::Str("1"), Token::Str("type"), + Token::NewtypeStruct { + name: "PermissionOverwriteType", + }, Token::U8(1), Token::StructEnd, ], @@ -142,8 +158,24 @@ mod tests { } #[test] - fn overwrite_type_name() { - serde_test::assert_tokens(&PermissionOverwriteType::Member, &[Token::U8(1)]); - serde_test::assert_tokens(&PermissionOverwriteType::Role, &[Token::U8(0)]); + fn overwrite_type() { + const MAP: &[(PermissionOverwriteType, u8)] = &[ + (PermissionOverwriteType::MEMBER, 1), + (PermissionOverwriteType::ROLE, 0), + ]; + + for (kind, num) in MAP { + serde_test::assert_tokens( + kind, + &[ + Token::NewtypeStruct { + name: "PermissionOverwriteType", + }, + Token::U8(*num), + ], + ); + assert_eq!(*kind, PermissionOverwriteType::from(*num)); + assert_eq!(*num, kind.get()); + } } } diff --git a/twilight-model/src/lib.rs b/twilight-model/src/lib.rs index c1ad496f122..45b12df1bbe 100644 --- a/twilight-model/src/lib.rs +++ b/twilight-model/src/lib.rs @@ -17,6 +17,9 @@ clippy::used_underscore_binding )] +#[macro_use] +pub mod util; + pub mod application; pub mod channel; pub mod gateway; @@ -25,7 +28,6 @@ pub mod http; pub mod id; pub mod oauth; pub mod user; -pub mod util; pub mod voice; mod visitor; diff --git a/twilight-model/src/oauth/current_authorization_information.rs b/twilight-model/src/oauth/current_authorization_information.rs index b9e25aef5a3..a01fe6fe701 100644 --- a/twilight-model/src/oauth/current_authorization_information.rs +++ b/twilight-model/src/oauth/current_authorization_information.rs @@ -1,4 +1,4 @@ -use super::Application; +use super::{Application, Scope}; use crate::{user::User, util::Timestamp}; use serde::{Deserialize, Serialize}; @@ -22,7 +22,7 @@ pub struct CurrentAuthorizationInformation { /// [`application`]: Self::application /// [`user`]: Self::user /// [scopes]: crate::oauth::scope - pub scopes: Vec, + pub scopes: Vec, /// User who has authorized, if the user has authorized with the /// [`IDENTIFY`] scope. /// @@ -35,7 +35,7 @@ pub struct CurrentAuthorizationInformation { mod tests { use crate::{ id::Id, - oauth::{scope, Application}, + oauth::{Application, Scope}, test::image_hash, util::{datetime::TimestampParseError, Timestamp}, }; @@ -93,7 +93,7 @@ mod tests { verify_key: "a".to_owned(), }, expires: Timestamp::parse("2023-01-09T17:19:44.000000+00:00")?, - scopes: Vec::from([scope::APPLICATIONS_COMMANDS_PERMISSIONS_UPDATE.to_owned()]), + scopes: Vec::from([Scope::APPLICATIONS_COMMANDS_PERMISSIONS_UPDATE]), user: None, }; @@ -147,7 +147,8 @@ mod tests { Token::Str("2023-01-09T17:19:44.000000+00:00"), Token::Str("scopes"), Token::Seq { len: Some(1) }, - Token::Str(scope::APPLICATIONS_COMMANDS_PERMISSIONS_UPDATE), + Token::NewtypeStruct { name: "Scope" }, + Token::Str(Scope::APPLICATIONS_COMMANDS_PERMISSIONS_UPDATE.get()), Token::SeqEnd, Token::StructEnd, ], diff --git a/twilight-model/src/oauth/install_params.rs b/twilight-model/src/oauth/install_params.rs index 65c65c8b129..5ce44737b1b 100644 --- a/twilight-model/src/oauth/install_params.rs +++ b/twilight-model/src/oauth/install_params.rs @@ -1,3 +1,4 @@ +use super::Scope; use crate::guild::Permissions; use serde::{Deserialize, Serialize}; @@ -12,14 +13,14 @@ pub struct InstallParams { pub permissions: Permissions, /// List of [scopes] to add the application to the guild with. /// - /// [scopes]: crate::oauth::scope - pub scopes: Vec, + /// [scopes]: crate::oauth::Scope + pub scopes: Vec, } #[cfg(test)] mod tests { use super::InstallParams; - use crate::{guild::Permissions, oauth::scope}; + use crate::{guild::Permissions, oauth::Scope}; use serde::{Deserialize, Serialize}; use serde_test::Token; use static_assertions::assert_impl_all; @@ -41,9 +42,9 @@ mod tests { let value = InstallParams { permissions: Permissions::empty(), scopes: Vec::from([ - scope::APPLICATIONS_COMMANDS.to_owned(), - scope::APPLICATIONS_COMMANDS_PERMISSIONS_UPDATE.to_owned(), - scope::IDENTIFY.to_owned(), + Scope::APPLICATIONS_COMMANDS, + Scope::APPLICATIONS_COMMANDS_PERMISSIONS_UPDATE, + Scope::IDENTIFY, ]), }; @@ -58,9 +59,12 @@ mod tests { Token::String("0"), Token::String("scopes"), Token::Seq { len: Some(3) }, - Token::String(scope::APPLICATIONS_COMMANDS), - Token::String(scope::APPLICATIONS_COMMANDS_PERMISSIONS_UPDATE), - Token::String(scope::IDENTIFY), + Token::NewtypeStruct { name: "Scope" }, + Token::String(Scope::APPLICATIONS_COMMANDS.get()), + Token::NewtypeStruct { name: "Scope" }, + Token::String(Scope::APPLICATIONS_COMMANDS_PERMISSIONS_UPDATE.get()), + Token::NewtypeStruct { name: "Scope" }, + Token::String(Scope::IDENTIFY.get()), Token::SeqEnd, Token::StructEnd, ], diff --git a/twilight-model/src/oauth/mod.rs b/twilight-model/src/oauth/mod.rs index c3715fa7ab2..e4f7d7a8fc8 100644 --- a/twilight-model/src/oauth/mod.rs +++ b/twilight-model/src/oauth/mod.rs @@ -1,4 +1,3 @@ -pub mod scope; pub mod team; pub mod current_application_info { @@ -14,11 +13,12 @@ mod application_flags; mod current_authorization_information; mod install_params; mod partial_application; +mod scope; pub use self::{ application::Application, application_flags::ApplicationFlags, current_authorization_information::CurrentAuthorizationInformation, - install_params::InstallParams, partial_application::PartialApplication, + install_params::InstallParams, partial_application::PartialApplication, scope::Scope, }; #[allow(deprecated)] diff --git a/twilight-model/src/oauth/scope.rs b/twilight-model/src/oauth/scope.rs index 630341ae55c..a2b312aeca7 100644 --- a/twilight-model/src/oauth/scope.rs +++ b/twilight-model/src/oauth/scope.rs @@ -4,125 +4,220 @@ //! //! [Discord Docs/OAuth 2 Scopes]: https://discord.com/developers/docs/topics/oauth2#shared-resources-oauth2-scopes -/// Allows your app to fetch data from a user's "Now Playing/Recently Played" -/// list. -/// -/// Requires approval from Discord. -pub const ACTIVITIES_READ: &str = "activities.read"; +use crate::util::known_string::KnownString; +use serde::{Deserialize, Serialize}; -/// Allows your app to update a user's activity +/// OAuth 2 scope. /// -/// Requires approval from Discord, but is not required for the Game SDK -/// activity manager. -pub const ACTIVITIES_WRITE: &str = "activities.write"; - -/// Allows your app to read build data for a user's applications. -pub const APPLICATIONS_BUILDS_READ: &str = "applications.builds.read"; - -/// Allows your app to upload/update builds for a user's applications. +/// # Examples /// -/// Requires approval from Discord. -pub const APPLICATIONS_BUILDS_UPLOAD: &str = "applications.builds.upload"; - -/// Allows your app to use commands in a guild. -pub const APPLICATIONS_COMMANDS: &str = "applications.commands"; - -/// Allows your app to update its commands using a Bearer token. +/// Match a requested scope and print what's being requested: /// -/// This is a client credentials grant only. -pub const APPLICATIONS_COMMANDS_UPDATE: &str = "applications.commands.update"; - -/// Allows your app to update permissions for its commands in a guild a user has -/// permissions to. -pub const APPLICATIONS_COMMANDS_PERMISSIONS_UPDATE: &str = - "applications.commands.permissions.update"; - -/// Allows your app to read entitlements for a user's applications -pub const APPLICATIONS_ENTITLEMENTS: &str = "applications.entitlements"; - -/// Allows your app to read and update store data (SKUs, store listings, -/// achievements, etc.) for a user's applications. -pub const APPLICATIONS_STORE_UPDATE: &str = "applications.store.update"; - -/// For oauth2 bots, this puts the bot in the user's selected guild by default -pub const BOT: &str = "bot"; - -/// Allows /users/@me/connections to return linked third-party accounts -pub const CONNECTIONS: &str = "connections"; - -/// Allows your app to see information about the user's DMs and group DMs +/// ```no_run +/// use twilight_model::oauth::Scope; /// -/// Requires approval from Discord. -pub const DM_CHANNELS_READ: &str = "dm_channels.read"; - -/// Enables `GET /users/@me` returning an email. -pub const EMAIL: &str = "email"; - -/// Allows your app to join users to a group DM. -pub const GDM_JOIN: &str = "gdm.join"; - -/// Allows `GET /users/@me/guilds` to return basic information about all of a -/// user's guilds. -pub const GUILDS: &str = "guilds"; - -/// Allows `GET /guilds/{guild.id}/members/{user.id}` to be used for joining -/// users to a guild. -pub const GUILDS_JOIN: &str = "guilds.join"; - -/// Allows `GET /users/@me/guilds/{guild.id}/member` to return a user's member -/// information in a guild. -pub const GUILDS_MEMBERS_READ: &str = "guilds.members.read"; - -/// Allows `GET /users/@me`, but without the user's email. -pub const IDENTIFY: &str = "identify"; - -/// For local RPC server API access, this allows you to read messages from all -/// client channels (otherwise restricted to channels/guilds your app creates). -pub const MESSAGES_READ: &str = "messages.read"; - -/// Allows your app to know a user's friends and implicit relationships. -/// -/// Requires approval from Discord. -pub const RELATIONSHIPS_READ: &str = "relationships.read"; - -/// Allows your app to update a user's connection and metadata for the app. -pub const ROLE_CONNECTIONS_WRITE: &str = "role_connections.write"; - -/// For local RPC server access, this allows you to control a user's local -/// Discord client. -/// -/// Requires approval from Discord. -pub const RPC: &str = "rpc"; - -/// For local rpc server access, this allows you to update a user's activity -/// -/// Requires approval from Discord. -pub const RPC_ACTIVITIES_WRITE: &str = "rpc.activities.write"; - -/// For local RPC server access, this allows you to receive notifications pushed -/// out to the user. -/// -/// Requires approval from Discord. -pub const RPC_NOTIFICATIONS_READ: &str = "rpc.notifications.read"; - -/// For local RPC server access, this allows you to read a user's voice settings -/// and listen for voice events. -/// -/// Requires approval from Discord. -pub const RPC_VOICE_READ: &str = "rpc.voice.read"; - -/// For local RPC server access, this allows you to update a user's voice -/// settings. -/// -/// Requires approval from Discord. -pub const RPC_VOICE_WRITE: &str = "rpc.voice.write"; - -/// Allows your app to connect to voice on the user's behalf and see all the -/// voice members. +/// let scope = Scope::IDENTIFY; /// -/// Requires approval from Discord. -pub const VOICE: &str = "voice"; - -/// This generates a webhook that is returned in the oauth token response for -/// authorization code grants. -pub const WEBHOOK_INCOMING: &str = "webhook.incoming"; +/// match scope { +/// Scope::CONNECTIONS => println!("Your list of connections is being requested."), +/// Scope::EMAIL => println!("Your email address is being requested."), +/// Scope::IDENTIFY => println!("Information about your account is being requested."), +/// _ => {} +/// } +/// ```` +#[derive(Clone, Copy, Deserialize, Eq, Hash, PartialEq, Serialize)] +pub struct Scope(KnownString<64>); + +impl Scope { + /// Allows your app to fetch data from a user's + /// "Now Playing/Recently Played" list. + /// + /// Requires approval from Discord. + pub const ACTIVITIES_READ: Self = Self::from_bytes(b"activities.read"); + + /// Allows your app to update a user's activity + /// + /// Requires approval from Discord, but is not required for the Game SDK + /// activity manager. + pub const ACTIVITIES_WRITE: Self = Self::from_bytes(b"activities.write"); + + /// Allows your app to read build data for a user's applications. + pub const APPLICATIONS_BUILDS_READ: Self = Self::from_bytes(b"applications.builds.read"); + + /// Allows your app to upload/update builds for a user's applications. + /// + /// Requires approval from Discord. + pub const APPLICATIONS_BUILDS_UPLOAD: Self = Self::from_bytes(b"applications.builds.upload"); + + /// Allows your app to use commands in a guild. + pub const APPLICATIONS_COMMANDS: Self = Self::from_bytes(b"applications.commands"); + + /// Allows your app to update its commands using a Bearer token. + /// + /// This is a client credentials grant only. + pub const APPLICATIONS_COMMANDS_UPDATE: Self = + Self::from_bytes(b"applications.commands.update"); + + /// Allows your app to update permissions for its commands in a guild a user + /// has permissions to. + pub const APPLICATIONS_COMMANDS_PERMISSIONS_UPDATE: Self = + Self::from_bytes(b"applications.commands.permissions.update"); + + /// Allows your app to read entitlements for a user's applications. + pub const APPLICATIONS_ENTITLEMENTS: Self = Self::from_bytes(b"applications.entitlements"); + + /// Allows your app to read and update store data (SKUs, store listings, + /// achievements, etc.) for a user's applications. + pub const APPLICATIONS_STORE_UPDATE: Self = Self::from_bytes(b"applications.store.update"); + + /// For oauth2 bots, this puts the bot in the user's selected guild by + /// default. + pub const BOT: Self = Self::from_bytes(b"bot"); + + /// Allows /users/@me/connections to return linked third-party accounts. + pub const CONNECTIONS: Self = Self::from_bytes(b"connections"); + + /// Allows your app to see information about the user's DMs and group DMs. + /// + /// Requires approval from Discord. + pub const DM_CHANNELS_READ: Self = Self::from_bytes(b"dm_channels.read"); + + /// Enables `GET /users/@me` returning an email. + pub const EMAIL: Self = Self::from_bytes(b"email"); + + /// Allows your app to join users to a group DM. + pub const GDM_JOIN: Self = Self::from_bytes(b"gdm.join"); + + /// Allows `GET /users/@me/guilds` to return basic information about all of + /// a user's guilds. + pub const GUILDS: Self = Self::from_bytes(b"guilds"); + + /// Allows `GET /guilds/{guild.id}/members/{user.id}` to be used for joining + /// users to a guild. + pub const GUILDS_JOIN: Self = Self::from_bytes(b"guilds.join"); + + /// Allows `GET /users/@me/guilds/{guild.id}/member` to return a user's + /// member information in a guild. + pub const GUILDS_MEMBERS_READ: Self = Self::from_bytes(b"guilds.members.read"); + + /// Allows `GET /users/@me`, but without the user's email. + pub const IDENTIFY: Self = Self::from_bytes(b"identify"); + + /// For local RPC server API access, this allows you to read messages from + /// all client channels (otherwise restricted to channels/guilds your app + /// creates). + pub const MESSAGES_READ: Self = Self::from_bytes(b"messages.read"); + + /// Allows your app to know a user's friends and implicit relationships. + /// + /// Requires approval from Discord. + pub const RELATIONSHIPS_READ: Self = Self::from_bytes(b"relationships.read"); + + /// Allows your app to update a user's connection and metadata for the app. + pub const ROLE_CONNECTIONS_WRITE: Self = Self::from_bytes(b"role_connections.write"); + + /// For local RPC server access, this allows you to control a user's local + /// Discord client. + /// + /// Requires approval from Discord. + pub const RPC: Self = Self::from_bytes(b"rpc"); + + /// For local rpc server access, this allows you to update a user's activity + /// + /// Requires approval from Discord. + pub const RPC_ACTIVITIES_WRITE: Self = Self::from_bytes(b"rpc.activities.write"); + + /// For local RPC server access, this allows you to receive notifications + /// pushed out to the user. + /// + /// Requires approval from Discord. + pub const RPC_NOTIFICATIONS_READ: Self = Self::from_bytes(b"rpc.notifications.read"); + + /// For local RPC server access, this allows you to read a user's voice + /// settings and listen for voice events. + /// + /// Requires approval from Discord. + pub const RPC_VOICE_READ: Self = Self::from_bytes(b"rpc.voice.read"); + + /// For local RPC server access, this allows you to update a user's voice + /// settings. + /// + /// Requires approval from Discord. + pub const RPC_VOICE_WRITE: Self = Self::from_bytes(b"rpc.voice.write"); + + /// Allows your app to connect to voice on the user's behalf and see all the + /// voice members. + /// + /// Requires approval from Discord. + pub const VOICE: Self = Self::from_bytes(b"voice"); + + /// This generates a webhook that is returned in the oauth token response for + /// authorization code grants. + pub const WEBHOOK_INCOMING: Self = Self::from_bytes(b"webhook.incoming"); + + /// Name of the associated constant. + /// + /// Returns `None` if the value doesn't have a defined constant. + pub const fn name(self) -> Option<&'static str> { + Some(match self { + Self::ACTIVITIES_READ => "ACTIVITIES_READ", + Self::ACTIVITIES_WRITE => "ACTIVITIES_WRITE", + Self::APPLICATIONS_BUILDS_READ => "APPLICATIONS_BUILDS_READ", + Self::APPLICATIONS_BUILDS_UPLOAD => "APPLICATIONS_BUILDS_UPLOAD", + Self::APPLICATIONS_COMMANDS => "APPLICATIONS_COMMANDS", + Self::APPLICATIONS_COMMANDS_UPDATE => "APPLICATIONS_COMMANDS_UPDATE", + Self::APPLICATIONS_COMMANDS_PERMISSIONS_UPDATE => { + "APPLICATIONS_COMMANDS_PERMISSIONS_UPDATE" + } + Self::APPLICATIONS_ENTITLEMENTS => "APPLICATIONS_ENTITLEMENTS", + Self::APPLICATIONS_STORE_UPDATE => "APPLICATIONS_STORE_UPDATE", + Self::BOT => "BOT", + Self::CONNECTIONS => "CONNECTIONS", + Self::DM_CHANNELS_READ => "DM_CHANNELS_READ", + Self::EMAIL => "EMAIL", + Self::GDM_JOIN => "GDM_JOIN", + Self::GUILDS => "GUILDS", + Self::GUILDS_JOIN => "GUILDS_JOIN", + Self::GUILDS_MEMBERS_READ => "GUILDS_MEMBERS_READ", + Self::IDENTIFY => "IDENTIFY", + Self::MESSAGES_READ => "MESSAGES_READ", + Self::RELATIONSHIPS_READ => "RELATIONSHIPS_READ", + Self::ROLE_CONNECTIONS_WRITE => "ROLE_CONNECTIONS_WRITE", + Self::RPC => "RPC", + Self::RPC_ACTIVITIES_WRITE => "RPC_ACTIVITIES_WRITE", + Self::RPC_NOTIFICATIONS_READ => "RPC_NOTIFICATIONS_READ", + Self::RPC_VOICE_READ => "RPC_VOICE_READ", + Self::RPC_VOICE_WRITE => "RPC_VOICE_WRITE", + Self::VOICE => "VOICE", + Self::WEBHOOK_INCOMING => "WEBHOOK_INCOMING", + _ => return None, + }) + } +} + +impl_typed!(Scope, String); + +#[cfg(test)] +mod tests { + use super::Scope; + use serde::{Deserialize, Serialize}; + use static_assertions::assert_impl_all; + use std::{fmt::Debug, hash::Hash, str::FromStr, string::ToString}; + + assert_impl_all!( + Scope: AsRef, + Clone, + Copy, + Debug, + Deserialize<'static>, + Eq, + FromStr, + Hash, + PartialEq, + Send, + Serialize, + Sync, + ToString, + TryFrom<&'static str>, + ); +} diff --git a/twilight-model/src/oauth/team/member.rs b/twilight-model/src/oauth/team/member.rs index 115aeb93982..746b07b3c33 100644 --- a/twilight-model/src/oauth/team/member.rs +++ b/twilight-model/src/oauth/team/member.rs @@ -22,7 +22,7 @@ mod tests { #[test] fn team_member() { let value = TeamMember { - membership_state: TeamMembershipState::Accepted, + membership_state: TeamMembershipState::ACCEPTED, permissions: vec!["*".to_owned()], team_id: Id::new(1), user: User { @@ -52,6 +52,9 @@ mod tests { len: 4, }, Token::Str("membership_state"), + Token::NewtypeStruct { + name: "TeamMembershipState", + }, Token::U8(2), Token::Str("permissions"), Token::Seq { len: Some(1) }, diff --git a/twilight-model/src/oauth/team/membership_state.rs b/twilight-model/src/oauth/team/membership_state.rs index 0c92ff9d49d..93e085b1621 100644 --- a/twilight-model/src/oauth/team/membership_state.rs +++ b/twilight-model/src/oauth/team/membership_state.rs @@ -1,44 +1,51 @@ use serde::{Deserialize, Serialize}; -#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)] -#[non_exhaustive] -#[serde(from = "u8", into = "u8")] -pub enum TeamMembershipState { - Invited, - Accepted, - /// Variant value is unknown to the library. - Unknown(u8), -} +#[derive(Clone, Copy, Deserialize, Eq, Hash, PartialEq, Serialize)] +pub struct TeamMembershipState(u8); -impl From for TeamMembershipState { - fn from(value: u8) -> Self { - match value { - 1 => TeamMembershipState::Invited, - 2 => TeamMembershipState::Accepted, - unknown => TeamMembershipState::Unknown(unknown), - } - } -} +impl TeamMembershipState { + pub const INVITED: Self = Self::new(1); -impl From for u8 { - fn from(value: TeamMembershipState) -> Self { - match value { - TeamMembershipState::Invited => 1, - TeamMembershipState::Accepted => 2, - TeamMembershipState::Unknown(unknown) => unknown, - } + pub const ACCEPTED: Self = Self::new(2); + + /// Name of the associated constant. + /// + /// Returns `None` if the value doesn't have a defined constant. + pub const fn name(self) -> Option<&'static str> { + Some(match self { + Self::ACCEPTED => "ACCEPTED", + Self::INVITED => "INVITED", + _ => return None, + }) } } +impl_typed!(TeamMembershipState, u8); + #[cfg(test)] mod tests { use super::TeamMembershipState; use serde_test::Token; + const MAP: &[(TeamMembershipState, u8)] = &[ + (TeamMembershipState::INVITED, 1), + (TeamMembershipState::ACCEPTED, 2), + ]; + #[test] fn variants() { - serde_test::assert_tokens(&TeamMembershipState::Invited, &[Token::U8(1)]); - serde_test::assert_tokens(&TeamMembershipState::Accepted, &[Token::U8(2)]); - serde_test::assert_tokens(&TeamMembershipState::Unknown(99), &[Token::U8(99)]); + for (kind, num) in MAP { + serde_test::assert_tokens( + kind, + &[ + Token::NewtypeStruct { + name: "TeamMembershipState", + }, + Token::U8(*num), + ], + ); + assert_eq!(*kind, TeamMembershipState::from(*num)); + assert_eq!(*num, kind.get()); + } } } diff --git a/twilight-model/src/user/connection.rs b/twilight-model/src/user/connection.rs index 1d0043e2616..0122962e49a 100644 --- a/twilight-model/src/user/connection.rs +++ b/twilight-model/src/user/connection.rs @@ -37,7 +37,7 @@ mod tests { show_activity: true, verified: true, two_way_link: false, - visibility: ConnectionVisibility::Everyone, + visibility: ConnectionVisibility::EVERYONE, }; serde_test::assert_tokens( @@ -68,6 +68,9 @@ mod tests { Token::Str("verified"), Token::Bool(true), Token::Str("visibility"), + Token::NewtypeStruct { + name: "ConnectionVisibility", + }, Token::U8(1), Token::StructEnd, ], diff --git a/twilight-model/src/user/connection_visibility.rs b/twilight-model/src/user/connection_visibility.rs index c72cea574a7..fa33d1f1eef 100644 --- a/twilight-model/src/user/connection_visibility.rs +++ b/twilight-model/src/user/connection_visibility.rs @@ -1,43 +1,53 @@ use serde::{Deserialize, Serialize}; -#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)] -#[non_exhaustive] -#[serde(from = "u8", into = "u8")] -pub enum ConnectionVisibility { - None, - Everyone, - Unknown(u8), -} +#[derive(Clone, Copy, Deserialize, Eq, Hash, PartialEq, Serialize)] +pub struct ConnectionVisibility(u8); -impl From for ConnectionVisibility { - fn from(value: u8) -> Self { - match value { - 0 => ConnectionVisibility::None, - 1 => ConnectionVisibility::Everyone, - unknown => ConnectionVisibility::Unknown(unknown), - } - } -} +impl ConnectionVisibility { + /// Connection isn't visible to anyone. + pub const NONE: Self = Self::new(0); -impl From for u8 { - fn from(value: ConnectionVisibility) -> Self { - match value { - ConnectionVisibility::None => 0, - ConnectionVisibility::Everyone => 1, - ConnectionVisibility::Unknown(unknown) => unknown, - } + /// Connection is visible to everyone. + pub const EVERYONE: Self = Self::new(1); + + /// Name of the associated constant. + /// + /// Returns `None` if the value doesn't have a defined constant. + pub const fn name(self) -> Option<&'static str> { + Some(match self { + Self::EVERYONE => "EVERYONE", + Self::NONE => "NONE", + _ => return None, + }) } } +impl_typed!(ConnectionVisibility, u8); + #[cfg(test)] mod tests { use super::ConnectionVisibility; use serde_test::Token; + const MAP: &[(ConnectionVisibility, u8)] = &[ + (ConnectionVisibility::NONE, 0), + (ConnectionVisibility::EVERYONE, 1), + ]; + #[test] fn variants() { - serde_test::assert_tokens(&ConnectionVisibility::None, &[Token::U8(0)]); - serde_test::assert_tokens(&ConnectionVisibility::Everyone, &[Token::U8(1)]); - serde_test::assert_tokens(&ConnectionVisibility::Unknown(99), &[Token::U8(99)]); + for (kind, num) in MAP { + serde_test::assert_tokens( + kind, + &[ + Token::NewtypeStruct { + name: "ConnectionVisibility", + }, + Token::U8(*num), + ], + ); + assert_eq!(*kind, ConnectionVisibility::from(*num)); + assert_eq!(*num, kind.get()); + } } } diff --git a/twilight-model/src/user/current_user.rs b/twilight-model/src/user/current_user.rs index 62aba5ccf52..1dc151bba75 100644 --- a/twilight-model/src/user/current_user.rs +++ b/twilight-model/src/user/current_user.rs @@ -111,6 +111,9 @@ mod tests { Token::Str("test name"), Token::Str("premium_type"), Token::Some, + Token::NewtypeStruct { + name: "PremiumType", + }, Token::U8(1), Token::Str("public_flags"), Token::Some, @@ -158,6 +161,9 @@ mod tests { Token::Str("test name"), Token::Str("premium_type"), Token::Some, + Token::NewtypeStruct { + name: "PremiumType", + }, Token::U8(1), Token::Str("public_flags"), Token::Some, @@ -182,7 +188,7 @@ mod tests { mfa_enabled: true, name: "test name".to_owned(), verified: Some(true), - premium_type: Some(PremiumType::NitroClassic), + premium_type: Some(PremiumType::NITRO_CLASSIC), public_flags: Some(UserFlags::STAFF), flags: None, locale: Some("test locale".to_owned()), @@ -211,7 +217,7 @@ mod tests { mfa_enabled: true, name: "test name".to_owned(), verified: Some(true), - premium_type: Some(PremiumType::NitroClassic), + premium_type: Some(PremiumType::NITRO_CLASSIC), public_flags: Some(UserFlags::STAFF), flags: Some(UserFlags::STAFF), locale: Some("test locale".to_owned()), diff --git a/twilight-model/src/user/mod.rs b/twilight-model/src/user/mod.rs index c94262e236e..2277835f6e5 100644 --- a/twilight-model/src/user/mod.rs +++ b/twilight-model/src/user/mod.rs @@ -224,6 +224,9 @@ mod tests { Token::Str("test"), Token::Str("premium_type"), Token::Some, + Token::NewtypeStruct { + name: "PremiumType", + }, Token::U8(2), Token::Str("public_flags"), Token::Some, @@ -272,6 +275,9 @@ mod tests { Token::Str("test"), Token::Str("premium_type"), Token::Some, + Token::NewtypeStruct { + name: "PremiumType", + }, Token::U8(2), Token::Str("public_flags"), Token::Some, @@ -309,7 +315,7 @@ mod tests { locale: Some("en-us".to_owned()), mfa_enabled: Some(true), name: "test".to_owned(), - premium_type: Some(PremiumType::Nitro), + premium_type: Some(PremiumType::NITRO), public_flags: Some(UserFlags::PREMIUM_EARLY_SUPPORTER | UserFlags::VERIFIED_DEVELOPER), system: None, verified: Some(true), @@ -339,7 +345,7 @@ mod tests { locale: Some("en-us".to_owned()), mfa_enabled: Some(true), name: "test".to_owned(), - premium_type: Some(PremiumType::Nitro), + premium_type: Some(PremiumType::NITRO), public_flags: Some(UserFlags::PREMIUM_EARLY_SUPPORTER | UserFlags::VERIFIED_DEVELOPER), system: Some(true), verified: Some(true), diff --git a/twilight-model/src/user/premium_type.rs b/twilight-model/src/user/premium_type.rs index 021ad2dd2b3..64f00117484 100644 --- a/twilight-model/src/user/premium_type.rs +++ b/twilight-model/src/user/premium_type.rs @@ -3,45 +3,37 @@ use serde::{Deserialize, Serialize}; /// Type of premium tier for a [`User`]. /// /// [`User`]: super::User -#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)] -#[non_exhaustive] -#[serde(from = "u8", into = "u8")] -pub enum PremiumType { +#[derive(Clone, Copy, Deserialize, Eq, Hash, PartialEq, Serialize)] +pub struct PremiumType(u8); + +impl PremiumType { /// User doesn't have premium. - None, + pub const NONE: Self = Self::new(0); + /// User has Nitro Classic. - NitroClassic, + pub const NITRO_CLASSIC: Self = Self::new(1); + /// User has the standard Nitro. - Nitro, + pub const NITRO: Self = Self::new(2); + /// User has Nitro Basic. - NitroBasic, - /// Variant value is unknown to the library. - Unknown(u8), -} + pub const NITRO_BASIC: Self = Self::new(3); -impl From for PremiumType { - fn from(value: u8) -> Self { - match value { - 0 => Self::None, - 1 => Self::NitroClassic, - 2 => Self::Nitro, - 3 => Self::NitroBasic, - unknown => PremiumType::Unknown(unknown), - } + /// Name of the associated constant. + /// + /// Returns `None` if the value doesn't have a defined constant. + pub const fn name(self) -> Option<&'static str> { + Some(match self { + Self::NITRO_BASIC => "NITRO_BASIC", + Self::NITRO_CLASSIC => "NITRO_CLASSIC", + Self::NITRO => "NITRO", + Self::NONE => "NONE", + _ => return None, + }) } } -impl From for u8 { - fn from(value: PremiumType) -> Self { - match value { - PremiumType::None => 0, - PremiumType::NitroClassic => 1, - PremiumType::Nitro => 2, - PremiumType::NitroBasic => 3, - PremiumType::Unknown(unknown) => unknown, - } - } -} +impl_typed!(PremiumType, u8); #[cfg(test)] mod tests { @@ -64,12 +56,27 @@ mod tests { Sync ); + const MAP: &[(PremiumType, u8)] = &[ + (PremiumType::NONE, 0), + (PremiumType::NITRO_CLASSIC, 1), + (PremiumType::NITRO, 2), + (PremiumType::NITRO_BASIC, 3), + ]; + #[test] fn variants() { - serde_test::assert_tokens(&PremiumType::None, &[Token::U8(0)]); - serde_test::assert_tokens(&PremiumType::NitroClassic, &[Token::U8(1)]); - serde_test::assert_tokens(&PremiumType::Nitro, &[Token::U8(2)]); - serde_test::assert_tokens(&PremiumType::NitroBasic, &[Token::U8(3)]); - serde_test::assert_tokens(&PremiumType::Unknown(42), &[Token::U8(42)]); + for (kind, num) in MAP { + serde_test::assert_tokens( + kind, + &[ + Token::NewtypeStruct { + name: "PremiumType", + }, + Token::U8(*num), + ], + ); + assert_eq!(*kind, PremiumType::from(*num)); + assert_eq!(*num, kind.get()); + } } } diff --git a/twilight-model/src/user/profile.rs b/twilight-model/src/user/profile.rs index 9e362c46fca..2585425d9ee 100644 --- a/twilight-model/src/user/profile.rs +++ b/twilight-model/src/user/profile.rs @@ -94,6 +94,9 @@ mod tests { Token::Str("user name"), Token::Str("premium_type"), Token::Some, + Token::NewtypeStruct { + name: "PremiumType", + }, Token::U8(2), Token::Str("verified"), Token::Some, @@ -116,7 +119,7 @@ mod tests { locale: Some("en-us".to_owned()), mfa_enabled: Some(true), name: "user name".to_owned(), - premium_type: Some(PremiumType::Nitro), + premium_type: Some(PremiumType::NITRO), verified: Some(true), }; diff --git a/twilight-model/src/util/known_string.rs b/twilight-model/src/util/known_string.rs new file mode 100644 index 00000000000..9cd2327a21d --- /dev/null +++ b/twilight-model/src/util/known_string.rs @@ -0,0 +1,349 @@ +//! Bytes container on the stack intended for efficient, constant-time string +//! storage. + +use serde::{ + de::{Deserialize, Deserializer, Error as DeError, Visitor}, + ser::{Serialize, Serializer}, +}; +use std::{ + fmt::{Debug, Formatter, Result as FmtResult}, + str::{self, FromStr}, +}; + +/// Bytes container with some abstractions intended for storing UTF-8 valid +/// data (i.e. Strings). +/// +/// We want to be able to pattern match types with string values, but +/// there's some tricky aspects about storing types with string values that +/// results in our having to store bytes. +/// +/// Say that we have a type like this: +/// +/// ```compile_fail +/// use serde::{Deserialize, Serialize}; +/// +/// #[derive(Clone, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)] +/// pub struct Letter(String); +/// +/// impl Letter { +/// pub const A: Self = Self("A".to_owned()); +/// } +/// ``` +/// +/// This clearly won't compile because String is a heap allocation, but we're +/// needing to work in constant expressions. Similarly, storing &'static str +/// won't work because deserialization from serde contexts won't have static +/// results -- well, they can operate off a known list of values, but then we +/// can't have unknown values, which defeats the purpose of the exercise. +/// +/// This leads us to considering another solution: [`Cow`]. [`Cow`]s can store +/// both borrowed and owned strings. However, this also fails to compile: +/// +/// ```compile_fail +/// use serde::{Deserialize, Serialize}; +/// use std::borrow::Cow; +/// +/// #[derive(Clone, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)] +/// pub struct Letter(Cow<'static, str>); +/// +/// impl Letter { +/// pub const A: Self = Self(Cow::Borrowed("A")); +/// pub const B: Self = Self(Cow::Borrowed("B")); +/// pub const C: Self = Self(Cow::Borrowed("C")); +/// +/// fn print_sound(&self) { +/// println!("{}", match self { +/// &Self::A => "ayy", +/// &Self::B => "bee", +/// &Self::C => "sea", +/// }); +/// } +/// } +/// ``` +/// +/// The reason for this is non-obvious: `Cow` doesn't derive `Eq` and +/// `PartialEq`. It can't because String doesn't in constant expressions. We get +/// this error on each of the constant evaluations: +/// +/// > to use a constant of type `Cow` in a pattern, `Cow` must be annotated with +/// > `#[derive(PartialEq, Eq)]` +/// +/// This brings us to another solution: storing an array of bytes. Because +/// arrays are on the stack and derive `Eq` and `PartialEq`, we *can* pattern +/// match them: +/// +/// ```no_run +/// use serde::{Deserialize, Serialize}; +/// +/// #[derive(Clone, Copy, Deserialize, Eq, Hash, PartialEq, Serialize)] +/// pub struct Letter([u8; 1]); +/// +/// impl Letter { +/// pub const A: Self = Self::new(b"A"); +/// pub const B: Self = Self::new(b"B"); +/// pub const C: Self = Self::new(b"C"); +/// +/// const fn new(input: &[u8]) -> Self { +/// Self([input[0]]) +/// } +/// +/// fn print_sound(self) { +/// println!( +/// "{}", +/// match self { +/// Self::A => "ayy", +/// Self::B => "bee", +/// Self::C => "sea", +/// _ => "unknown", +/// } +/// ); +/// } +/// } +/// ``` +/// +/// As a bonus, we get the efficiency of storing on the stack, low allocation +/// sizes (equal to the requested length), and we get to derive Copy, which +/// means match statements look pleasant. +#[derive(Clone, Copy, Eq, Hash, PartialEq)] +pub struct KnownString { + bytes: [u8; LENGTH], +} + +impl KnownString { + /// Create a known value from a string. + /// + /// Returns `None` if the string is larger than the container size. + pub fn from_str(value: &str) -> Option { + if value.len() > LENGTH { + return None; + } + + let mut bytes = [0; LENGTH]; + + for (index, byte) in value.as_bytes().iter().enumerate() { + bytes[index] = *byte; + } + + Some(Self { bytes }) + } + + /// Create a known value from a slice of bytes. + /// + /// # Panics + /// + /// Panics if the input is larger than the allocated number of bytes. This + /// is okay to do since this is only called by Twilight's associated + /// constants and methods with constrained types, such as + /// [`AutoArchiveDuration::new`]. + /// + /// [`AutoArchiveDuration::new`]: crate::channel::thread::AutoArchiveDuration::new + pub const fn from_bytes(input: &[u8]) -> Self { + assert!( + input.len() <= LENGTH, + "input is greater than allocated length" + ); + + let mut bytes = [0; LENGTH]; + let mut index = 0; + + while index < input.len() { + let byte = input[index]; + + if byte == 0x00 { + break; + } + + bytes[index] = byte; + index += 1; + } + + Self { bytes } + } + + /// Parse the known value as a string, trimming null bytes. + /// + /// # Panics + /// + /// Panics if the value is not UTF-8 valid. + pub fn get(&self) -> &str { + str::from_utf8(&self.bytes) + .expect("string is not utf8 valid") + .trim_matches(char::from(0)) + } +} + +impl AsRef for KnownString { + fn as_ref(&self) -> &str { + self.get() + } +} + +impl Debug for KnownString { + fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult { + f.write_str(self.get()) + } +} + +pub struct KnownStringVisitor; + +impl<'de, const LENGTH: usize> Visitor<'de> for KnownStringVisitor { + type Value = KnownString; + + fn expecting(&self, f: &mut Formatter<'_>) -> FmtResult { + f.write_str("string") + } + + fn visit_str(self, v: &str) -> Result { + KnownString::from_str(v).ok_or_else(|| DeError::custom("string is too long")) + } + + fn visit_string(self, v: String) -> Result { + KnownString::from_str(&v).ok_or_else(|| DeError::custom("string is too long")) + } +} + +impl<'de, const LENGTH: usize> Deserialize<'de> for KnownString { + fn deserialize>(deserializer: D) -> Result { + deserializer.deserialize_any(KnownStringVisitor:: {}) + } +} + +impl FromStr for KnownString { + type Err = (); + + fn from_str(s: &str) -> Result { + KnownString::try_from(s) + } +} + +impl Serialize for KnownString { + fn serialize(&self, serializer: S) -> Result { + serializer.collect_str(self.as_ref()) + } +} + +impl ToString for KnownString { + fn to_string(&self) -> String { + self.get().to_owned() + } +} + +impl TryFrom<&str> for KnownString { + type Error = (); + + fn try_from(value: &str) -> Result { + Self::from_str(value).ok_or(()) + } +} + +#[cfg(test)] +mod tests { + use super::KnownString; + use serde::{Deserialize, Serialize}; + use serde_test::Token; + use static_assertions::{assert_impl_all, const_assert_eq}; + use std::{fmt::Debug, hash::Hash, mem, str::FromStr, string::ToString}; + + assert_impl_all!( + KnownString<1>: AsRef, + Clone, + Debug, + Deserialize<'static>, + Eq, + FromStr, + Hash, + PartialEq, + Send, + Serialize, + Sync, + ToString, + TryFrom<&'static str>, + ); + const_assert_eq!(16, mem::size_of::>()); + + #[test] + fn as_ref() { + let string = KnownString::<3>::from_bytes(b"bot"); + + assert_eq!("bot", string.as_ref()); + } + + #[test] + fn debug() { + let string = KnownString::<3>::from_bytes(b"bot"); + + assert_eq!("bot", format!("{string:?}")); + } + + #[test] + fn from_bytes() { + let string = KnownString::<3>::from_bytes(b"BOT"); + assert_eq!(b'B', string.bytes[0]); + assert_eq!(b'O', string.bytes[1]); + assert_eq!(b'T', string.bytes[2]); + } + + #[test] + #[should_panic] + const fn from_bytes_too_long() { + KnownString::<3>::from_bytes(b"TEST"); + } + + #[test] + fn from_str() { + let strings = [ + KnownString::<64>::from_str("BOT").unwrap(), + FromStr::from_str("BOT").unwrap(), + ]; + + for string in strings { + assert_eq!(b'B', string.bytes[0]); + assert_eq!(b'O', string.bytes[1]); + assert_eq!(b'T', string.bytes[2]); + } + + // Input is larger than the container can hold + assert!(KnownString::<3>::from_str("test").is_none()); + } + + #[test] + fn equality() { + assert_eq!( + KnownString::<64>::from_str("test").unwrap(), + KnownString::<64>::from_str("test").unwrap() + ); + assert_ne!( + KnownString::<64>::from_str("foo"), + KnownString::<64>::from_str("bar") + ); + } + + #[test] + fn get() { + assert_eq!( + "identify", + KnownString::<64>::from_str("identify").unwrap().get() + ); + } + + #[test] + fn serde() { + let string = KnownString::<64>::from_str("test").unwrap(); + + serde_test::assert_tokens(&string, &[Token::Str("test")]); + } + + #[test] + fn to_string() { + let string = KnownString::<4>::from_bytes(b"TEST"); + + assert_eq!("TEST", string.to_string()); + } + + #[test] + fn try_from() { + let string = KnownString::<3>::try_from("BOT").unwrap(); + + assert_eq!(b"BOT", &string.bytes); + } +} diff --git a/twilight-model/src/util/mod.rs b/twilight-model/src/util/mod.rs index b1d0a6e9f5f..a052bf2f253 100644 --- a/twilight-model/src/util/mod.rs +++ b/twilight-model/src/util/mod.rs @@ -3,9 +3,127 @@ pub mod datetime; pub mod image_hash; +pub(crate) mod known_string; + pub use self::{datetime::Timestamp, image_hash::ImageHash}; #[allow(clippy::trivially_copy_pass_by_ref)] pub(crate) fn is_false(value: &bool) -> bool { !value } + +macro_rules! impl_typed { + ($type: ty, u8) => { + impl_typed!($type, integer, u8); + }; + ($type: ty, u16) => { + impl_typed!($type, integer, u16); + }; + ($type: ty, integer, $raw: ty) => { + impl $type { + /// Create a new value from a dynamic raw value. + /// + /// The provided value isn't validated. Known valid values are + /// associated constants on this type. + pub const fn new(raw_value: $raw) -> Self { + Self(raw_value) + } + + /// Retrieve the raw value. + pub const fn get(&self) -> $raw { + self.0 + } + } + + impl std::fmt::Debug for $type { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + if let Some(name) = self.name() { + f.debug_struct(stringify!($type)) + .field("name", &name) + .field("value", &self.0) + .finish() + } else { + f.debug_tuple(stringify!($type)).field(&self.0).finish() + } + } + } + + impl From<$raw> for $type { + fn from(value: $raw) -> Self { + Self(value) + } + } + + impl From<$type> for $raw { + fn from(value: $type) -> Self { + value.get() + } + } + }; + ($type: ty, String) => { + impl $type { + /// Create a mention type from a dynamic value. + /// + /// The provided mention type must be 64 bytes or smaller. + pub fn new(mention_type: &str) -> Option { + $crate::util::known_string::KnownString::from_str(mention_type).map(Self) + } + + /// Get the value of the mention type. + /// + /// # Panics + /// + /// Panics if the mention type isn't valid UTF-8. + pub fn get(&self) -> &str { + self.0.get() + } + + /// Create a event type from a set of bytes. + const fn from_bytes(input: &[u8]) -> Self { + Self(KnownString::from_bytes(input)) + } + } + + impl AsRef for $type { + fn as_ref(&self) -> &str { + self.get() + } + } + + impl std::fmt::Debug for $type { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.write_str(self.name().unwrap_or_else(|| self.get())) + } + } + + impl std::ops::Deref for $type { + type Target = str; + + fn deref(&self) -> &Self::Target { + self.get() + } + } + + impl std::str::FromStr for $type { + type Err = (); + + fn from_str(s: &str) -> Result { + Self::try_from(s) + } + } + + impl ToString for $type { + fn to_string(&self) -> String { + KnownString::to_string(&self.0) + } + } + + impl TryFrom<&str> for $type { + type Error = (); + + fn try_from(value: &str) -> Result { + Self::new(value).ok_or(()) + } + } + }; +} diff --git a/twilight-model/src/visitor.rs b/twilight-model/src/visitor.rs index 5f8a6646f39..b9cc5d6bf8d 100644 --- a/twilight-model/src/visitor.rs +++ b/twilight-model/src/visitor.rs @@ -1,9 +1,3 @@ -use serde::de::{Error as DeError, Visitor}; -use std::{ - fmt::{Formatter, Result as FmtResult}, - marker::PhantomData, -}; - /// Deserializers for optional nullable fields. /// /// Some booleans in the Discord API are null when true, and not present when @@ -137,35 +131,3 @@ pub mod zeroable_id { }) } } - -pub struct U16EnumVisitor<'a> { - description: &'a str, - phantom: PhantomData, -} - -impl<'a> U16EnumVisitor<'a> { - pub const fn new(description: &'a str) -> Self { - Self { - description, - phantom: PhantomData, - } - } -} - -impl<'de> Visitor<'de> for U16EnumVisitor<'_> { - type Value = u16; - - fn expecting(&self, f: &mut Formatter<'_>) -> FmtResult { - f.write_str(self.description) - } - - fn visit_u16(self, value: u16) -> Result { - Ok(value) - } - - fn visit_u64(self, value: u64) -> Result { - let smaller = u16::try_from(value).map_err(E::custom)?; - - self.visit_u16(smaller) - } -} diff --git a/twilight-model/src/voice/close_code.rs b/twilight-model/src/voice/close_code.rs index e858384ca24..efb72953ba8 100644 --- a/twilight-model/src/voice/close_code.rs +++ b/twilight-model/src/voice/close_code.rs @@ -1,53 +1,99 @@ -use serde_repr::{Deserialize_repr, Serialize_repr}; +use serde::{Deserialize, Serialize}; /// Voice gateway close event codes. -#[derive(Clone, Copy, Debug, Deserialize_repr, Eq, Hash, PartialEq, Serialize_repr)] -#[non_exhaustive] -#[repr(u16)] -pub enum CloseCode { +#[derive(Clone, Copy, Deserialize, Eq, Hash, PartialEq, Serialize)] +pub struct CloseCode(u16); + +impl CloseCode { /// An invalid opcode was sent. - UnknownOpcode = 4001, + pub const UNKNOWN_OPCODE: Self = Self::new(4001); + /// An invalid payload was sent. - DecodeError = 4002, + pub const DECODE_ERROR: Self = Self::new(4002); + /// A payload was sent prior to identifying. - NotAuthenticated = 4003, + pub const NOT_AUTHENTICATED: Self = Self::new(4003); + /// An invalid token was sent when identifying. - AuthenticationFailed = 4004, + pub const AUTHENTICATION_FAILED: Self = Self::new(4004); + /// Multiple identify payloads were sent. - AlreadyAuthenticated = 4005, + pub const ALREADY_AUTHENTICATED: Self = Self::new(4005); + /// The session was invalidated. - SessionNoLongerValid = 4006, + pub const SESSION_NO_LONGER_VALID: Self = Self::new(4006); + /// The session timed out. - SessionTimedOut = 4009, + pub const SESSION_TIMED_OUT: Self = Self::new(4009); + /// The specified voice server was not found. - ServerNotFound = 4011, + pub const SERVER_NOT_FOUND: Self = Self::new(4011); + /// An unknown protocol was sent. - UnknownProtocol = 4012, + pub const UNKNOWN_PROTOCOL: Self = Self::new(4012); + /// Disconnected from the voice channel. - Disconnected = 4014, + pub const DISCONNECTED: Self = Self::new(4014); + /// The voice server crashed. - VoiceServerCrashed = 4015, + pub const VOICE_SERVER_CRASHED: Self = Self::new(4015); + /// The encryption could not be recognized. - UnknownEncryptionMode = 4016, + pub const UNKNOWN_ENCRYPTION_MODE: Self = Self::new(4016); + + /// Name of the associated constant. + /// + /// Returns `None` if the value doesn't have a defined constant. + pub const fn name(self) -> Option<&'static str> { + Some(match self { + Self::UNKNOWN_OPCODE => "UNKNOWN_OPCODE", + Self::DECODE_ERROR => "DECODE_ERROR", + Self::NOT_AUTHENTICATED => "NOT_AUTHENTICATED", + Self::AUTHENTICATION_FAILED => "AUTHENTICATION_FAILED", + Self::ALREADY_AUTHENTICATED => "ALREADY_AUTHENTICATED", + Self::SESSION_NO_LONGER_VALID => "SESSION_NO_LONGER_VALID", + Self::SESSION_TIMED_OUT => "SESSION_TIMED_OUT", + Self::SERVER_NOT_FOUND => "SERVER_NOT_FOUND", + Self::UNKNOWN_PROTOCOL => "UNKNOWN_PROTOCOL", + Self::DISCONNECTED => "DISCONNECTED", + Self::VOICE_SERVER_CRASHED => "VOICE_SERVER_CRASHED", + Self::UNKNOWN_ENCRYPTION_MODE => "UNKNOWN_ENCRYPTION_MODE", + _ => return None, + }) + } } +impl_typed!(CloseCode, u16); + #[cfg(test)] mod tests { use super::CloseCode; use serde_test::Token; + const MAP: &[(CloseCode, u16)] = &[ + (CloseCode::UNKNOWN_OPCODE, 4001), + (CloseCode::DECODE_ERROR, 4002), + (CloseCode::NOT_AUTHENTICATED, 4003), + (CloseCode::AUTHENTICATION_FAILED, 4004), + (CloseCode::ALREADY_AUTHENTICATED, 4005), + (CloseCode::SESSION_NO_LONGER_VALID, 4006), + (CloseCode::SESSION_TIMED_OUT, 4009), + (CloseCode::SERVER_NOT_FOUND, 4011), + (CloseCode::UNKNOWN_PROTOCOL, 4012), + (CloseCode::DISCONNECTED, 4014), + (CloseCode::VOICE_SERVER_CRASHED, 4015), + (CloseCode::UNKNOWN_ENCRYPTION_MODE, 4016), + ]; + #[test] fn variants() { - serde_test::assert_tokens(&CloseCode::UnknownOpcode, &[Token::U16(4001)]); - serde_test::assert_tokens(&CloseCode::DecodeError, &[Token::U16(4002)]); - serde_test::assert_tokens(&CloseCode::NotAuthenticated, &[Token::U16(4003)]); - serde_test::assert_tokens(&CloseCode::AuthenticationFailed, &[Token::U16(4004)]); - serde_test::assert_tokens(&CloseCode::AlreadyAuthenticated, &[Token::U16(4005)]); - serde_test::assert_tokens(&CloseCode::SessionTimedOut, &[Token::U16(4009)]); - serde_test::assert_tokens(&CloseCode::ServerNotFound, &[Token::U16(4011)]); - serde_test::assert_tokens(&CloseCode::UnknownProtocol, &[Token::U16(4012)]); - serde_test::assert_tokens(&CloseCode::Disconnected, &[Token::U16(4014)]); - serde_test::assert_tokens(&CloseCode::VoiceServerCrashed, &[Token::U16(4015)]); - serde_test::assert_tokens(&CloseCode::UnknownEncryptionMode, &[Token::U16(4016)]); + for (kind, num) in MAP { + serde_test::assert_tokens( + kind, + &[Token::NewtypeStruct { name: "CloseCode" }, Token::U16(*num)], + ); + assert_eq!(*kind, CloseCode::from(*num)); + assert_eq!(*num, kind.get()); + } } } diff --git a/twilight-model/src/voice/opcode.rs b/twilight-model/src/voice/opcode.rs index ef6b61f44f2..d61bfca6629 100644 --- a/twilight-model/src/voice/opcode.rs +++ b/twilight-model/src/voice/opcode.rs @@ -1,51 +1,93 @@ -use serde_repr::{Deserialize_repr, Serialize_repr}; +use serde::{Deserialize, Serialize}; /// Voice gateway opcodes. -#[derive(Clone, Copy, Debug, Deserialize_repr, Eq, Hash, PartialEq, Serialize_repr)] -#[non_exhaustive] -#[repr(u8)] -pub enum OpCode { +#[derive(Clone, Copy, Deserialize, Eq, Hash, PartialEq, Serialize)] +pub struct OpCode(u8); + +impl OpCode { /// Start a voice websocket connection. - Identify = 0, + pub const IDENTIFY: Self = Self::new(0); + /// Select the protocol to use. - SelectProtocol = 1, + pub const SELECT_PROTOCOL: Self = Self::new(1); + /// Received to indicate completion of handshake. - Ready = 2, + pub const READY: Self = Self::new(2); + /// Fired periodically to keep connection alive. - Heartbeat = 3, + pub const HEARTBEAT: Self = Self::new(3); + /// Received to indicate session description. - SessionDescription = 4, + pub const SESSION_DESCRIPTION: Self = Self::new(4); + /// Sent and received to indicate speaking status. - Speaking = 5, + pub const SPEAKING: Self = Self::new(5); + /// Received in response to a heartbeat. - HeartbeatAck = 6, + pub const HEARTBEAT_ACK: Self = Self::new(6); + /// Resume a previously disconnected session. - Resume = 7, + pub const RESUME: Self = Self::new(7); + /// Received after connecting, contains heartbeat interval. - Hello = 8, + pub const HELLO: Self = Self::new(8); + /// Received to indicate a successful resume. - Resumed = 9, + pub const RESUMED: Self = Self::new(9); + /// Received to indicate someone was disconnected. - ClientDisconnect = 13, + pub const CLIENT_DISCONNECT: Self = Self::new(13); + + /// Name of the associated constant. + /// + /// Returns `None` if the value doesn't have a defined constant. + pub const fn name(self) -> Option<&'static str> { + Some(match self { + Self::CLIENT_DISCONNECT => "CLIENT_DISCONNECT", + Self::HEARTBEAT => "HEARTBEAT", + Self::HEARTBEAT_ACK => "HEARTBEAT_ACK", + Self::HELLO => "HELLO", + Self::IDENTIFY => "IDENTIFY", + Self::READY => "READY", + Self::RESUME => "RESUME", + Self::RESUMED => "RESUMED", + Self::SELECT_PROTOCOL => "SELECT_PROTOCOL", + Self::SESSION_DESCRIPTION => "SESSION_DESCRIPTION", + Self::SPEAKING => "SPEAKING", + _ => return None, + }) + } } +impl_typed!(OpCode, u8); + #[cfg(test)] mod tests { use super::OpCode; use serde_test::Token; + const MAP: &[(OpCode, u8)] = &[ + (OpCode::SELECT_PROTOCOL, 1), + (OpCode::READY, 2), + (OpCode::HEARTBEAT, 3), + (OpCode::SESSION_DESCRIPTION, 4), + (OpCode::SPEAKING, 5), + (OpCode::HEARTBEAT_ACK, 6), + (OpCode::RESUME, 7), + (OpCode::HELLO, 8), + (OpCode::RESUMED, 9), + (OpCode::CLIENT_DISCONNECT, 13), + ]; + #[test] fn variants() { - serde_test::assert_tokens(&OpCode::Identify, &[Token::U8(0)]); - serde_test::assert_tokens(&OpCode::SelectProtocol, &[Token::U8(1)]); - serde_test::assert_tokens(&OpCode::Ready, &[Token::U8(2)]); - serde_test::assert_tokens(&OpCode::Heartbeat, &[Token::U8(3)]); - serde_test::assert_tokens(&OpCode::SessionDescription, &[Token::U8(4)]); - serde_test::assert_tokens(&OpCode::Speaking, &[Token::U8(5)]); - serde_test::assert_tokens(&OpCode::HeartbeatAck, &[Token::U8(6)]); - serde_test::assert_tokens(&OpCode::Resume, &[Token::U8(7)]); - serde_test::assert_tokens(&OpCode::Hello, &[Token::U8(8)]); - serde_test::assert_tokens(&OpCode::Resumed, &[Token::U8(9)]); - serde_test::assert_tokens(&OpCode::ClientDisconnect, &[Token::U8(13)]); + for (kind, num) in MAP { + serde_test::assert_tokens( + kind, + &[Token::NewtypeStruct { name: "OpCode" }, Token::U8(*num)], + ); + assert_eq!(*kind, OpCode::from(*num)); + assert_eq!(*num, kind.get()); + } } } diff --git a/twilight-standby/src/lib.rs b/twilight-standby/src/lib.rs index b109ff2bf84..6447d58391d 100644 --- a/twilight-standby/src/lib.rs +++ b/twilight-standby/src/lib.rs @@ -156,7 +156,7 @@ impl Standby { match event { Event::InteractionCreate(e) => { - if e.kind == InteractionType::MessageComponent { + if e.kind == InteractionType::MESSAGE_COMPONENT { if let Some(message) = &e.message { completions.add_with(&Self::process_specific_event( &self.components, @@ -216,7 +216,7 @@ impl Standby { /// let guild_id = Id::new(123); /// /// let reaction = standby - /// .wait_for(guild_id, |event: &Event| event.kind() == EventType::BanAdd) + /// .wait_for(guild_id, |event: &Event| event.kind() == EventType::BAN_ADD) /// .await?; /// # Ok(()) } /// ``` @@ -265,7 +265,7 @@ impl Standby { /// let guild_id = Id::new(123); /// /// let mut stream = - /// standby.wait_for_stream(guild_id, |event: &Event| event.kind() == EventType::BanAdd); + /// standby.wait_for_stream(guild_id, |event: &Event| event.kind() == EventType::BAN_ADD); /// /// while let Some(event) = stream.next().await { /// if let Event::BanAdd(ban) = event { @@ -1103,7 +1103,7 @@ mod tests { guild_id: Some(Id::new(4)), id: Id::new(3), interaction: None, - kind: MessageType::Regular, + kind: MessageType::REGULAR, member: None, mention_channels: Vec::new(), mention_everyone: false, @@ -1143,14 +1143,14 @@ mod tests { data: Some(InteractionData::MessageComponent( MessageComponentInteractionData { custom_id: String::from("Click"), - component_type: ComponentType::Button, + component_type: ComponentType::BUTTON, values: Vec::new(), }, )), guild_id: Some(Id::new(3)), guild_locale: None, id: Id::new(4), - kind: InteractionType::MessageComponent, + kind: InteractionType::MESSAGE_COMPONENT, locale: Some("en-GB".to_owned()), member: None, message: Some(message()), @@ -1359,7 +1359,7 @@ mod tests { async fn test_wait_for_event_stream() { let standby = Standby::new(); let mut stream = - standby.wait_for_event_stream(|event: &Event| event.kind() == EventType::Resumed); + standby.wait_for_event_stream(|event: &Event| event.kind() == EventType::RESUMED); standby.process(&Event::Resumed); assert_eq!(stream.next().await, Some(Event::Resumed)); assert!(!standby.events.is_empty()); @@ -1481,7 +1481,7 @@ mod tests { #[tokio::test] async fn test_handles_wrong_events() { let standby = Standby::new(); - let wait = standby.wait_for_event(|event: &Event| event.kind() == EventType::Resumed); + let wait = standby.wait_for_event(|event: &Event| event.kind() == EventType::RESUMED); standby.process(&Event::PresencesReplace); standby.process(&Event::PresencesReplace); @@ -1499,17 +1499,18 @@ mod tests { let standby = Standby::new(); // generic event handler gets message creates - let wait = standby.wait_for_event(|event: &Event| event.kind() == EventType::MessageCreate); + let wait = + standby.wait_for_event(|event: &Event| event.kind() == EventType::MESSAGE_CREATE); standby.process(&Event::MessageCreate(Box::new(MessageCreate(message())))); assert!(matches!(wait.await, Ok(Event::MessageCreate(_)))); // generic event handler gets reaction adds - let wait = standby.wait_for_event(|event: &Event| event.kind() == EventType::ReactionAdd); + let wait = standby.wait_for_event(|event: &Event| event.kind() == EventType::REACTION_ADD); standby.process(&Event::ReactionAdd(Box::new(ReactionAdd(reaction())))); assert!(matches!(wait.await, Ok(Event::ReactionAdd(_)))); // generic event handler gets other guild events - let wait = standby.wait_for_event(|event: &Event| event.kind() == EventType::RoleDelete); + let wait = standby.wait_for_event(|event: &Event| event.kind() == EventType::ROLE_DELETE); standby.process(&Event::RoleDelete(RoleDelete { guild_id: Id::new(1), role_id: Id::new(2), @@ -1518,7 +1519,7 @@ mod tests { // guild event handler gets message creates or reaction events let wait = standby.wait_for(Id::new(1), |event: &Event| { - event.kind() == EventType::ReactionAdd + event.kind() == EventType::REACTION_ADD }); standby.process(&Event::ReactionAdd(Box::new(ReactionAdd(reaction())))); assert!(matches!(wait.await, Ok(Event::ReactionAdd(_)))); diff --git a/twilight-util/src/builder/command.rs b/twilight-util/src/builder/command.rs index 92e9a3f549f..1be432a9ba4 100644 --- a/twilight-util/src/builder/command.rs +++ b/twilight-util/src/builder/command.rs @@ -9,7 +9,7 @@ //! CommandBuilder::new( //! "blep", //! "Send a random adorable animal photo", -//! CommandType::ChatInput, +//! CommandType::CHAT_INPUT, //! ) //! .option( //! StringBuilder::new("animal", "The type of animal") @@ -33,7 +33,7 @@ //! CommandBuilder::new( //! "birthday", //! "Wish a friend a happy birthday", -//! CommandType::ChatInput, +//! CommandType::CHAT_INPUT, //! ) //! .name_localizations([("zh-CN", "生日"), ("el", "γενέθλια")]) //! .description_localizations([("zh-Cn", "祝你朋友生日快乐")]) @@ -202,7 +202,7 @@ impl AttachmentBuilder { choices: None, description: description.into(), description_localizations: None, - kind: CommandOptionType::Attachment, + kind: CommandOptionType::ATTACHMENT, max_length: None, max_value: None, min_length: None, @@ -286,7 +286,7 @@ impl BooleanBuilder { choices: None, description: description.into(), description_localizations: None, - kind: CommandOptionType::Boolean, + kind: CommandOptionType::BOOLEAN, max_length: None, max_value: None, min_length: None, @@ -370,7 +370,7 @@ impl ChannelBuilder { choices: None, description: description.into(), description_localizations: None, - kind: CommandOptionType::Channel, + kind: CommandOptionType::CHANNEL, max_length: None, max_value: None, min_length: None, @@ -462,7 +462,7 @@ impl IntegerBuilder { choices: Some(Vec::new()), description: description.into(), description_localizations: None, - kind: CommandOptionType::Integer, + kind: CommandOptionType::INTEGER, max_length: None, max_value: None, min_length: None, @@ -631,7 +631,7 @@ impl MentionableBuilder { choices: None, description: description.into(), description_localizations: None, - kind: CommandOptionType::Mentionable, + kind: CommandOptionType::MENTIONABLE, max_length: None, max_value: None, min_length: None, @@ -715,7 +715,7 @@ impl NumberBuilder { choices: Some(Vec::new()), description: description.into(), description_localizations: None, - kind: CommandOptionType::Number, + kind: CommandOptionType::NUMBER, max_length: None, max_value: None, min_length: None, @@ -884,7 +884,7 @@ impl RoleBuilder { choices: None, description: description.into(), description_localizations: None, - kind: CommandOptionType::Role, + kind: CommandOptionType::ROLE, max_length: None, max_value: None, min_length: None, @@ -968,7 +968,7 @@ impl StringBuilder { choices: Some(Vec::new()), description: description.into(), description_localizations: None, - kind: CommandOptionType::String, + kind: CommandOptionType::STRING, max_length: None, max_value: None, min_length: None, @@ -1140,7 +1140,7 @@ impl SubCommandBuilder { choices: None, description: description.into(), description_localizations: None, - kind: CommandOptionType::SubCommand, + kind: CommandOptionType::SUB_COMMAND, max_length: None, max_value: None, min_length: None, @@ -1232,7 +1232,7 @@ impl SubCommandGroupBuilder { choices: None, description: description.into(), description_localizations: None, - kind: CommandOptionType::SubCommandGroup, + kind: CommandOptionType::SUB_COMMAND_GROUP, max_length: None, max_value: None, min_length: None, @@ -1316,7 +1316,7 @@ impl UserBuilder { choices: None, description: description.into(), description_localizations: None, - kind: CommandOptionType::User, + kind: CommandOptionType::USER, max_length: None, max_value: None, min_length: None, @@ -1410,7 +1410,7 @@ mod tests { CommandBuilder::new( "permissions", "Get or edit permissions for a user or a role", - CommandType::ChatInput, + CommandType::CHAT_INPUT, ) .nsfw(true) .option( @@ -1460,7 +1460,7 @@ mod tests { description: String::from("Get or edit permissions for a user or a role"), guild_id: None, id: None, - kind: CommandType::ChatInput, + kind: CommandType::CHAT_INPUT, name: String::from("permissions"), name_localizations: None, nsfw: Some(true), @@ -1472,7 +1472,7 @@ mod tests { choices: None, description: "Get or edit permissions for a user".to_owned(), description_localizations: None, - kind: CommandOptionType::SubCommandGroup, + kind: CommandOptionType::SUB_COMMAND_GROUP, max_length: None, max_value: None, min_length: None, @@ -1486,7 +1486,7 @@ mod tests { choices: None, description: "Get permissions for a user".to_owned(), description_localizations: None, - kind: CommandOptionType::SubCommand, + kind: CommandOptionType::SUB_COMMAND, max_length: None, max_value: None, min_length: None, @@ -1500,7 +1500,7 @@ mod tests { choices: None, description: "The user to get".to_owned(), description_localizations: None, - kind: CommandOptionType::User, + kind: CommandOptionType::USER, max_length: None, max_value: None, min_length: None, @@ -1519,7 +1519,7 @@ mod tests { permissions will be returned" .to_owned(), description_localizations: None, - kind: CommandOptionType::Channel, + kind: CommandOptionType::CHANNEL, max_length: None, max_value: None, min_length: None, @@ -1538,7 +1538,7 @@ mod tests { choices: None, description: "Edit permissions for a user".to_owned(), description_localizations: None, - kind: CommandOptionType::SubCommand, + kind: CommandOptionType::SUB_COMMAND, max_length: None, max_value: None, min_length: None, @@ -1552,7 +1552,7 @@ mod tests { choices: None, description: "The user to edit".to_owned(), description_localizations: None, - kind: CommandOptionType::User, + kind: CommandOptionType::USER, max_length: None, max_value: None, min_length: None, @@ -1571,7 +1571,7 @@ mod tests { permissions will be edited" .to_owned(), description_localizations: None, - kind: CommandOptionType::Channel, + kind: CommandOptionType::CHANNEL, max_length: None, max_value: None, min_length: None, @@ -1593,7 +1593,7 @@ mod tests { choices: None, description: "Get or edit permissions for a role".to_owned(), description_localizations: None, - kind: CommandOptionType::SubCommandGroup, + kind: CommandOptionType::SUB_COMMAND_GROUP, max_length: None, max_value: None, min_length: None, @@ -1607,7 +1607,7 @@ mod tests { choices: None, description: "Get permissions for a role".to_owned(), description_localizations: None, - kind: CommandOptionType::SubCommand, + kind: CommandOptionType::SUB_COMMAND, max_length: None, max_value: None, min_length: None, @@ -1621,7 +1621,7 @@ mod tests { choices: None, description: "The role to get".to_owned(), description_localizations: None, - kind: CommandOptionType::Role, + kind: CommandOptionType::ROLE, max_length: None, max_value: None, min_length: None, @@ -1640,7 +1640,7 @@ mod tests { permissions will be returned" .to_owned(), description_localizations: None, - kind: CommandOptionType::Channel, + kind: CommandOptionType::CHANNEL, max_length: None, max_value: None, min_length: None, @@ -1659,7 +1659,7 @@ mod tests { choices: None, description: "Edit permissions for a role".to_owned(), description_localizations: None, - kind: CommandOptionType::SubCommand, + kind: CommandOptionType::SUB_COMMAND, max_length: None, max_value: None, min_length: None, @@ -1673,7 +1673,7 @@ mod tests { choices: None, description: "The role to edit".to_owned(), description_localizations: None, - kind: CommandOptionType::Role, + kind: CommandOptionType::ROLE, max_length: None, max_value: None, min_length: None, @@ -1692,7 +1692,7 @@ mod tests { permissions will be edited" .to_owned(), description_localizations: None, - kind: CommandOptionType::Channel, + kind: CommandOptionType::CHANNEL, max_length: None, max_value: None, min_length: None, @@ -1717,7 +1717,7 @@ mod tests { #[test] fn validate() { - let result = CommandBuilder::new("", "", CommandType::ChatInput).validate(); + let result = CommandBuilder::new("", "", CommandType::CHAT_INPUT).validate(); assert!(result.is_err()); } diff --git a/twilight-util/src/builder/interaction_response_data.rs b/twilight-util/src/builder/interaction_response_data.rs index d848914cf53..16d100b982a 100644 --- a/twilight-util/src/builder/interaction_response_data.rs +++ b/twilight-util/src/builder/interaction_response_data.rs @@ -16,7 +16,7 @@ use twilight_model::{ /// /// let component = Component::ActionRow(ActionRow { /// components: Vec::from([Component::Button(Button { -/// style: ButtonStyle::Primary, +/// style: ButtonStyle::PRIMARY, /// emoji: None, /// label: Some("Button label".to_string()), /// custom_id: Some("button_id".to_string()), @@ -83,9 +83,9 @@ impl InteractionResponseDataBuilder { /// Set the autocomplete choices of the response. /// /// Only valid when the type of the interaction is - /// [`ApplicationCommandAutocompleteResult`]. + /// [`APPLICATION_COMMAND_AUTOCOMPLETE_RESULT`]. /// - /// [`ApplicationCommandAutocompleteResult`]: twilight_model::http::interaction::InteractionResponseType::ApplicationCommandAutocompleteResult + /// [`APPLICATION_COMMAND_AUTOCOMPLETE_RESULT`]: twilight_model::http::interaction::InteractionResponseType::APPLICATION_COMMAND_AUTOCOMPLETE_RESULT pub fn choices(mut self, choices: impl IntoIterator) -> Self { self.0.choices = Some(choices.into_iter().collect()); @@ -191,12 +191,12 @@ mod tests { #[test] fn callback_data_builder() { let allowed_mentions = AllowedMentions { - parse: Vec::from([MentionType::Everyone]), + parse: Vec::from([MentionType::EVERYONE]), ..Default::default() }; let component = Component::Button(Button { - style: ButtonStyle::Primary, + style: ButtonStyle::PRIMARY, emoji: None, label: Some("test label".into()), custom_id: Some("test custom id".into()), diff --git a/twilight-util/src/permission_calculator/mod.rs b/twilight-util/src/permission_calculator/mod.rs index 74405f0bd35..68b05779f65 100644 --- a/twilight-util/src/permission_calculator/mod.rs +++ b/twilight-util/src/permission_calculator/mod.rs @@ -60,19 +60,19 @@ //! allow: Permissions::ADD_REACTIONS | Permissions::EMBED_LINKS, //! deny: Permissions::empty(), //! id: Id::new(1), -//! kind: PermissionOverwriteType::Role, +//! kind: PermissionOverwriteType::ROLE, //! }, //! // Member is denied the Send Messages permission. //! PermissionOverwrite { //! allow: Permissions::empty(), //! deny: Permissions::SEND_MESSAGES, //! id: user_id.cast(), -//! kind: PermissionOverwriteType::Member, +//! kind: PermissionOverwriteType::MEMBER, //! }, //! ]; //! //! let calculator = PermissionCalculator::new(guild_id, user_id, everyone_role, member_roles); -//! let calculated_permissions = calculator.in_channel(ChannelType::GuildText, channel_overwrites); +//! let calculated_permissions = calculator.in_channel(ChannelType::GUILD_TEXT, channel_overwrites); //! //! // Now that we've got the member's permissions in the channel, we can //! // check that they have the server-wide View Channel permission and @@ -388,11 +388,11 @@ impl<'a> PermissionCalculator<'a> { // Remove the permissions not used by a channel depending on the channel // type. - if matches!(channel_type, ChannelType::GuildStageVoice) { + if matches!(channel_type, ChannelType::GUILD_STAGE_VOICE) { permissions = bitops::remove(permissions, PERMISSIONS_STAGE_OMIT); - } else if matches!(channel_type, ChannelType::GuildText) { + } else if matches!(channel_type, ChannelType::GUILD_TEXT) { permissions = bitops::remove(permissions, PERMISSIONS_TEXT_OMIT); - } else if matches!(channel_type, ChannelType::GuildVoice) { + } else if matches!(channel_type, ChannelType::GUILD_VOICE) { permissions = bitops::remove(permissions, PERMISSIONS_VOICE_OMIT); } @@ -438,7 +438,7 @@ const fn process_permission_overwrites( let overwrite = &channel_overwrites[idx]; match overwrite.kind { - PermissionOverwriteType::Role => { + PermissionOverwriteType::ROLE => { // We need to process the @everyone role first, so apply it // straight to the permissions. The other roles' permissions // will be applied later. @@ -460,15 +460,14 @@ const fn process_permission_overwrites( roles_allow = bitops::insert(roles_allow, overwrite.allow); roles_deny = bitops::insert(roles_deny, overwrite.deny); } - PermissionOverwriteType::Member => { + PermissionOverwriteType::MEMBER => { if overwrite.id.get() == configured_user_id.get() { member_allow = bitops::insert(member_allow, overwrite.allow); member_deny = bitops::insert(member_deny, overwrite.deny); } } // Unknown, impossible to try and calculate with this - PermissionOverwriteType::Unknown(_) => (), - _ => unimplemented!(), + _ => (), } idx += 1; @@ -558,11 +557,11 @@ mod tests { allow: Permissions::SEND_TTS_MESSAGES, deny: Permissions::VIEW_CHANNEL, id: Id::new(3), - kind: PermissionOverwriteType::Role, + kind: PermissionOverwriteType::ROLE, }]; let calculated = PermissionCalculator::new(guild_id, user_id, everyone_role, roles) - .in_channel(ChannelType::GuildText, overwrites); + .in_channel(ChannelType::GUILD_TEXT, overwrites); assert_eq!(calculated, Permissions::empty()); } @@ -573,11 +572,11 @@ mod tests { allow: Permissions::SEND_TTS_MESSAGES, deny: Permissions::VIEW_CHANNEL, id: Id::new(2), - kind: PermissionOverwriteType::Member, + kind: PermissionOverwriteType::MEMBER, }]; let calculated = PermissionCalculator::new(guild_id, user_id, everyone_role, roles) - .in_channel(ChannelType::GuildText, overwrites); + .in_channel(ChannelType::GUILD_TEXT, overwrites); assert_eq!(calculated, Permissions::empty()); } @@ -589,18 +588,18 @@ mod tests { allow: Permissions::VIEW_CHANNEL, deny: Permissions::empty(), id: Id::new(2), - kind: PermissionOverwriteType::Member, + kind: PermissionOverwriteType::MEMBER, }, PermissionOverwrite { allow: Permissions::empty(), deny: Permissions::VIEW_CHANNEL, id: Id::new(3), - kind: PermissionOverwriteType::Role, + kind: PermissionOverwriteType::ROLE, }, ]; let calculated = PermissionCalculator::new(guild_id, user_id, everyone_role, roles) - .in_channel(ChannelType::GuildText, overwrites); + .in_channel(ChannelType::GUILD_TEXT, overwrites); assert_eq!( calculated, @@ -619,7 +618,7 @@ mod tests { let roles = &[(Id::new(3), Permissions::SEND_MESSAGES)]; let calculated = PermissionCalculator::new(guild_id, user_id, everyone_role, roles) - .in_channel(ChannelType::GuildVoice, &[]); + .in_channel(ChannelType::GUILD_VOICE, &[]); assert_eq!(calculated, Permissions::CONNECT); } @@ -632,7 +631,7 @@ mod tests { let roles = &[(Id::new(3), Permissions::SEND_MESSAGES)]; let calculated = PermissionCalculator::new(guild_id, user_id, everyone_role, roles) - .in_channel(ChannelType::GuildText, &[]); + .in_channel(ChannelType::GUILD_TEXT, &[]); // The `CONNECT` permission isn't included because text channels don't // have the permission. @@ -654,11 +653,11 @@ mod tests { allow: Permissions::ATTACH_FILES, deny: Permissions::SEND_MESSAGES, id: Id::new(3), - kind: PermissionOverwriteType::Role, + kind: PermissionOverwriteType::ROLE, }]; let calculated = PermissionCalculator::new(guild_id, user_id, everyone_role, roles) - .in_channel(ChannelType::GuildText, overwrites); + .in_channel(ChannelType::GUILD_TEXT, overwrites); assert_eq!(calculated, Permissions::MANAGE_MESSAGES); } @@ -674,7 +673,7 @@ mod tests { // Ensure that the denial of "send messages" doesn't actually occur due // to the user being an administrator. - assert!(calc.in_channel(ChannelType::GuildText, &[]).is_all()); + assert!(calc.in_channel(ChannelType::GUILD_TEXT, &[]).is_all()); } /// Test that guild-level permissions are removed in the permissions for a @@ -682,11 +681,11 @@ mod tests { #[test] fn guild_level_removed_in_channel() { const CHANNEL_TYPES: &[ChannelType] = &[ - ChannelType::GuildCategory, - ChannelType::GuildAnnouncement, - ChannelType::GuildStageVoice, - ChannelType::GuildText, - ChannelType::GuildVoice, + ChannelType::GUILD_CATEGORY, + ChannelType::GUILD_ANNOUNCEMENT, + ChannelType::GUILD_STAGE_VOICE, + ChannelType::GUILD_TEXT, + ChannelType::GUILD_VOICE, ]; // We need to remove the `ADMINISTRATOR` permission or else the diff --git a/twilight-validate/src/channel.rs b/twilight-validate/src/channel.rs index 467144b0c04..c1721b64c65 100644 --- a/twilight-validate/src/channel.rs +++ b/twilight-validate/src/channel.rs @@ -91,7 +91,11 @@ impl Display for ChannelValidationError { } ChannelValidationErrorType::TopicInvalid => f.write_str("the topic is invalid"), ChannelValidationErrorType::TypeInvalid { kind } => { - Display::fmt(kind.name(), f)?; + if let Some(name) = kind.name() { + Display::fmt(&name, f)?; + } else { + Display::fmt(&kind.get(), f)?; + } f.write_str(" is not a thread") } @@ -183,7 +187,7 @@ pub const fn bulk_delete_messages(message_count: usize) -> Result<(), ChannelVal pub const fn is_thread(kind: ChannelType) -> Result<(), ChannelValidationError> { if matches!( kind, - ChannelType::AnnouncementThread | ChannelType::PublicThread | ChannelType::PrivateThread + ChannelType::ANNOUNCEMENT_THREAD | ChannelType::PUBLIC_THREAD | ChannelType::PRIVATE_THREAD ) { Ok(()) } else { @@ -330,11 +334,11 @@ mod tests { #[test] fn thread_is_thread() { - assert!(is_thread(ChannelType::AnnouncementThread).is_ok()); - assert!(is_thread(ChannelType::PrivateThread).is_ok()); - assert!(is_thread(ChannelType::PublicThread).is_ok()); + assert!(is_thread(ChannelType::ANNOUNCEMENT_THREAD).is_ok()); + assert!(is_thread(ChannelType::PRIVATE_THREAD).is_ok()); + assert!(is_thread(ChannelType::PUBLIC_THREAD).is_ok()); - assert!(is_thread(ChannelType::Group).is_err()); + assert!(is_thread(ChannelType::GROUP).is_err()); } #[test] diff --git a/twilight-validate/src/command.rs b/twilight-validate/src/command.rs index ab1b192c764..7f4e15bfd2c 100644 --- a/twilight-validate/src/command.rs +++ b/twilight-validate/src/command.rs @@ -285,7 +285,7 @@ pub fn command(value: &Command) -> Result<(), CommandValidationError> { .. } = value; - if *kind == CommandType::ChatInput { + if *kind == CommandType::CHAT_INPUT { self::description(description)?; if let Some(description_localizations) = description_localizations { for description in description_localizations.values() { @@ -300,22 +300,20 @@ pub fn command(value: &Command) -> Result<(), CommandValidationError> { if let Some(name_localizations) = name_localizations { for name in name_localizations.values() { - match kind { - CommandType::ChatInput => self::chat_input_name(name)?, - CommandType::User | CommandType::Message => { + match *kind { + CommandType::CHAT_INPUT => self::chat_input_name(name)?, + CommandType::USER | CommandType::MESSAGE => { self::name(name)?; } - CommandType::Unknown(_) => (), - _ => unimplemented!(), + _ => {} } } } - match kind { - CommandType::ChatInput => self::chat_input_name(name), - CommandType::User | CommandType::Message => self::name(name), - CommandType::Unknown(_) => Ok(()), - _ => unimplemented!(), + match *kind { + CommandType::CHAT_INPUT => self::chat_input_name(name), + CommandType::USER | CommandType::MESSAGE => self::name(name), + _ => Ok(()), } } @@ -344,7 +342,7 @@ pub fn option_characters(option: &CommandOption) -> usize { longest_localization_characters(&option.description, &option.description_localizations); match option.kind { - CommandOptionType::String => { + CommandOptionType::STRING => { if let Some(choices) = option.choices.as_ref() { for choice in choices { if let CommandOptionChoiceValue::String(string_choice) = &choice.value { @@ -356,7 +354,7 @@ pub fn option_characters(option: &CommandOption) -> usize { } } } - CommandOptionType::SubCommandGroup | CommandOptionType::SubCommand => { + CommandOptionType::SUB_COMMAND_GROUP | CommandOptionType::SUB_COMMAND => { if let Some(options) = option.options.as_ref() { for option in options { characters += option_characters(option); @@ -427,9 +425,9 @@ pub fn description(value: impl AsRef) -> Result<(), CommandValidationError> /// /// Returns an error of type [`NameLengthInvalid`] if the name is invalid. /// -/// [`User`]: CommandType::User -/// [`Message`]: CommandType::Message -/// [`ChatInput`]: CommandType::ChatInput +/// [`User`]: CommandType::USER +/// [`Message`]: CommandType::MESSAGE +/// [`ChatInput`]: CommandType::CHAT_INPUT /// [`NameLengthInvalid`]: CommandValidationErrorType::NameLengthInvalid pub fn name(value: impl AsRef) -> Result<(), CommandValidationError> { let len = value.as_ref().chars().count(); @@ -459,7 +457,7 @@ pub fn name(value: impl AsRef) -> Result<(), CommandValidationError> { /// non-alphanumeric character or an uppercase character for which a lowercase /// variant exists. /// -/// [`ChatInput`]: CommandType::ChatInput +/// [`ChatInput`]: CommandType::CHAT_INPUT /// [`NameLengthInvalid`]: CommandValidationErrorType::NameLengthInvalid /// [`NameCharacterInvalid`]: CommandValidationErrorType::NameCharacterInvalid pub fn chat_input_name(value: impl AsRef) -> Result<(), CommandValidationError> { @@ -513,7 +511,7 @@ pub fn option_name(value: impl AsRef) -> Result<(), CommandValidationError> /// non-alphanumeric character or an uppercase character for which a lowercase /// variant exists. /// -/// [`ChatInput`]: CommandType::ChatInput +/// [`ChatInput`]: CommandType::CHAT_INPUT /// [`NameCharacterInvalid`]: CommandValidationErrorType::NameCharacterInvalid fn name_characters(value: impl AsRef) -> Result<(), CommandValidationError> { let chars = value.as_ref().chars(); @@ -661,7 +659,7 @@ mod tests { )])), guild_id: Some(Id::new(2)), id: Some(Id::new(3)), - kind: CommandType::ChatInput, + kind: CommandType::CHAT_INPUT, name: "b".repeat(32), name_localizations: Some(HashMap::from([("en-US".to_string(), "b".repeat(32))])), nsfw: None, @@ -680,7 +678,7 @@ mod tests { let valid_context_menu_command = Command { description: String::new(), - kind: CommandType::Message, + kind: CommandType::MESSAGE, ..valid_command.clone() }; @@ -688,7 +686,7 @@ mod tests { let invalid_context_menu_command = Command { description: "example description".to_string(), - kind: CommandType::Message, + kind: CommandType::MESSAGE, ..valid_command }; @@ -731,7 +729,7 @@ mod tests { )])), guild_id: Some(Id::new(2)), id: Some(Id::new(3)), - kind: CommandType::ChatInput, + kind: CommandType::CHAT_INPUT, name: "b".repeat(10), name_localizations: Some(HashMap::from([("en-US".to_string(), "b".repeat(32))])), nsfw: None, @@ -744,7 +742,7 @@ mod tests { "en-US".to_string(), "a".repeat(100), )])), - kind: CommandOptionType::SubCommandGroup, + kind: CommandOptionType::SUB_COMMAND_GROUP, max_length: None, max_value: None, min_length: None, @@ -760,7 +758,7 @@ mod tests { "en-US".to_string(), "a".repeat(10), )])), - kind: CommandOptionType::SubCommand, + kind: CommandOptionType::SUB_COMMAND, max_length: None, max_value: None, min_length: None, @@ -786,7 +784,7 @@ mod tests { "en-US".to_string(), "a".repeat(10), )])), - kind: CommandOptionType::String, + kind: CommandOptionType::STRING, max_length: None, max_value: None, min_length: None, @@ -827,7 +825,7 @@ mod tests { choices: None, description: "a description".to_owned(), description_localizations: None, - kind: CommandOptionType::String, + kind: CommandOptionType::STRING, max_length: None, max_value: None, min_length: None, diff --git a/twilight-validate/src/component.rs b/twilight-validate/src/component.rs index 5370e9f99fc..cf5c2ac4f0c 100644 --- a/twilight-validate/src/component.rs +++ b/twilight-validate/src/component.rs @@ -203,7 +203,7 @@ impl Display for ComponentValidationError { Debug::fmt(style, f)?; f.write_str(", which must have a ")?; - f.write_str(if *style == ButtonStyle::Link { + f.write_str(if *style == ButtonStyle::LINK { "url" } else { "custom id" @@ -233,13 +233,23 @@ impl Display for ComponentValidationError { } ComponentValidationErrorType::InvalidChildComponent { kind } => { f.write_str("a '")?; - Display::fmt(&kind, f)?; + + if let Some(name) = kind.name() { + Display::fmt(&name, f)?; + } else { + Display::fmt(&kind.get(), f)?; + } f.write_str(" component was provided, but can not be a child component") } ComponentValidationErrorType::InvalidRootComponent { kind } => { f.write_str("a '")?; - Display::fmt(kind, f)?; + + if let Some(name) = kind.name() { + Display::fmt(&name, f)?; + } else { + Display::fmt(&kind.get(), f)?; + } f.write_str("' component was provided, but can not be a root component") } @@ -353,7 +363,7 @@ pub enum ComponentValidationErrorType { ButtonConflict, /// Button does not have the required field based on its style. /// - /// A button with a style of [`ButtonStyle::Link`] must have a URL set, + /// A button with a style of [`ButtonStyle::LINK`] must have a URL set, /// while buttons of other styles must have a custom ID set. ButtonStyle { /// Style of the button. @@ -518,7 +528,7 @@ pub fn action_row(action_row: &ActionRow) -> Result<(), ComponentValidationError Component::ActionRow(_) => { return Err(ComponentValidationError { kind: ComponentValidationErrorType::InvalidChildComponent { - kind: ComponentType::ActionRow, + kind: ComponentType::ACTION_ROW, }, }); } @@ -527,9 +537,7 @@ pub fn action_row(action_row: &ActionRow) -> Result<(), ComponentValidationError Component::TextInput(text_input) => self::text_input(text_input)?, Component::Unknown(unknown) => { return Err(ComponentValidationError { - kind: ComponentValidationErrorType::InvalidChildComponent { - kind: ComponentType::Unknown(*unknown), - }, + kind: ComponentValidationErrorType::InvalidChildComponent { kind: *unknown }, }) } } @@ -547,8 +555,8 @@ pub fn action_row(action_row: &ActionRow) -> Result<(), ComponentValidationError /// /// Returns an error of type /// [`ButtonStyle`][`ComponentValidationErrorType::ButtonStyle`] if -/// [`ButtonStyle::Link`] is provided and a URL is provided, or if the style is -/// not [`ButtonStyle::Link`] and a custom ID is not provided. +/// [`ButtonStyle::LINK`] is provided and a URL is provided, or if the style is +/// not [`ButtonStyle::LINK`] and a custom ID is not provided. /// /// Returns an error of type [`ComponentCustomIdLength`] if the provided custom /// ID is too long. @@ -575,7 +583,7 @@ pub fn button(button: &Button) -> Result<(), ComponentValidationError> { // // Lastly, we check if the button is not a link and a custom ID is // not set. - let is_link = button.style == ButtonStyle::Link; + let is_link = button.style == ButtonStyle::LINK; if (is_link && !has_url) || (!is_link && !has_custom_id) { return Err(ComponentValidationError { @@ -1071,11 +1079,11 @@ mod tests { // All styles of buttons. const ALL_BUTTON_STYLES: &[ButtonStyle] = &[ - ButtonStyle::Primary, - ButtonStyle::Secondary, - ButtonStyle::Success, - ButtonStyle::Danger, - ButtonStyle::Link, + ButtonStyle::PRIMARY, + ButtonStyle::SECONDARY, + ButtonStyle::SUCCESS, + ButtonStyle::DANGER, + ButtonStyle::LINK, ]; #[test] @@ -1087,7 +1095,7 @@ mod tests { name: "📚".into() }), label: Some("Read".into()), - style: ButtonStyle::Link, + style: ButtonStyle::LINK, url: Some("https://abebooks.com".into()), }; @@ -1142,7 +1150,7 @@ mod tests { disabled: false, emoji: None, label: None, - style: ButtonStyle::Primary, + style: ButtonStyle::PRIMARY, url: Some("https://twilight.rs".to_owned()), };