From fc77033737234cdf19004528ada1461341e2f4ad Mon Sep 17 00:00:00 2001 From: Zeyla Hellyer Date: Sat, 7 Jan 2023 19:17:47 -0500 Subject: [PATCH 01/10] feat!: rework model enums wip --- Cargo.toml | 26 +- examples/model-webhook-slash.rs | 10 +- twilight-cache-inmemory/src/event/guild.rs | 18 +- .../src/event/interaction.rs | 8 +- twilight-cache-inmemory/src/event/message.rs | 2 +- twilight-cache-inmemory/src/event/presence.rs | 4 +- .../src/event/stage_instance.rs | 2 +- twilight-cache-inmemory/src/model/sticker.rs | 10 +- twilight-cache-inmemory/src/test.rs | 20 +- twilight-gateway/src/config.rs | 4 +- twilight-gateway/src/error.rs | 4 +- twilight-gateway/src/event.rs | 147 +- twilight-gateway/src/json.rs | 12 +- twilight-gateway/src/shard.rs | 22 +- twilight-http/src/client/mod.rs | 56 +- .../create_global_command/chat_input.rs | 2 +- .../command/create_global_command/message.rs | 2 +- .../command/create_global_command/user.rs | 2 +- .../create_guild_command/chat_input.rs | 2 +- .../command/create_guild_command/message.rs | 2 +- .../command/create_guild_command/user.rs | 2 +- .../src/request/application/command/mod.rs | 4 +- .../interaction/create_response.rs | 2 +- .../request/channel/invite/create_invite.rs | 2 +- .../request/channel/thread/create_thread.rs | 4 +- .../thread/create_thread_from_message.rs | 20 +- .../thread/get_public_archived_threads.rs | 12 +- .../channel/thread/remove_thread_member.rs | 4 +- .../src/request/channel/update_channel.rs | 4 +- .../channel/update_channel_permission.rs | 6 +- .../create_auto_moderation_rule.rs | 46 +- .../src/request/guild/create_guild/builder.rs | 34 +- .../src/request/guild/update_guild.rs | 12 +- .../create_guild_scheduled_event/external.rs | 2 +- .../create_guild_scheduled_event/mod.rs | 4 +- .../stage_instance.rs | 2 +- .../create_guild_scheduled_event/voice.rs | 2 +- .../update_guild_scheduled_event.rs | 28 +- twilight-model/Cargo.toml | 1 - .../src/application/command/command_type.rs | 103 +- twilight-model/src/application/command/mod.rs | 97 +- .../src/application/command/option.rs | 175 +- .../src/application/command/permissions.rs | 78 +- .../interaction/application_command/mod.rs | 12 +- .../interaction/application_command/option.rs | 119 +- .../application_command/resolved.rs | 20 +- .../interaction/interaction_type.rs | 157 +- .../interaction/message_component.rs | 19 +- .../src/application/interaction/mod.rs | 67 +- .../src/application/interaction/modal.rs | 22 +- twilight-model/src/channel/channel_mention.rs | 5 +- twilight-model/src/channel/channel_type.rs | 260 +-- twilight-model/src/channel/forum.rs | 159 +- .../src/channel/message/activity.rs | 117 +- .../src/channel/message/allowed_mentions.rs | 94 +- .../src/channel/message/component/button.rs | 101 +- .../src/channel/message/component/kind.rs | 140 +- .../src/channel/message/component/mod.rs | 97 +- .../channel/message/component/text_input.rs | 72 +- .../src/channel/message/interaction.rs | 12 +- twilight-model/src/channel/message/kind.rs | 259 +-- twilight-model/src/channel/message/mod.rs | 30 +- .../channel/message/sticker/format_type.rs | 82 +- .../src/channel/message/sticker/kind.rs | 82 +- .../src/channel/message/sticker/message.rs | 7 +- .../src/channel/message/sticker/mod.rs | 29 +- .../src/channel/message/sticker/pack.rs | 14 +- twilight-model/src/channel/mod.rs | 42 +- .../src/channel/permission_overwrite.rs | 79 +- .../src/channel/stage_instance/mod.rs | 5 +- .../channel/stage_instance/privacy_level.rs | 61 +- .../channel/thread/auto_archive_duration.rs | 102 +- twilight-model/src/channel/thread/metadata.rs | 5 +- .../src/channel/video_quality_mode.rs | 90 +- twilight-model/src/channel/webhook/kind.rs | 88 +- twilight-model/src/channel/webhook/mod.rs | 10 +- twilight-model/src/gateway/close_code.rs | 271 ++-- twilight-model/src/gateway/event/dispatch.rs | 126 +- twilight-model/src/gateway/event/gateway.rs | 87 +- twilight-model/src/gateway/event/kind.rs | 577 ++++--- twilight-model/src/gateway/event/mod.rs | 136 +- twilight-model/src/gateway/mod.rs | 5 +- twilight-model/src/gateway/opcode.rs | 156 +- .../auto_moderation_action_execution.rs | 18 +- .../gateway/payload/incoming/member_chunk.rs | 12 +- .../payload/incoming/thread_members_update.rs | 15 +- .../src/gateway/payload/outgoing/heartbeat.rs | 2 +- .../src/gateway/payload/outgoing/identify.rs | 2 +- .../payload/outgoing/request_guild_members.rs | 6 +- .../src/gateway/payload/outgoing/resume.rs | 2 +- .../payload/outgoing/update_presence.rs | 2 +- .../payload/outgoing/update_voice_state.rs | 2 +- .../src/gateway/presence/activity_type.rs | 104 +- .../src/gateway/presence/client_status.rs | 15 +- twilight-model/src/gateway/presence/mod.rs | 19 +- twilight-model/src/gateway/presence/status.rs | 169 +- twilight-model/src/guild/audit_log/change.rs | 152 +- .../src/guild/audit_log/change_key.rs | 1429 +++++++++-------- twilight-model/src/guild/audit_log/entry.rs | 7 +- .../src/guild/audit_log/event_type.rs | 441 +++-- .../guild/audit_log/optional_entry_info.rs | 134 +- .../src/guild/auto_moderation/action.rs | 62 +- .../src/guild/auto_moderation/event_type.rs | 44 +- .../src/guild/auto_moderation/mod.rs | 35 +- .../src/guild/auto_moderation/preset_type.rs | 58 +- .../guild/auto_moderation/trigger_metadata.rs | 21 +- .../src/guild/auto_moderation/trigger_type.rs | 65 +- .../default_message_notification_level.rs | 70 +- .../src/guild/explicit_content_filter.rs | 72 +- twilight-model/src/guild/feature.rs | 349 ++-- twilight-model/src/guild/integration.rs | 40 +- .../src/guild/integration_expire_behavior.rs | 69 +- twilight-model/src/guild/integration_type.rs | 136 +- twilight-model/src/guild/invite/channel.rs | 5 +- twilight-model/src/guild/invite/guild.rs | 5 +- twilight-model/src/guild/invite/mod.rs | 21 +- .../src/guild/invite/target_type.rs | 61 +- twilight-model/src/guild/mfa_level.rs | 58 +- twilight-model/src/guild/mod.rs | 31 +- twilight-model/src/guild/nsfw_level.rs | 72 +- twilight-model/src/guild/partial_guild.rs | 31 +- twilight-model/src/guild/premium_tier.rs | 89 +- .../src/guild/scheduled_event/mod.rs | 169 +- twilight-model/src/guild/template/mod.rs | 45 +- .../src/guild/verification_level.rs | 84 +- twilight-model/src/guild/widget/mod.rs | 8 +- twilight-model/src/http/interaction.rs | 96 +- .../src/http/permission_overwrite.rs | 86 +- twilight-model/src/oauth/install_params.rs | 18 +- twilight-model/src/oauth/mod.rs | 3 +- twilight-model/src/oauth/scope.rs | 250 +++ twilight-model/src/oauth/team/member.rs | 5 +- .../src/oauth/team/membership_state.rs | 66 +- twilight-model/src/user/connection.rs | 5 +- .../src/user/connection_visibility.rs | 68 +- twilight-model/src/user/current_user.rs | 10 +- twilight-model/src/user/mod.rs | 10 +- twilight-model/src/user/premium_type.rs | 81 +- twilight-model/src/user/profile.rs | 5 +- twilight-model/src/util/known_string.rs | 296 ++++ twilight-model/src/util/mod.rs | 2 + twilight-model/src/visitor.rs | 39 - twilight-model/src/voice/close_code.rs | 112 +- twilight-model/src/voice/opcode.rs | 107 +- twilight-standby/src/lib.rs | 29 +- twilight-util/src/builder/command.rs | 60 +- .../src/builder/interaction_response_data.rs | 10 +- .../src/permission_calculator/mod.rs | 53 +- twilight-validate/src/channel.rs | 10 +- twilight-validate/src/command.rs | 30 +- twilight-validate/src/component.rs | 30 +- 151 files changed, 6341 insertions(+), 4186 deletions(-) create mode 100644 twilight-model/src/oauth/scope.rs create mode 100644 twilight-model/src/util/known_string.rs diff --git a/Cargo.toml b/Cargo.toml index 005e3ef0ab8..08cd73df5b1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,19 +1,19 @@ [workspace] members = [ - "book/tests", - "examples", - "twilight", - "twilight-cache-inmemory", - "twilight-gateway", - "twilight-gateway-queue", - "twilight-http", - "twilight-http-ratelimiting", - "twilight-lavalink", - "twilight-mention", + # "book/tests", + # "examples", + # "twilight", + # "twilight-cache-inmemory", + # "twilight-gateway", + # "twilight-gateway-queue", + # "twilight-http", + # "twilight-http-ratelimiting", + # "twilight-lavalink", + # "twilight-mention", "twilight-model", - "twilight-standby", - "twilight-util", - "twilight-validate", + # "twilight-standby", + # "twilight-util", + # "twilight-validate", ] resolver = "2" 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 9f3947f577e..836b133eb70 100644 --- a/twilight-cache-inmemory/src/event/guild.rs +++ b/twilight-cache-inmemory/src/event/guild.rs @@ -318,7 +318,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, @@ -355,7 +355,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 { @@ -380,7 +380,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, @@ -399,11 +399,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), @@ -414,16 +414,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(), roles: Vec::new(), rules_channel_id: None, @@ -435,7 +435,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 edb24664342..d3e637a551f 100644 --- a/twilight-cache-inmemory/src/event/interaction.rs +++ b/twilight-cache-inmemory/src/event/interaction.rs @@ -105,7 +105,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(), @@ -156,7 +156,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, @@ -177,7 +177,7 @@ mod tests { reactions: Vec::new(), reference: None, sticker_items: vec![MessageSticker { - format_type: StickerFormatType::Png, + format_type: StickerFormatType::PNG, id: Id::new(1), name: "sticker name".to_owned(), }], @@ -230,7 +230,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 7a9e0853598..2ee74b663c8 100644 --- a/twilight-cache-inmemory/src/event/message.rs +++ b/twilight-cache-inmemory/src/event/message.rs @@ -180,7 +180,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/test.rs b/twilight-cache-inmemory/src/test.rs index 973f2bd5da8..c3457546b9c 100644 --- a/twilight-cache-inmemory/src/test.rs +++ b/twilight-cache-inmemory/src/test.rs @@ -66,7 +66,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, @@ -240,7 +240,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, @@ -303,10 +303,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, @@ -369,11 +369,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, @@ -384,16 +384,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(), roles: Vec::new(), rules_channel_id: None, @@ -405,7 +405,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 8a399d98799..5f831c39f54 100644 --- a/twilight-gateway/src/config.rs +++ b/twilight-gateway/src/config.rs @@ -418,14 +418,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..89fb00095fd 100644 --- a/twilight-gateway/src/error.rs +++ b/twilight-gateway/src/error.rs @@ -338,7 +338,7 @@ mod tests { ), ( ReceiveMessageErrorType::FatallyClosed { - close_code: CloseCode::InvalidIntents, + close_code: CloseCode::INVALID_INTENTS, }, "shard fatally closed: Invalid Intents", ), @@ -368,7 +368,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 c5aee4a265a..fb336abd753 100644 --- a/twilight-gateway/src/event.rs +++ b/twilight-gateway/src/event.rs @@ -314,74 +314,75 @@ 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::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::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_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(), } } } @@ -391,11 +392,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 f6f74e130ea..9d1cd5a10c6 100644 --- a/twilight-gateway/src/json.rs +++ b/twilight-gateway/src/json.rs @@ -59,15 +59,17 @@ pub fn parse( #[cfg(not(feature = "simd-json"))] let mut json_deserializer = serde_json::Deserializer::from_str(json); - let opcode = match OpCode::from(gateway_deserializer.op()) { - Some(opcode) => opcode, - None => { + let opcode = match OpCode::new(gateway_deserializer.op()) { + opcode => opcode, + #[allow(unused)] + _ => { + // todo!() return Err(ReceiveMessageError { kind: ReceiveMessageErrorType::Deserializing { event: String::from_utf8_lossy(&bytes).into_owned(), }, source: Some(format!("unknown opcode: {}", gateway_deserializer.op()).into()), - }) + }); } }; @@ -83,7 +85,7 @@ pub fn parse( source: Some( format!( "unknown opcode/dispatch event type: {}/{event_type:?}", - opcode as u8 + u8::from(opcode), ) .into(), ), diff --git a/twilight-gateway/src/shard.rs b/twilight-gateway/src/shard.rs index 27125c540c8..32ebf7322b9 100644 --- a/twilight-gateway/src/shard.rs +++ b/twilight-gateway/src/shard.rs @@ -141,8 +141,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, @@ -938,7 +938,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(), @@ -988,11 +988,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"); @@ -1001,7 +1001,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 @@ -1033,7 +1033,7 @@ impl Shard { None => self.identify(), } } - Some(OpCode::InvalidSession) => { + OpCode::INVALID_SESSION => { let resumable = Self::parse_event(event)?.data; tracing::debug!(resumable, "received invalid session"); if resumable { @@ -1047,7 +1047,7 @@ impl Shard { .map_err(ProcessError::from_send)?; } } - Some(OpCode::Reconnect) => { + OpCode::RECONNECT => { tracing::debug!("received reconnect"); self.session = self .close(CloseFrame::RESUME) @@ -1167,7 +1167,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!( @@ -1178,8 +1178,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 a79e41d67a3..e12a282ec86 100644 --- a/twilight-http/src/client/mod.rs +++ b/twilight-http/src/client/mod.rs @@ -332,7 +332,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"]) @@ -617,7 +617,7 @@ impl Client { /// allow: Some(Permissions::VIEW_CHANNEL), /// deny: Some(Permissions::SEND_MESSAGES), /// id: role_id.cast(), - /// kind: PermissionOverwriteType::Role, + /// kind: PermissionOverwriteType::ROLE, /// }; /// /// client @@ -1791,7 +1791,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 @@ -1802,7 +1802,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, @@ -1815,11 +1815,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. @@ -1834,11 +1834,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, @@ -1891,15 +1891,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, @@ -1913,11 +1913,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, @@ -2186,7 +2186,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", @@ -2210,7 +2210,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", @@ -2273,17 +2273,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 2705685ee95..9176ba265ba 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 70217c21a03..56871479b51 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 @@ -82,7 +82,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"]) @@ -123,13 +123,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(), }, ); @@ -137,13 +137,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() @@ -154,13 +154,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() @@ -192,13 +192,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( @@ -212,28 +212,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, @@ -247,12 +247,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, @@ -264,7 +264,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, @@ -284,7 +284,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 772c4d35579..9ab72d5bf04 100644 --- a/twilight-model/Cargo.toml +++ b/twilight-model/Cargo.toml @@ -16,7 +16,6 @@ version = "0.14.1" 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..9480d760424 100644 --- a/twilight-model/src/application/command/command_type.rs +++ b/twilight-model/src/application/command/command_type.rs @@ -2,92 +2,83 @@ 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 { +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), -} + pub const MESSAGE: Self = Self::new(3); -impl CommandType { - pub const fn kind(self) -> &'static str { - match self { - Self::ChatInput => "ChatInput", - Self::User => "User", - Self::Message => "Message", - Self::Unknown(_) => "Unknown", - } + /// Create a new command type from a dynamic value. + /// + /// The provided value isn't validated. Known valid values are associated + /// constants such as [`CHAT_INPUT`][`Self::CHAT_INPUT`]. + pub const fn new(command_type: u8) -> Self { + Self(command_type) + } + + /// Retrieve the value of the command type. + /// + /// # Examples + /// + /// ``` + /// use twilight_model::application::command::CommandType; + /// + /// assert_eq!(3, CommandType::MESSAGE.get()); + /// ``` + pub const fn get(&self) -> u8 { + self.0 } } impl From for CommandType { fn from(value: u8) -> Self { - match value { - 1 => Self::ChatInput, - 2 => Self::User, - 3 => Self::Message, - unknown => Self::Unknown(unknown), - } + Self(value) } } impl From for u8 { fn from(value: CommandType) -> Self { - match value { - CommandType::ChatInput => 1, - CommandType::User => 2, - CommandType::Message => 3, - CommandType::Unknown(unknown) => unknown, - } + value.get() } } #[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 207a90bed12..5798dfaefa5 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, @@ -263,7 +263,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, @@ -279,7 +279,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, @@ -298,7 +298,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), @@ -350,7 +350,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"), @@ -368,7 +371,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"), @@ -381,7 +387,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"), @@ -394,7 +403,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, @@ -405,7 +417,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"), @@ -423,7 +438,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, @@ -434,12 +452,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, @@ -457,7 +481,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), @@ -480,7 +507,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, @@ -513,7 +543,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, @@ -524,7 +557,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"), @@ -541,7 +577,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 c11130dafb0..7382886fe74 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,99 @@ 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, Debug, 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); + + /// Create a new command option type from a dynamic value. + /// + /// The provided value isn't validated. Known valid values are associated + /// constants such as [`STRING`][`Self::STRING`]. + pub const fn new(command_option_type: u8) -> Self { + Self(command_option_type) + } + + /// Retrieve the value of the command option type. + /// + /// # Examples + /// + /// ``` + /// use twilight_model::application::command::CommandOptionType; + /// + /// assert_eq!(4, CommandOptionType::INTEGER.get()); + /// ``` + pub const fn get(&self) -> u8 { + self.0 + } +} + +impl From for CommandOptionType { + fn from(value: u8) -> Self { + Self(value) + } +} + +impl From for u8 { + fn from(value: CommandOptionType) -> Self { + value.get() + } +} + +#[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..a2c85fd0e2c 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)] @@ -62,9 +61,9 @@ impl CommandPermissionType { /// 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, } } } @@ -80,13 +79,48 @@ 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, Debug, Deserialize, Eq, 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); + + /// Create a new command permission data type from a dynamic value. + /// + /// The provided value isn't validated. Known valid values are associated + /// constants such as [`ROLE`][`Self::ROLE`]. + pub const fn new(command_permission_data_type: u8) -> Self { + Self(command_permission_data_type) + } + + /// Retrieve the value of the command permission data type. + /// + /// # Examples + /// + /// ``` + /// use twilight_model::application::command::permissions::CommandPermissionDataType; + /// + /// assert_eq!(3, CommandPermissionDataType::CHANNEL.get()); + /// ``` + pub const fn get(&self) -> u8 { + self.0 + } +} + +impl From for CommandPermissionDataType { + fn from(value: u8) -> Self { + Self(value) + } +} + +impl From for u8 { + fn from(value: CommandPermissionDataType) -> Self { + value.get() + } } impl<'de> Deserialize<'de> for CommandPermission { @@ -97,9 +131,10 @@ 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()), + _ => todo!(), }; tracing::trace!(id = %data.id, kind = ?data.kind); @@ -150,7 +185,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 +240,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 +255,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 06697a36f6e..31563a0aff3 100644 --- a/twilight-model/src/application/interaction/application_command/resolved.rs +++ b/twilight-model/src/application/interaction/application_command/resolved.rs @@ -138,7 +138,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(), @@ -193,7 +193,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, @@ -214,7 +214,7 @@ mod tests { reactions: Vec::new(), reference: None, sticker_items: vec![MessageSticker { - format_type: StickerFormatType::Png, + format_type: StickerFormatType::PNG, id: Id::new(1), name: "sticker name".to_owned(), }], @@ -257,7 +257,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, ), @@ -319,6 +319,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"), @@ -403,6 +406,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, @@ -444,6 +450,9 @@ mod tests { len: 3, }, Token::Str("format_type"), + Token::NewtypeStruct { + name: "StickerFormatType", + }, Token::U8(1), Token::Str("id"), Token::NewtypeStruct { name: "Id" }, @@ -523,6 +532,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..22c256dc73c 100644 --- a/twilight-model/src/application/interaction/interaction_type.rs +++ b/twilight-model/src/application/interaction/interaction_type.rs @@ -1,131 +1,96 @@ -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, Debug, 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, + pub const MESSAGE_COMPONENT: Self = Self::new(3); + /// Interaction involves an autocomplete request. - ApplicationCommandAutocomplete = 4, + pub const APPLICATION_COMMAND_AUTOCOMPLETE: Self = Self::new(4); + /// Interaction involves a modal submit. - ModalSubmit = 5, -} + pub const MODAL_SUBMIT: Self = Self::new(5); -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", - } + /// Create a new interaction type from a dynamic value. + /// + /// The provided value isn't validated. Known valid values are associated + /// constants such as [`MODAL_SUBMIT`][`Self::MODAL_SUBMIT`]. + pub const fn new(connection_visibility: u8) -> Self { + Self(connection_visibility) } -} -#[derive(Debug)] -pub struct UnknownInteractionTypeError { - value: u8, + /// Retrieve the value of the interaction type. + /// + /// # Examples + /// + /// ``` + /// use twilight_model::application::interaction::InteractionType; + /// + /// assert_eq!(3, InteractionType::MESSAGE_COMPONENT.get()); + /// ``` + pub const fn get(&self) -> u8 { + self.0 + } } -impl Display for UnknownInteractionTypeError { - fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult { - f.write_str("unknown interaction type: ")?; - - Display::fmt(&self.value, f) +impl From for InteractionType { + fn from(value: u8) -> Self { + Self(value) } } -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 From for u8 { + fn from(value: InteractionType) -> Self { + value.get() } } #[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}; + use super::InteractionType; + use serde_test::Token; - 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); + 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 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()); - } - - #[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 5678b9313be..b61e8e09ab6 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), } @@ -444,7 +444,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)), @@ -495,7 +495,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, @@ -559,6 +559,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) }, @@ -569,7 +572,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"), @@ -644,7 +650,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..8262e9f399d 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, +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, + pub const GUILD_DIRECTORY: Self = Self::new(14); + /// Channel that can only contain threads. - GuildForum, - Unknown(u8), -} + pub const GUILD_FORUM: Self = Self::new(15); -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), - } + /// Create a new channel type from a dynamic value. + /// + /// The provided value isn't validated. Known valid values are associated + /// constants such as [`GUILD_TEXT`][`Self::GUILD_TEXT`]. + pub const fn new(channel_type: u8) -> Self { + Self(channel_type) } -} -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, - } + /// Retrieve the value of the channel type. + /// + /// # Examples + /// + /// ``` + /// use twilight_model::channel::ChannelType; + /// + /// assert_eq!(15, ChannelType::GUILD_FORUM.get()); + /// ``` + pub const fn get(&self) -> u8 { + self.0 } -} -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,98 @@ 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", + Self::ANNOUNCEMENT_THREAD => "ANNOUNCEMENT_THREAD", + Self::GROUP => "GROUP", + Self::GUILD_CATEGORY => "GUILD_CATEGORY", + Self::GUILD_DIRECTORY => "GUILD_DIRECTORY", + Self::GUILD_FORUM => "GUILD_FORUM", + Self::GUILD_ANNOUNCEMENT => "GUILD_ANNOUNCEMENT", + Self::GUILD_STAGE_VOICE => "GUILD_STAGE_VOICE", + Self::GUILD_TEXT => "GUILD_TEXT", + Self::GUILD_VOICE => "GUILD_VOICE", + Self::PRIVATE => "PRIVATE", + Self::PRIVATE_THREAD => "PRIVATE_THREAD", + Self::PUBLIC_THREAD => "PUBLIC_THREAD", + _ => "UNKNOWN", } } } +impl From for ChannelType { + fn from(value: u8) -> Self { + Self(value) + } +} + +impl From for u8 { + fn from(value: ChannelType) -> Self { + value.get() + } +} + #[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(), *name); + } } } diff --git a/twilight-model/src/channel/forum.rs b/twilight-model/src/channel/forum.rs index fa8f48693f0..d970b0877bd 100644 --- a/twilight-model/src/channel/forum.rs +++ b/twilight-model/src/channel/forum.rs @@ -22,106 +22,125 @@ pub struct DefaultReaction { /// Layout of a [channel] that is a [forum]. /// /// [channel]: super::Channel -/// [forum]: super::ChannelType::GuildForum +/// [forum]: super::ChannelType::GUILD_FORUM #[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)] -#[non_exhaustive] -#[serde(from = "u8", into = "u8")] -pub enum ForumLayout { +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, + pub const LIST_VIEW: Self = Self::new(1); + /// No default has been set for the forum channel. - NotSet, - /// Variant value is unknown to the library. - Unknown(u8), -} + pub const NOT_SET: Self = Self::new(0); + + /// Create a new forum layout from a dynamic value. + /// + /// The provided value isn't validated. Known valid values are associated + /// constants such as [`GALLERY_VIEW`][`Self::GALLERY_VIEW`]. + pub const fn new(forum_layout: u8) -> Self { + Self(forum_layout) + } + + /// Retrieve the value of the forum layout. + /// + /// # Examples + /// + /// ``` + /// use twilight_model::channel::forum::ForumLayout; + /// + /// assert_eq!(1, ForumLayout::LIST_VIEW.get()); + /// ``` + pub const fn get(&self) -> u8 { + self.0 + } -impl ForumLayout { pub const fn name(self) -> &'static str { match self { - Self::ListView => "ListView", - Self::NotSet => "NotSet", - Self::GalleryView => "GalleryView", - Self::Unknown(_) => "Unknown", + Self::GALLERY_VIEW => "GALLERY_VIEW", + Self::LIST_VIEW => "LIST_VIEW", + Self::NOT_SET => "NOT_SET", + _ => "UNKNOWN", } } } impl From for ForumLayout { fn from(value: u8) -> Self { - match value { - 0 => Self::NotSet, - 1 => Self::ListView, - 2 => Self::GalleryView, - unknown => Self::Unknown(unknown), - } + Self(value) } } impl From for u8 { fn from(value: ForumLayout) -> Self { - match value { - ForumLayout::NotSet => 0, - ForumLayout::ListView => 1, - ForumLayout::GalleryView => 2, - ForumLayout::Unknown(unknown) => unknown, - } + value.get() } } /// Layout of a [channel] that is a [forum]. /// /// [channel]: super::Channel -/// [forum]: super::ChannelType::GuildForum +/// [forum]: super::ChannelType::GUILD_FORUM #[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), -} +pub struct ForumSortOrder(u8); impl ForumSortOrder { + /// Sort forum posts by activity. + pub const LATEST_ACTIVITY: Self = Self::new(0); + + /// Sort forum posts by creation time (from most recent to oldest). + pub const CREATION_DATE: Self = Self::new(1); + + /// Create a new forum sort order from a dynamic value. + /// + /// The provided value isn't validated. Known valid values are associated + /// constants such as [`CREATION_DATE`][`Self::CREATION_DATE`]. + pub const fn new(forum_sort_order: u8) -> Self { + Self(forum_sort_order) + } + + /// Retrieve the value of the forum sort order. + /// + /// # Examples + /// + /// ``` + /// use twilight_model::channel::forum::ForumSortOrder; + /// + /// assert_eq!(0, ForumSortOrder::LATEST_ACTIVITY.get()); + /// ``` + pub const fn get(&self) -> u8 { + self.0 + } + pub const fn name(self) -> &'static str { match self { - Self::CreationDate => "CreationDate", - Self::LatestActivity => "LatestActivity", - Self::Unknown(_) => "Unknown", + Self::CREATION_DATE => "CREATION_DATE", + Self::LATEST_ACTIVITY => "LATEST_ACTIVITY", + _ => "UNKNOWN", } } } impl From for ForumSortOrder { fn from(value: u8) -> Self { - match value { - 0 => Self::LatestActivity, - 1 => Self::CreationDate, - unknown => Self::Unknown(unknown), - } + Self(value) } } impl From for u8 { fn from(value: ForumSortOrder) -> Self { - match value { - ForumSortOrder::LatestActivity => 0, - ForumSortOrder::CreationDate => 1, - ForumSortOrder::Unknown(unknown) => unknown, - } + value.get() } } -/// Tag that is able to be applied to a thread in a [`GuildForum`] [`Channel`]. +/// 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. @@ -211,33 +230,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(), *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!(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..d3516e73c5f 100644 --- a/twilight-model/src/channel/message/activity.rs +++ b/twilight-model/src/channel/message/activity.rs @@ -13,42 +13,53 @@ 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 { + +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); + + /// Create a new message activity type from a dynamic value. + /// + /// The provided value isn't validated. Known valid values are associated + /// constants such as [`SPECTATE`][`Self::SPECTATE`]. + pub const fn new(message_activity_type: u8) -> Self { + Self(message_activity_type) + } + + /// Retrieve the value of the message activity type. + /// + /// # Examples + /// + /// ``` + /// use twilight_model::channel::message::MessageActivityType; + /// + /// assert_eq!(1, MessageActivityType::JOIN.get()); + /// ``` + pub const fn get(&self) -> u8 { + self.0 + } } 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), - } + Self(value) } } 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, - } + value.get() } } @@ -60,7 +71,7 @@ mod tests { #[test] fn message_activity() { let value = MessageActivity { - kind: MessageActivityType::Join, + kind: MessageActivityType::JOIN, party_id: None, }; @@ -72,6 +83,9 @@ mod tests { len: 1, }, Token::Str("type"), + Token::NewtypeStruct { + name: "MessageActivityType", + }, Token::U8(1), Token::StructEnd, ], @@ -81,7 +95,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 +107,9 @@ mod tests { len: 2, }, Token::Str("type"), + Token::NewtypeStruct { + name: "MessageActivityType", + }, Token::U8(1), Token::Str("party_id"), Token::Some, @@ -104,10 +121,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..96d2462ae78 100644 --- a/twilight-model/src/channel/message/allowed_mentions.rs +++ b/twilight-model/src/channel/message/allowed_mentions.rs @@ -3,9 +3,14 @@ use crate::{ marker::{RoleMarker, UserMarker}, Id, }, - util::is_false, + util::{is_false, known_string::KnownString}, }; use serde::{Deserialize, Serialize}; +use std::{ + fmt::{Debug, Formatter, Result as FmtResult}, + ops::Deref, + str::FromStr, +}; /// Allowed mentions (pings). /// @@ -23,7 +28,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,16 +49,81 @@ 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"); + + /// 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 { + 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 mention type from a set of bytes. + const fn from_bytes(input: &[u8]) -> Self { + Self(KnownString::from_bytes(input)) + } +} + +impl AsRef for MentionType { + fn as_ref(&self) -> &str { + self.get() + } +} + +impl Debug for MentionType { + fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult { + f.write_str(self.get()) + } +} + +impl Deref for MentionType { + type Target = str; + + fn deref(&self) -> &Self::Target { + self.get() + } +} + +impl FromStr for MentionType { + type Err = (); + + fn from_str(s: &str) -> Result { + Self::try_from(s) + } +} + +impl ToString for MentionType { + fn to_string(&self) -> String { + KnownString::to_string(&self.0) + } +} + +impl TryFrom<&str> for MentionType { + type Error = (); + + fn try_from(value: &str) -> Result { + Self::new(value).ok_or(()) + } } #[cfg(test)] @@ -89,7 +159,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 +174,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..a7dfc26881e 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,82 @@ 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 { +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); + + /// Create a new button style from a dynamic value. + /// + /// The provided value isn't validated. Known valid values are associated + /// constants such as [`DANGER`][`Self::DANGER`]. + pub const fn new(button_style: u8) -> Self { + Self(button_style) + } + + /// Retrieve the value of the button style. + /// + /// # Examples + /// + /// ``` + /// use twilight_model::channel::message::component::ButtonStyle; + /// + /// assert_eq!(3, ButtonStyle::SUCCESS.get()); + /// ``` + pub const fn get(&self) -> u8 { + self.0 + } } 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), - } + Self(value) } } 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, - } + value.get() } } #[cfg(test)] mod tests { - use super::*; use serde::{Deserialize, Serialize}; use serde_test::Token; @@ -116,12 +124,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..3508b57ed5f 100644 --- a/twilight-model/src/channel/message/component/kind.rs +++ b/twilight-model/src/channel/message/component/kind.rs @@ -5,80 +5,88 @@ use std::fmt::{Display, Formatter, Result as FmtResult}; /// /// [`Component`]: super::Component #[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)] -#[non_exhaustive] -#[serde(from = "u8", into = "u8")] -pub enum ComponentType { +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), -} + pub const TEXT_INPUT: Self = Self::new(4); -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), - } + /// Create a new command type from a dynamic value. + /// + /// The provided value isn't validated. Known valid values are associated + /// constants such as [`ACTION_ROW`][`Self::ACTION_ROW`]. + pub const fn new(command_type: u8) -> Self { + Self(command_type) } -} -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, - } + /// Retrieve the value of the command type. + /// + /// # Examples + /// + /// ``` + /// use twilight_model::channel::message::component::ComponentType; + /// + /// assert_eq!(2, ComponentType::BUTTON.get()); + /// ``` + pub const fn get(&self) -> u8 { + self.0 } -} -impl ComponentType { /// Name of the component type. /// /// Variants have a name equivalent to the variant name itself. /// /// # Examples /// - /// Check the [`ActionRow`] variant's name: + /// Check the [`ACTION_ROW`] variant's name: /// /// ``` /// use twilight_model::channel::message::component::ComponentType; /// - /// assert_eq!("ActionRow", ComponentType::ActionRow.name()); + /// assert_eq!("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) -> &'static str { + match *self { + Self::ACTION_ROW => "ACTION_ROW", + Self::BUTTON => "BUTTON", + Self::SELECT_MENU => "SELECT_MENU", + Self::TEXT_INPUT => "TEXT_INPUT", + _ => "UNKNOWN", } } } +impl From for ComponentType { + fn from(value: u8) -> Self { + Self(value) + } +} + +impl From for u8 { + fn from(value: ComponentType) -> Self { + value.get() + } +} + impl Display for ComponentType { fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult { f.write_str(self.name()) @@ -87,40 +95,30 @@ impl Display for ComponentType { #[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..462b4c8ea41 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,14 +28,48 @@ 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, Debug, 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); + + /// Create a new text input style from a dynamic value. + /// + /// The provided value isn't validated. Known valid values are associated + /// constants such as [`SHORT`][`Self::SHORT`]. + pub const fn new(text_input_style: u8) -> Self { + Self(text_input_style) + } + + /// Retrieve the value of the text input style. + /// + /// # Examples + /// + /// ``` + /// use twilight_model::channel::message::component::TextInputStyle; + /// + /// assert_eq!(2, TextInputStyle::PARAGRAPH.get()); + /// ``` + pub const fn get(&self) -> u8 { + self.0 + } +} + +impl From for TextInputStyle { + fn from(value: u8) -> Self { + Self(value) + } +} + +impl From for u8 { + fn from(value: TextInputStyle) -> Self { + value.get() + } } #[cfg(test)] @@ -43,7 +77,7 @@ 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 +103,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 86c0a4ecd9b..3ac1ed235a9 100644 --- a/twilight-model/src/channel/message/interaction.rs +++ b/twilight-model/src/channel/message/interaction.rs @@ -43,7 +43,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, @@ -69,7 +69,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, ), @@ -89,7 +89,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 { @@ -153,6 +156,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 1047e631d47..0c36c413a5e 100644 --- a/twilight-model/src/channel/message/kind.rs +++ b/twilight-model/src/channel/message/kind.rs @@ -9,55 +9,101 @@ use serde::{Deserialize, Serialize}; /// [`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 { +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, - /// Variant value is unknown to the library. - Unknown(u8), + pub const AUTO_MODERATION_ACTION: Self = Self::new(24); + + /// Create a new message type from a dynamic value. + /// + /// The provided value isn't validated. Known valid values are associated + /// constants such as [`REGULAR`][`Self::REGULAR`]. + pub const fn new(message_type: u8) -> Self { + Self(message_type) + } + + /// Retrieve the value of the message type. + /// + /// # Examples + /// + /// ``` + /// use twilight_model::channel::message::MessageType; + /// + /// assert_eq!(19, MessageType::REPLY.get()); + /// ``` + pub const fn get(&self) -> u8 { + self.0 + } } impl MessageType { @@ -65,7 +111,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. /// @@ -77,20 +123,20 @@ 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::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 ) } @@ -98,7 +144,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 @@ -107,7 +153,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(), }; @@ -121,65 +167,13 @@ 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, - unknown => Self::Unknown(unknown), - } + Self(value) } } 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::Unknown(unknown) => unknown, - } + value.get() } } @@ -205,55 +199,66 @@ 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::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), ]; 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 e9cd9e1df22..6f9c1e345da 100644 --- a/twilight-model/src/channel/message/mod.rs +++ b/twilight-model/src/channel/message/mod.rs @@ -238,7 +238,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, @@ -259,7 +259,7 @@ mod tests { reactions: Vec::new(), reference: None, sticker_items: vec![MessageSticker { - format_type: StickerFormatType::Png, + format_type: StickerFormatType::PNG, id: Id::new(1), name: "sticker name".to_owned(), }], @@ -323,6 +323,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, @@ -364,6 +367,9 @@ mod tests { len: 3, }, Token::Str("format_type"), + Token::NewtypeStruct { + name: "StickerFormatType", + }, Token::U8(1), Token::Str("id"), Token::NewtypeStruct { name: "Id" }, @@ -390,7 +396,7 @@ mod tests { let value = Message { activity: Some(MessageActivity { - kind: MessageActivityType::Join, + kind: MessageActivityType::JOIN, party_id: None, }), application: Some(MessageApplication { @@ -428,7 +434,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, @@ -444,7 +450,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, @@ -465,7 +471,7 @@ mod tests { fail_if_not_exists: None, }), sticker_items: vec![MessageSticker { - format_type: StickerFormatType::Png, + format_type: StickerFormatType::PNG, id: Id::new(1), name: "sticker name".to_owned(), }], @@ -490,6 +496,9 @@ mod tests { len: 1, }, Token::Str("type"), + Token::NewtypeStruct { + name: "MessageActivityType", + }, Token::U8(1), Token::StructEnd, Token::Str("application"), @@ -563,6 +572,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, @@ -600,6 +612,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"), @@ -653,6 +668,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 393403b5a2c..778c025620f 100644 --- a/twilight-model/src/channel/message/sticker/format_type.rs +++ b/twilight-model/src/channel/message/sticker/format_type.rs @@ -4,38 +4,49 @@ use serde::{Deserialize, Serialize}; /// /// [`Sticker`]: super::Sticker #[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)] -#[non_exhaustive] -#[serde(from = "u8", into = "u8")] -pub enum StickerFormatType { +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, - /// Variant value is unknown to the library. - Unknown(u8), + pub const LOTTIE: Self = Self::new(3); + + /// Create a new sticker format type from a dynamic value. + /// + /// The provided value isn't validated. Known valid values are associated + /// constants such as [`LOTTIE`][`Self::LOTTIE`]. + pub const fn new(command_type: u8) -> Self { + Self(command_type) + } + + /// Retrieve the value of the sticker format type. + /// + /// # Examples + /// + /// ``` + /// use twilight_model::channel::message::sticker::StickerFormatType; + /// + /// assert_eq!(1, StickerFormatType::PNG.get()); + /// ``` + pub const fn get(&self) -> u8 { + self.0 + } } impl From for StickerFormatType { fn from(value: u8) -> Self { - match value { - 1 => StickerFormatType::Png, - 2 => StickerFormatType::Apng, - 3 => StickerFormatType::Lottie, - unknown => StickerFormatType::Unknown(unknown), - } + Self(value) } } impl From for u8 { fn from(value: StickerFormatType) -> Self { - match value { - StickerFormatType::Png => 1, - StickerFormatType::Apng => 2, - StickerFormatType::Lottie => 3, - StickerFormatType::Unknown(unknown) => unknown, - } + value.get() } } @@ -44,19 +55,26 @@ 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::Unknown(99), &[Token::U8(99)]); - } + const MAP: &[(StickerFormatType, u8)] = &[ + (StickerFormatType::PNG, 1), + (StickerFormatType::APNG, 2), + (StickerFormatType::LOTTIE, 3), + ]; #[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(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..c608001ba7c 100644 --- a/twilight-model/src/channel/message/sticker/kind.rs +++ b/twilight-model/src/channel/message/sticker/kind.rs @@ -4,36 +4,48 @@ use serde::{Deserialize, Serialize}; /// /// [`Sticker`]: super::Sticker #[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)] -#[non_exhaustive] -#[serde(from = "u8", into = "u8")] -pub enum StickerType { +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); + + /// Create a new sticker type from a dynamic value. + /// + /// The provided value isn't validated. Known valid values are associated + /// constants such as [`STANDARD`][`Self::STANDARD`]. + pub const fn new(sticker_type: u8) -> Self { + Self(sticker_type) + } + + /// Retrieve the value of the sticker type. + /// + /// # Examples + /// + /// ``` + /// use twilight_model::channel::message::sticker::StickerType; + /// + /// assert_eq!(2, StickerType::GUILD.get()); + /// ``` + pub const fn get(&self) -> u8 { + self.0 + } } impl From for StickerType { fn from(value: u8) -> Self { - match value { - 1 => StickerType::Standard, - 2 => StickerType::Guild, - unknown => StickerType::Unknown(unknown), - } + Self(value) } } impl From for u8 { fn from(value: StickerType) -> Self { - match value { - StickerType::Standard => 1, - StickerType::Guild => 2, - StickerType::Unknown(unknown) => unknown, - } + value.get() } } @@ -44,15 +56,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 8cb0d09abbb..965809d9d34 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 { @@ -589,7 +589,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), @@ -597,7 +597,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, @@ -613,7 +613,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, @@ -633,7 +633,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..759b4a456b4 100644 --- a/twilight-model/src/channel/permission_overwrite.rs +++ b/twilight-model/src/channel/permission_overwrite.rs @@ -17,34 +17,46 @@ 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 { +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); + + /// Create a new permission overwrite type from a dynamic value. + /// + /// The provided value isn't validated. Known valid values are associated + /// constants such as [`MEMBER`][`Self::MEMBER`]. + pub const fn new(permission_overwrite_type: u8) -> Self { + Self(permission_overwrite_type) + } + + /// Retrieve the value of the permission overwrite type. + /// + /// # Examples + /// + /// ``` + /// use twilight_model::channel::permission_overwrite::PermissionOverwriteType; + /// + /// assert_eq!(0, PermissionOverwriteType::ROLE.get()); + /// ``` + pub const fn get(&self) -> u8 { + self.0 + } } impl From for PermissionOverwriteType { fn from(value: u8) -> Self { - match value { - 0 => PermissionOverwriteType::Role, - 1 => PermissionOverwriteType::Member, - unknown => PermissionOverwriteType::Unknown(unknown), - } + Self(value) } } impl From for u8 { fn from(value: PermissionOverwriteType) -> Self { - match value { - PermissionOverwriteType::Member => 1, - PermissionOverwriteType::Role => 0, - PermissionOverwriteType::Unknown(unknown) => unknown, - } + value.get() } } @@ -86,7 +98,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 +116,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 +139,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 +161,9 @@ mod tests { Token::NewtypeStruct { name: "Id" }, Token::Str("1"), Token::Str("type"), + Token::NewtypeStruct { + name: "PermissionOverwriteType", + }, Token::U8(1), Token::StructEnd, ], @@ -154,8 +172,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..d4dd38e62a6 100644 --- a/twilight-model/src/channel/stage_instance/privacy_level.rs +++ b/twilight-model/src/channel/stage_instance/privacy_level.rs @@ -1,10 +1,43 @@ -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, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)] +pub struct PrivacyLevel(u8); + +impl PrivacyLevel { + pub const GUILD_ONLY: Self = Self::new(2); + + /// Create a new privacy level from a dynamic value. + /// + /// The provided value isn't validated. Known valid values are associated + /// constants such as [`GUILD_ONLY`][`Self::GUILD_ONLY`]. + pub const fn new(privacy_level: u8) -> Self { + Self(privacy_level) + } + + /// Retrieve the value of the privacy level. + /// + /// # Examples + /// + /// ``` + /// use twilight_model::channel::stage_instance::PrivacyLevel; + /// + /// assert_eq!(2, PrivacyLevel::GUILD_ONLY.get()); + /// ``` + pub const fn get(&self) -> u8 { + self.0 + } +} + +impl From for PrivacyLevel { + fn from(value: u8) -> Self { + Self(value) + } +} + +impl From for u8 { + fn from(value: PrivacyLevel) -> Self { + value.get() + } } #[cfg(test)] @@ -12,8 +45,22 @@ 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..497461176f2 100644 --- a/twilight-model/src/channel/thread/auto_archive_duration.rs +++ b/twilight-model/src/channel/thread/auto_archive_duration.rs @@ -1,69 +1,55 @@ -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, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)] +pub struct AutoArchiveDuration(u16); impl AutoArchiveDuration { - /// Retrieve the length of the duration in minutes, used by the API + pub const HOUR: Self = Self::new(60); + + 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); + + /// Create a new auto archive duration from a dynamic value. + /// + /// The provided value isn't validated. Known valid values are associated + /// constants, such as [`HOUR`][`Self::HOUR`] or [`WEEK`][`Self::WEEK`]. + pub const fn new(auto_archive_duration: u16) -> Self { + Self(auto_archive_duration) + } + + /// Retrieve the value of the auto archive duration. /// /// # Examples /// /// ``` /// use twilight_model::channel::thread::AutoArchiveDuration; /// - /// assert_eq!(60, AutoArchiveDuration::Hour.number()); + /// assert_eq!(60, AutoArchiveDuration::HOUR.get()); /// ``` - 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 fn get(&self) -> u16 { + self.0 } } 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 }, - } + Self::new(value) } } -impl From for Duration { +impl From for u16 { fn from(value: AutoArchiveDuration) -> Self { - Self::from_secs(u64::from(value.number()) * 60) + value.get() } } -impl<'de> Deserialize<'de> for AutoArchiveDuration { - fn deserialize>(deserializer: D) -> Result { - deserializer - .deserialize_u16(U16EnumVisitor::new("auto archive duration")) - .map(u16::into) - } -} - -impl Serialize for AutoArchiveDuration { - fn serialize(&self, serializer: S) -> Result { - serializer.serialize_u16(self.number()) +impl From for Duration { + fn from(value: AutoArchiveDuration) -> Self { + Self::from_secs(u64::from(value.get()) * 60) } } @@ -74,34 +60,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..6be51cd13c1 100644 --- a/twilight-model/src/channel/video_quality_mode.rs +++ b/twilight-model/src/channel/video_quality_mode.rs @@ -1,44 +1,54 @@ use serde::{Deserialize, Serialize}; #[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)] -#[non_exhaustive] -#[serde(from = "u8", into = "u8")] -pub enum VideoQualityMode { +pub struct VideoQualityMode(u8); + +impl VideoQualityMode { /// Discord chooses the quality for optimal performance. - Auto, + pub const AUTO: Self = Self::new(1); + /// 720p. - Full, - /// Variant value is unknown to the library. - Unknown(u8), + pub const FULL: Self = Self::new(2); + + pub const fn name(self) -> &'static str { + match self { + Self::AUTO => "Auto", + Self::FULL => "Full", + _ => "UNKNOWN", + } + } + + /// Create a new video quality mode from a dynamic value. + /// + /// The provided value isn't validated. Known valid values are associated + /// constants such as [`AUTO`][`Self::AUTO`]. + pub const fn new(video_quality_mode: u8) -> Self { + Self(video_quality_mode) + } + + /// Retrieve the value of the video quality mode. + /// + /// # Examples + /// + /// ``` + /// use twilight_model::channel::VideoQualityMode; + /// + /// assert_eq!(2, VideoQualityMode::FULL.get()); + /// ``` + pub const fn get(&self) -> u8 { + self.0 + } } impl From for VideoQualityMode { fn from(value: u8) -> Self { - match value { - 1 => VideoQualityMode::Auto, - 2 => VideoQualityMode::Full, - unknown => VideoQualityMode::Unknown(unknown), - } + Self(value) } } impl From for u8 { fn from(value: VideoQualityMode) -> Self { - match value { - VideoQualityMode::Auto => 1, - VideoQualityMode::Full => 2, - VideoQualityMode::Unknown(unknown) => unknown, - } - } -} - -impl VideoQualityMode { - pub const fn name(self) -> &'static str { - match self { - Self::Auto => "Auto", - Self::Full => "Full", - Self::Unknown(_) => "Unknown", - } + value.get() } } @@ -47,17 +57,23 @@ 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..25fe75ba8b3 100644 --- a/twilight-model/src/channel/webhook/kind.rs +++ b/twilight-model/src/channel/webhook/kind.rs @@ -1,41 +1,53 @@ use serde::{Deserialize, Serialize}; #[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)] -#[serde(from = "u8", into = "u8")] -pub enum WebhookType { - Incoming, - ChannelFollower, +pub struct WebhookType(u8); + +impl WebhookType { + pub const INCOMING: Self = Self::new(1); + + pub const CHANNEL_FOLLOWER: Self = Self::new(2); + /// Webhooks used with interactions. - Application, - /// Variant value is unknown to the library. - Unknown(u8), + pub const APPLICATION: Self = Self::new(3); + + /// Create a new webhook type from a dynamic value. + /// + /// The provided value isn't validated. Known valid values are associated + /// constants such as [`INCOMING`][`Self::INCOMING`]. + pub const fn new(webhook_type: u8) -> Self { + Self(webhook_type) + } + + /// Retrieve the value of the webhook type. + /// + /// # Examples + /// + /// ``` + /// use twilight_model::channel::WebhookType; + /// + /// assert_eq!(2, WebhookType::CHANNEL_FOLLOWER.get()); + /// ``` + pub const fn get(&self) -> u8 { + self.0 + } +} + +impl Default for WebhookType { + fn default() -> Self { + Self::INCOMING + } } impl From for WebhookType { fn from(value: u8) -> Self { - match value { - 1 => WebhookType::Incoming, - 2 => WebhookType::ChannelFollower, - 3 => WebhookType::Application, - unknown => WebhookType::Unknown(unknown), - } + Self(value) } } impl From for u8 { fn from(value: WebhookType) -> Self { - match value { - WebhookType::Incoming => 1, - WebhookType::ChannelFollower => 2, - WebhookType::Application => 3, - WebhookType::Unknown(unknown) => unknown, - } - } -} - -impl Default for WebhookType { - fn default() -> Self { - Self::Incoming + value.get() } } @@ -44,16 +56,26 @@ 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..f1c73e43042 100644 --- a/twilight-model/src/gateway/close_code.rs +++ b/twilight-model/src/gateway/close_code.rs @@ -1,142 +1,126 @@ -use serde_repr::{Deserialize_repr, Serialize_repr}; -use std::{ - error::Error, - fmt::{Display, Formatter, Result as FmtResult}, -}; +use serde::{Deserialize, Serialize}; +use std::fmt::{Display, Formatter, Result as FmtResult}; /// 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, Debug, 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); + + /// Create a new command type from a dynamic value. + /// + /// The provided value isn't validated. Known valid values are associated + /// constants such as [`UNKNOWN_OPCODE`][`Self::UNKNOWN_OPCODE`]. + pub const fn new(command_type: u16) -> Self { + Self(command_type) + } + + /// Retrieve the value of the command type. + /// + /// # Examples + /// + /// ``` + /// use twilight_model::gateway::CloseCode; + /// + /// assert_eq!(4014, CloseCode::DISALLOWED_INTENTS.get()); + /// ``` + pub const fn get(&self) -> u16 { + self.0 + } -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", + f.write_str(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", + _ => "UNKNOWN", }) } } -#[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 From for CloseCode { + fn from(value: u16) -> Self { + Self(value) } } -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 From for u16 { + fn from(value: CloseCode) -> Self { + value.get() } } @@ -145,67 +129,32 @@ 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 d34510108c5..068bf9bf4df 100644 --- a/twilight-model/src/gateway/event/dispatch.rs +++ b/twilight-model/src/gateway/event/dispatch.rs @@ -83,69 +83,69 @@ 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::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::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..9b1511f33e7 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,30 +233,23 @@ impl<'de> Visitor<'de> for GatewayEventVisitor<'_> { V: MapAccess<'de>, { static VALID_OPCODES: &[&str] = &[ - "EVENT", - "HEARTBEAT", - "HEARTBEAT_ACK", - "HELLO", - "IDENTIFY", - "INVALID_SESSION", - "RECONNECT", + OpCode::DISPATCH.name(), + OpCode::HEARTBEAT.name(), + OpCode::HEARTBEAT_ACK.name(), + OpCode::HELLO.name(), + OpCode::IDENTIFY.name(), + OpCode::INVALID_SESSION.name(), + OpCode::RECONNECT.name(), ]; let span = tracing::trace_span!("deserializing gateway event"); 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,25 +343,26 @@ impl<'de> Visitor<'de> for GatewayEventVisitor<'_> { GatewayEvent::InvalidateSession(invalidate) } - OpCode::Identify => return Err(DeError::unknown_variant("Identify", VALID_OPCODES)), - OpCode::Reconnect => { + OpCode::IDENTIFY => return Err(DeError::unknown_variant("Identify", VALID_OPCODES)), + OpCode::RECONNECT => { Self::ignore_all(&mut map)?; GatewayEvent::Reconnect } - OpCode::RequestGuildMembers => { + OpCode::REQUEST_GUILD_MEMBERS => { return Err(DeError::unknown_variant( "RequestGuildMembers", VALID_OPCODES, )) } - OpCode::Resume => return Err(DeError::unknown_variant("Resume", VALID_OPCODES)), - OpCode::PresenceUpdate => { + OpCode::RESUME => return Err(DeError::unknown_variant("Resume", VALID_OPCODES)), + OpCode::PRESENCE_UPDATE => { return Err(DeError::unknown_variant("PresenceUpdate", VALID_OPCODES)) } - OpCode::VoiceStateUpdate => { + OpCode::VOICE_STATE_UPDATE => { return Err(DeError::unknown_variant("VoiceStateUpdate", VALID_OPCODES)) } + _ => todo!(), }) } } @@ -394,12 +385,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 +772,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 +810,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 +833,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 +858,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 +889,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 +912,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 ebc8d1e30fe..ea9675057d8 100644 --- a/twilight-model/src/gateway/event/kind.rs +++ b/twilight-model/src/gateway/event/kind.rs @@ -1,237 +1,220 @@ +use crate::util::known_string::KnownString; use serde::{Deserialize, Serialize}; +use std::{ + fmt::{Debug, Formatter, Result as FmtResult}, + ops::Deref, + str::FromStr, +}; /// 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, - GatewayHeartbeat, - GatewayHeartbeatAck, - GatewayHello, - GatewayInvalidateSession, - GatewayReconnect, - GiftCodeUpdate, - 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 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::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::GatewayHeartbeat - | Self::GatewayHeartbeatAck - | Self::GatewayHello - | Self::GatewayInvalidateSession - | Self::GatewayReconnect => None, - } + 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_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_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"); + + /// Create a event type from a dynamic value. + /// + /// The provided event type must be 64 bytes or smaller. + pub fn new(event_type: &str) -> Option { + KnownString::from_str(event_type).map(Self) + } + + /// Get the value of the event type. + /// + /// # Panics + /// + /// Panics if the event 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 EventType { + fn as_ref(&self) -> &str { + self.get() } } -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_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 Debug for EventType { + fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult { + f.write_str(self.get()) + } +} + +impl Deref for EventType { + type Target = str; + + fn deref(&self) -> &Self::Target { + self.get() + } +} + +impl FromStr for EventType { + type Err = (); + + fn from_str(s: &str) -> Result { + Self::try_from(s) + } +} + +impl ToString for EventType { + fn to_string(&self) -> String { + KnownString::to_string(&self.0) + } +} + +impl TryFrom<&str> for EventType { + type Error = (); + + fn try_from(value: &str) -> Result { + Self::new(value).ok_or(()) } } @@ -243,10 +226,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)], ); } @@ -254,110 +234,113 @@ 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::GatewayHeartbeat, "GATEWAY_HEARTBEAT"); - assert_variant(EventType::GatewayHeartbeatAck, "GATEWAY_HEARTBEAT_ACK"); - assert_variant(EventType::GatewayHello, "GATEWAY_HELLO"); + 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::GuildCreate, "GUILD_CREATE"); - assert_variant(EventType::GuildDelete, "GUILD_DELETE"); - assert_variant(EventType::GuildEmojisUpdate, "GUILD_EMOJIS_UPDATE"); + assert_variant(EventType::GATEWAY_RECONNECT, "GATEWAY_RECONNECT"); + assert_variant(EventType::GIFT_CODE_UPDATE, "GIFT_CODE_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 0564c6183d4..8c92a854104 100644 --- a/twilight-model/src/gateway/event/mod.rs +++ b/twilight-model/src/gateway/event/mod.rs @@ -249,74 +249,74 @@ 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::GatewayHeartbeat(_) => EventType::GatewayHeartbeat, - Self::GatewayHeartbeatAck => EventType::GatewayHeartbeatAck, - Self::GatewayHello(_) => EventType::GatewayHello, - Self::GatewayInvalidateSession(_) => EventType::GatewayInvalidateSession, - Self::GatewayReconnect => EventType::GatewayReconnect, - Self::GiftCodeUpdate => EventType::GiftCodeUpdate, - 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::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::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 53e1e38a786..676f537017b 100644 --- a/twilight-model/src/gateway/mod.rs +++ b/twilight-model/src/gateway/mod.rs @@ -10,9 +10,6 @@ mod reaction; mod session_start_limit; pub use self::{ - close_code::{CloseCode, CloseCodeConversionError}, - intents::Intents, - opcode::OpCode, - reaction::GatewayReaction, + close_code::CloseCode, intents::Intents, opcode::OpCode, reaction::GatewayReaction, session_start_limit::SessionStartLimit, }; diff --git a/twilight-model/src/gateway/opcode.rs b/twilight-model/src/gateway/opcode.rs index 2d3b1b55eb7..c39dfe87cce 100644 --- a/twilight-model/src/gateway/opcode.rs +++ b/twilight-model/src/gateway/opcode.rs @@ -1,70 +1,109 @@ -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, Debug, 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 + pub const HEARTBEAT_ACK: Self = Self::new(11); + + /// Create a new opcode from a dynamic value. /// - /// [`Heartbeat`]: Self::Heartbeat - HeartbeatAck = 11, + /// The provided value isn't validated. Known valid values are associated + /// constants such as [`DISPATCH`][`Self::DISPATCH`]. + pub const fn new(opcode: u8) -> Self { + Self(opcode) + } + + /// Retrieve the value of the opcode. + /// + /// # Examples + /// + /// ``` + /// use twilight_model::gateway::OpCode; + /// + /// assert_eq!(2, OpCode::IDENTIFY.get()); + /// ``` + pub const fn get(&self) -> u8 { + self.0 + } + + pub const fn name(self) -> &'static str { + 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", + _ => "UNKNOWN", + } + } } -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, - _ => return None, - }) +impl From for OpCode { + fn from(value: u8) -> Self { + Self(value) + } +} + +impl From for u8 { + fn from(value: OpCode) -> Self { + value.get() } } @@ -88,18 +127,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 d109d4d2726..eb875f8d8d5 100644 --- a/twilight-model/src/gateway/payload/incoming/member_chunk.rs +++ b/twilight-model/src/gateway/payload/incoming/member_chunk.rs @@ -441,10 +441,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 { @@ -452,21 +452,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 8224099efd1..1098ed82a68 100644 --- a/twilight-model/src/gateway/payload/incoming/thread_members_update.rs +++ b/twilight-model/src/gateway/payload/incoming/thread_members_update.rs @@ -149,7 +149,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(), @@ -165,12 +165,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) }, }; @@ -293,6 +293,9 @@ mod tests { Token::Some, Token::Str("aaaaaaaaaaaaaaaa"), Token::Str("type"), + Token::NewtypeStruct { + name: "ActivityType", + }, Token::U8(4), Token::Str("name"), Token::Str("foo"), @@ -305,18 +308,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 362db4ec312..6f0a56ba02f 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 ed7b4d7a9a9..87069b0cdb3 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..9e7ba38fc85 100644 --- a/twilight-model/src/gateway/presence/activity_type.rs +++ b/twilight-model/src/gateway/presence/activity_type.rs @@ -1,49 +1,53 @@ 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), +pub struct ActivityType(u8); + +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); + + /// Create a new activity type from a dynamic value. + /// + /// The provided value isn't validated. Known valid values are associated + /// constants such as [`WATCHING`][`Self::WATCHING`]. + pub const fn new(activity_type: u8) -> Self { + Self(activity_type) + } + + /// Retrieve the value of the activity type. + /// + /// # Examples + /// + /// ``` + /// use twilight_model::gateway::presence::ActivityType; + /// + /// assert_eq!(2, ActivityType::LISTENING.get()); + /// ``` + pub const fn get(&self) -> u8 { + self.0 + } +} + +impl Default for ActivityType { + fn default() -> Self { + Self::PLAYING + } } 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), - } + Self(value) } } 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, - } - } -} - -impl Default for ActivityType { - fn default() -> Self { - Self::Playing + value.get() } } @@ -52,19 +56,29 @@ 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..d10fb955a52 100644 --- a/twilight-model/src/gateway/presence/status.rs +++ b/twilight-model/src/gateway/presence/status.rs @@ -1,60 +1,135 @@ +use crate::util::known_string::KnownString; use serde::{Deserialize, Serialize}; +use std::{ + fmt::{Debug, Formatter, Result as FmtResult}, + ops::Deref, + str::FromStr, +}; -#[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"); + + /// Create a status from a dynamic value. + /// + /// The provided status must be 64 bytes or smaller. + pub fn new(status: &str) -> Option { + KnownString::from_str(status).map(Self) + } + + /// Get the value of the status. + /// + /// # Panics + /// + /// Panics if the status isn't valid UTF-8. + pub fn get(&self) -> &str { + self.0.get() + } + + /// Create a status from a set of bytes. + const fn from_bytes(input: &[u8]) -> Self { + Self(KnownString::from_bytes(input)) + } +} + +impl AsRef for Status { + fn as_ref(&self) -> &str { + self.get() + } +} + +impl Debug for Status { + fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult { + f.write_str(self.get()) + } +} + +impl Deref for Status { + type Target = str; + + fn deref(&self) -> &Self::Target { + self.get() + } +} + +impl FromStr for Status { + type Err = (); + + fn from_str(s: &str) -> Result { + Self::try_from(s) + } +} + +impl ToString for Status { + fn to_string(&self) -> String { + KnownString::to_string(&self.0) + } +} + +impl TryFrom<&str> for Status { + type Error = (); + + fn try_from(value: &str) -> Result { + Self::new(value).ok_or(()) + } } #[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/audit_log/change.rs b/twilight-model/src/guild/audit_log/change.rs index e047972ef9b..5fa64cecd4b 100644 --- a/twilight-model/src/guild/audit_log/change.rs +++ b/twilight-model/src/guild/audit_log/change.rs @@ -719,92 +719,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, }) } @@ -907,7 +907,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, @@ -937,7 +937,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, diff --git a/twilight-model/src/guild/audit_log/change_key.rs b/twilight-model/src/guild/audit_log/change_key.rs index 8190036f87e..a94f452a618 100644 --- a/twilight-model/src/guild/audit_log/change_key.rs +++ b/twilight-model/src/guild/audit_log/change_key.rs @@ -1,5 +1,10 @@ +use crate::util::known_string::KnownString; use serde::{Deserialize, Serialize}; -use std::fmt::{Display, Formatter, Result as FmtResult}; +use std::{ + fmt::{Debug, Formatter, Result as FmtResult}, + ops::Deref, + str::FromStr, +}; /// Type of [`AuditLogChange`]. /// @@ -7,262 +12,295 @@ 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 + /// Create a scope from a dynamic value. /// - /// Check the names of the [`Allow`] and [`BannerHash`] keys: - /// - /// ``` - /// use twilight_model::guild::audit_log::AuditLogChangeKey; + /// The provided scope must be 64 bytes or smaller. + pub fn new(scope: &str) -> Option { + KnownString::from_str(scope).map(Self) + } + + /// Get the value of the scope. /// - /// assert_eq!("allow", AuditLogChangeKey::Allow.name()); - /// assert_eq!("banner_hash", AuditLogChangeKey::BannerHash.name()); - /// ``` + /// # Panics /// - /// [`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", - } + /// Panics if the scope isn't valid UTF-8. + pub fn get(&self) -> &str { + self.0.get() + } + + /// Create a scope from a set of bytes. + const fn from_bytes(input: &[u8]) -> Self { + Self(KnownString::from_bytes(input)) } } -impl Display for AuditLogChangeKey { +impl AsRef for AuditLogChangeKey { + fn as_ref(&self) -> &str { + self.get() + } +} + +impl Debug for AuditLogChangeKey { fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult { - f.write_str(self.name()) + f.write_str(self.get()) + } +} + +impl Deref for AuditLogChangeKey { + type Target = str; + + fn deref(&self) -> &Self::Target { + self.get() + } +} + +impl FromStr for AuditLogChangeKey { + type Err = (); + + fn from_str(s: &str) -> Result { + Self::try_from(s) + } +} + +impl ToString for AuditLogChangeKey { + fn to_string(&self) -> String { + KnownString::to_string(&self.0) + } +} + +impl TryFrom<&str> for AuditLogChangeKey { + type Error = (); + + fn try_from(value: &str) -> Result { + Self::new(value).ok_or(()) } } @@ -272,526 +310,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::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!("$add", AuditLogChangeKey::ROLE_ADDED.get()); + assert_eq!("$remove", AuditLogChangeKey::ROLE_REMOVED.get()); + assert_eq!( + "rules_channel_id", + AuditLogChangeKey::RULES_CHANNEL_ID.get() + ); + 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 e48d58ed6c7..c09bb82633e 100644 --- a/twilight-model/src/guild/audit_log/entry.rs +++ b/twilight-model/src/guild/audit_log/entry.rs @@ -69,7 +69,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), @@ -89,7 +89,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..bb8ea560cd8 100644 --- a/twilight-model/src/guild/audit_log/event_type.rs +++ b/twilight-model/src/guild/audit_log/event_type.rs @@ -4,353 +4,314 @@ use serde::{Deserialize, Serialize}; /// /// [`AuditLogEntry`]: super::AuditLogEntry #[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)] -#[serde(from = "u16", into = "u16")] -pub enum AuditLogEventType { +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); + + /// 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); + + /// A member has been timed out by Automod. + pub const AUTO_MODERATION_USER_COMMUNICATION_DISABLED: Self = Self::new(145); + + /// Create a new audit log event type from a dynamic value. + /// + /// The provided value isn't validated. Known valid values are associated + /// constants such as [`GUILD_UPDATE`][`Self::GUILD_UPDATE`]. + pub const fn new(audit_log_event_type: u16) -> Self { + Self(audit_log_event_type) + } + + /// Retrieve the value of the audit log event type. + /// + /// # Examples + /// + /// ``` + /// use twilight_model::guild::audit_log::AuditLogEventType; + /// + /// assert_eq!(40, AuditLogEventType::INVITE_CREATE.get()); + /// ``` + pub const fn get(&self) -> u16 { + self.0 + } } 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), - } + Self(value) } } 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, - } + value.get() } } @@ -376,71 +337,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..5259f2e0b7a 100644 --- a/twilight-model/src/guild/auto_moderation/action.rs +++ b/twilight-model/src/guild/auto_moderation/action.rs @@ -29,44 +29,56 @@ pub struct AutoModerationActionMetadata { /// Type of [`AutoModerationAction`]. #[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)] -#[serde(from = "u8", into = "u8")] -pub enum AutoModerationActionType { +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); + + /// Create a new auto moderation action type from a dynamic value. + /// + /// The provided value isn't validated. Known valid values are associated + /// constants such as [`BLOCK_MESSAGE`][`Self::BLOCK_MESSAGE`]. + pub const fn new(auto_moderation_action_type: u8) -> Self { + Self(auto_moderation_action_type) + } + + /// Retrieve the value of the auto moderation action type. + /// + /// # Examples + /// + /// ``` + /// use twilight_model::guild::auto_moderation::AutoModerationActionType; + /// + /// assert_eq!(3, AutoModerationActionType::TIMEOUT.get()); + /// ``` + pub const fn get(&self) -> u8 { + self.0 + } } impl From for AutoModerationActionType { fn from(value: u8) -> Self { - match value { - 1 => Self::BlockMessage, - 2 => Self::SendAlertMessage, - 3 => Self::Timeout, - _ => Self::Unknown(value), - } + Self(value) } } impl From for u8 { fn from(value: AutoModerationActionType) -> Self { - match value { - AutoModerationActionType::BlockMessage => 1, - AutoModerationActionType::SendAlertMessage => 2, - AutoModerationActionType::Timeout => 3, - AutoModerationActionType::Unknown(unknown) => unknown, - } + value.get() } } @@ -116,9 +128,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..e98ccc717da 100644 --- a/twilight-model/src/guild/auto_moderation/event_type.rs +++ b/twilight-model/src/guild/auto_moderation/event_type.rs @@ -2,29 +2,43 @@ 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 { +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); + + /// Create a new auto moderation event type from a dynamic value. + /// + /// The provided value isn't validated. Known valid values are associated + /// constants such as [`MESSAGE_SEND`][`Self::MESSAGE_SEND`]. + pub const fn new(auto_moderation_event_type: u8) -> Self { + Self(auto_moderation_event_type) + } + + /// Retrieve the value of the auto moderation event type. + /// + /// # Examples + /// + /// ``` + /// use twilight_model::guild::auto_moderation::AutoModerationEventType; + /// + /// assert_eq!(1, AutoModerationEventType::MESSAGE_SEND.get()); + /// ``` + pub const fn get(&self) -> u8 { + self.0 + } } impl From for AutoModerationEventType { fn from(value: u8) -> Self { - match value { - 1 => Self::MessageSend, - _ => Self::Unknown(value), - } + Self(value) } } impl From for u8 { fn from(value: AutoModerationEventType) -> Self { - match value { - AutoModerationEventType::MessageSend => 1, - AutoModerationEventType::Unknown(unknown) => unknown, - } + value.get() } } @@ -50,7 +64,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..e9b62ba46ec 100644 --- a/twilight-model/src/guild/auto_moderation/preset_type.rs +++ b/twilight-model/src/guild/auto_moderation/preset_type.rs @@ -2,37 +2,49 @@ 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 { +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); + + /// Create a new auto moderation keyword preset type from a dynamic value. + /// + /// The provided value isn't validated. Known valid values are associated + /// constants such as [`PROFANITY`][`Self::PROFANITY`]. + pub const fn new(auto_moderation_keyword_preset_type: u8) -> Self { + Self(auto_moderation_keyword_preset_type) + } + + /// Retrieve the value of the auto moderation keyword preset type. + /// + /// # Examples + /// + /// ``` + /// use twilight_model::guild::auto_moderation::AutoModerationKeywordPresetType; + /// + /// assert_eq!(2, AutoModerationKeywordPresetType::SEXUAL_CONTENT.get()); + /// ``` + pub const fn get(&self) -> u8 { + self.0 + } } impl From for AutoModerationKeywordPresetType { fn from(value: u8) -> Self { - match value { - 1 => Self::Profanity, - 2 => Self::SexualContent, - 3 => Self::Slurs, - _ => Self::Unknown(value), - } + Self(value) } } impl From for u8 { fn from(value: AutoModerationKeywordPresetType) -> Self { - match value { - AutoModerationKeywordPresetType::Profanity => 1, - AutoModerationKeywordPresetType::SexualContent => 2, - AutoModerationKeywordPresetType::Slurs => 3, - AutoModerationKeywordPresetType::Unknown(unknown) => unknown, - } + value.get() } } @@ -58,9 +70,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..96103adad7d 100644 --- a/twilight-model/src/guild/auto_moderation/trigger_type.rs +++ b/twilight-model/src/guild/auto_moderation/trigger_type.rs @@ -2,47 +2,58 @@ 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 { +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); + + /// Create a new auto moderation trigger type from a dynamic value. + /// + /// The provided value isn't validated. Known valid values are associated + /// constants such as [`SPAM`][`Self::SPAM`]. + pub const fn new(auto_moderation_trigger_type: u8) -> Self { + Self(auto_moderation_trigger_type) + } + + /// Retrieve the value of the auto moderation trigger type. + /// + /// # Examples + /// + /// ``` + /// use twilight_model::guild::auto_moderation::AutoModerationTriggerType; + /// + /// assert_eq!(5, AutoModerationTriggerType::MENTION_SPAM.get()); + /// ``` + pub const fn get(&self) -> u8 { + self.0 + } } 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), - } + Self(value) } } 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, - } + value.get() } } @@ -68,10 +79,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..07da9ae99a2 100644 --- a/twilight-model/src/guild/default_message_notification_level.rs +++ b/twilight-model/src/guild/default_message_notification_level.rs @@ -1,32 +1,44 @@ 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), +pub struct DefaultMessageNotificationLevel(u8); + +impl DefaultMessageNotificationLevel { + pub const ALL: Self = Self::new(0); + + pub const MENTIONS: Self = Self::new(1); + + /// Create a new command type from a dynamic value. + /// + /// The provided value isn't validated. Known valid values are associated + /// constants such as [`ALL`][`Self::ALL`]. + pub const fn new(default_message_notification_level: u8) -> Self { + Self(default_message_notification_level) + } + + /// Retrieve the value of the command type. + /// + /// # Examples + /// + /// ``` + /// use twilight_model::guild::DefaultMessageNotificationLevel; + /// + /// assert_eq!(1, DefaultMessageNotificationLevel::MENTIONS.get()); + /// ``` + pub const fn get(&self) -> u8 { + self.0 + } } impl From for DefaultMessageNotificationLevel { fn from(value: u8) -> Self { - match value { - 0 => DefaultMessageNotificationLevel::All, - 1 => DefaultMessageNotificationLevel::Mentions, - unknown => DefaultMessageNotificationLevel::Unknown(unknown), - } + Self(value) } } impl From for u8 { fn from(value: DefaultMessageNotificationLevel) -> Self { - match value { - DefaultMessageNotificationLevel::All => 0, - DefaultMessageNotificationLevel::Mentions => 1, - DefaultMessageNotificationLevel::Unknown(unknown) => unknown, - } + value.get() } } @@ -35,13 +47,25 @@ 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..98e05b1a15c 100644 --- a/twilight-model/src/guild/explicit_content_filter.rs +++ b/twilight-model/src/guild/explicit_content_filter.rs @@ -1,35 +1,44 @@ 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), +pub struct ExplicitContentFilter(u8); + +impl ExplicitContentFilter { + pub const NONE: Self = Self::new(0); + pub const MEMBERS_WITHOUT_ROLE: Self = Self::new(1); + pub const ALL_MEMBERS: Self = Self::new(2); + + /// Create a new explicit content filter from a dynamic value. + /// + /// The provided value isn't validated. Known valid values are associated + /// constants such as [`NONE`][`Self::NONE`]. + pub const fn new(explicit_content_filter: u8) -> Self { + Self(explicit_content_filter) + } + + /// Retrieve the value of the explicit content filter. + /// + /// # Examples + /// + /// ``` + /// use twilight_model::guild::ExplicitContentFilter; + /// + /// assert_eq!(2, ExplicitContentFilter::ALL_MEMBERS.get()); + /// ``` + pub const fn get(&self) -> u8 { + self.0 + } } impl From for ExplicitContentFilter { fn from(value: u8) -> Self { - match value { - 0 => ExplicitContentFilter::None, - 1 => ExplicitContentFilter::MembersWithoutRole, - 2 => ExplicitContentFilter::AllMembers, - unknown => ExplicitContentFilter::Unknown(unknown), - } + Self(value) } } impl From for u8 { fn from(value: ExplicitContentFilter) -> Self { - match value { - ExplicitContentFilter::None => 0, - ExplicitContentFilter::MembersWithoutRole => 1, - ExplicitContentFilter::AllMembers => 2, - ExplicitContentFilter::Unknown(unknown) => unknown, - } + value.get() } } @@ -38,11 +47,26 @@ 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 4737cd4f640..ff3148019b7 100644 --- a/twilight-model/src/guild/feature.rs +++ b/twilight-model/src/guild/feature.rs @@ -1,201 +1,244 @@ -#![allow(deprecated)] -use std::borrow::Cow; - +use crate::util::known_string::KnownString; use serde::{Deserialize, Serialize}; +use std::{ + fmt::{Debug, Formatter, Result as FmtResult}, + ops::Deref, + str::FromStr, +}; /// Special and optional guild features. /// /// 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, + 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. - 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, + 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"); + + /// Create a guild feature from a dynamic value. + /// + /// The provided guild feature must be 64 bytes or smaller. + pub fn new(guild_feature: &str) -> Option { + KnownString::from_str(guild_feature).map(Self) + } + + /// Get the value of the guild feature. + /// + /// # Panics + /// + /// Panics if the guild feature isn't valid UTF-8. + pub fn get(&self) -> &str { + self.0.get() + } + + /// Create a guild feature from a set of bytes. + const fn from_bytes(input: &[u8]) -> Self { + Self(KnownString::from_bytes(input)) + } } -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::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::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(), - } +impl AsRef for GuildFeature { + fn as_ref(&self) -> &str { + self.get() } } -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, - "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, - "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 Debug for GuildFeature { + fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult { + f.write_str(self.get()) + } +} + +impl Deref for GuildFeature { + type Target = str; + + fn deref(&self) -> &Self::Target { + self.get() + } +} + +impl FromStr for GuildFeature { + type Err = (); + + fn from_str(s: &str) -> Result { + Self::try_from(s) + } +} + +impl ToString for GuildFeature { + fn to_string(&self) -> String { + KnownString::to_string(&self.0) + } +} + +impl TryFrom<&str> for GuildFeature { + type Error = (); + + fn try_from(value: &str) -> Result { + Self::new(value).ok_or(()) } } #[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::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::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 8f73ff31867..5af7481e1d7 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]: https://discord.com/developers/docs/topics/oauth2#shared-resources-oauth2-scopes + /// [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,6 +61,7 @@ mod tests { use crate::{ guild::GuildIntegrationType, id::Id, + oauth::Scope, test::image_hash, util::datetime::{Timestamp, TimestampParseError}, }; @@ -79,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([ - "applications.commands".to_owned(), - "bot".to_owned(), - ])), + scopes: Some(Vec::from([Scope::APPLICATIONS_COMMANDS, Scope::BOT])), subscriber_count: Some(1337), synced_at: Some(synced_at), syncing: Some(false), @@ -138,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, @@ -146,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"), @@ -159,7 +164,9 @@ mod tests { Token::Str("scopes"), Token::Some, Token::Seq { len: Some(2) }, + Token::NewtypeStruct { name: "Scope" }, Token::Str("applications.commands"), + Token::NewtypeStruct { name: "Scope" }, Token::Str("bot"), Token::SeqEnd, Token::Str("subscriber_count"), @@ -220,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([ - "applications.commands".to_owned(), - "bot".to_owned(), - ])), + scopes: Some(Vec::from([Scope::APPLICATIONS_COMMANDS, Scope::BOT])), subscriber_count: Some(1337), synced_at: Some(synced_at), syncing: Some(false), @@ -294,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, @@ -302,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"), @@ -315,7 +325,9 @@ mod tests { Token::Str("scopes"), Token::Some, Token::Seq { len: Some(2) }, + Token::NewtypeStruct { name: "Scope" }, Token::Str("applications.commands"), + Token::NewtypeStruct { name: "Scope" }, Token::Str("bot"), Token::SeqEnd, Token::Str("subscriber_count"), diff --git a/twilight-model/src/guild/integration_expire_behavior.rs b/twilight-model/src/guild/integration_expire_behavior.rs index e13b69068e9..5351b668d26 100644 --- a/twilight-model/src/guild/integration_expire_behavior.rs +++ b/twilight-model/src/guild/integration_expire_behavior.rs @@ -2,34 +2,46 @@ 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 { +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); + + /// Create a new integration expire behavior from a dynamic value. + /// + /// The provided value isn't validated. Known valid values are associated + /// constants such as [`REMOVE_ROLE`][`Self::REMOVE_ROLE`]. + pub const fn new(integration_expire_behavior: u8) -> Self { + Self(integration_expire_behavior) + } + + /// Retrieve the value of the integration expire behavior. + /// + /// # Examples + /// + /// ``` + /// use twilight_model::guild::IntegrationExpireBehavior; + /// + /// assert_eq!(1, IntegrationExpireBehavior::KICK.get()); + /// ``` + pub const fn get(&self) -> u8 { + self.0 + } } impl From for IntegrationExpireBehavior { fn from(value: u8) -> Self { - match value { - 0 => IntegrationExpireBehavior::RemoveRole, - 1 => IntegrationExpireBehavior::Kick, - unknown => IntegrationExpireBehavior::Unknown(unknown), - } + Self(value) } } impl From for u8 { fn from(value: IntegrationExpireBehavior) -> Self { - match value { - IntegrationExpireBehavior::RemoveRole => 0, - IntegrationExpireBehavior::Kick => 1, - IntegrationExpireBehavior::Unknown(unknown) => unknown, - } + value.get() } } @@ -38,10 +50,25 @@ 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..b9e4cf2c2cc 100644 --- a/twilight-model/src/guild/integration_type.rs +++ b/twilight-model/src/guild/integration_type.rs @@ -1,64 +1,114 @@ -#![allow(deprecated)] -use std::borrow::Cow; - +use crate::util::known_string::KnownString; use serde::{Deserialize, Serialize}; +use std::{ + fmt::{Debug, Formatter, Result as FmtResult}, + ops::Deref, + str::FromStr, +}; /// Special and optional guild features. /// /// 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"); + + /// Integration is a Youtube connection. + pub const YOUTUBE: Self = Self::from_bytes(b"youtube"); + + /// Create a guild integration type from a dynamic value. + /// + /// The provided guild integration type must be 64 bytes or smaller. + pub fn new(guild_integration_type: &str) -> Option { + KnownString::from_str(guild_integration_type).map(Self) + } + + /// Get the value of the guild integration type. + /// + /// # Panics + /// + /// Panics if the guild integration type isn't valid UTF-8. + pub fn get(&self) -> &str { + self.0.get() + } + + /// Create a guild integration type from a set of bytes. + const fn from_bytes(input: &[u8]) -> Self { + Self(KnownString::from_bytes(input)) + } } -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(), - } +impl AsRef for GuildIntegrationType { + fn as_ref(&self) -> &str { + self.get() } } -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), - } +impl Debug for GuildIntegrationType { + fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult { + f.write_str(self.get()) + } +} + +impl Deref for GuildIntegrationType { + type Target = str; + + fn deref(&self) -> &Self::Target { + self.get() + } +} + +impl FromStr for GuildIntegrationType { + type Err = (); + + fn from_str(s: &str) -> Result { + Self::try_from(s) + } +} + +impl ToString for GuildIntegrationType { + fn to_string(&self) -> String { + KnownString::to_string(&self.0) + } +} + +impl TryFrom<&str> for GuildIntegrationType { + type Error = (); + + fn try_from(value: &str) -> Result { + Self::new(value).ok_or(()) } } #[cfg(test)] mod tests { use super::GuildIntegrationType; - use serde_test::Token; - - #[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)]); - } - } + use serde::{Deserialize, Serialize}; + use static_assertions::assert_impl_all; + use std::{fmt::Debug, hash::Hash, str::FromStr, string::ToString}; + + 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 4f1e5a2a706..c1057ede4bc 100644 --- a/twilight-model/src/guild/invite/guild.rs +++ b/twilight-model/src/guild/invite/guild.rs @@ -57,7 +57,7 @@ mod tests { 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![ @@ -112,6 +112,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 1df73c0eef6..3716bbeb56b 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(), @@ -174,7 +178,7 @@ mod tests { 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"), @@ -300,6 +307,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 +387,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..fd5f9f317b6 100644 --- a/twilight-model/src/guild/invite/target_type.rs +++ b/twilight-model/src/guild/invite/target_type.rs @@ -1,31 +1,44 @@ 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), +pub struct TargetType(u8); + +impl TargetType { + pub const STREAM: Self = Self::new(1); + + pub const EMBEDDED_APPLICATION: Self = Self::new(2); + + /// Create a new command type from a dynamic value. + /// + /// The provided value isn't validated. Known valid values are associated + /// constants such as [`STREAM`][`Self::STREAM`]. + pub const fn new(target_type: u8) -> Self { + Self(target_type) + } + + /// Retrieve the value of the command type. + /// + /// # Examples + /// + /// ``` + /// use twilight_model::guild::invite::TargetType; + /// + /// assert_eq!(2, TargetType::EMBEDDED_APPLICATION.get()); + /// ``` + pub const fn get(&self) -> u8 { + self.0 + } } impl From for TargetType { fn from(value: u8) -> Self { - match value { - 1 => TargetType::Stream, - 2 => TargetType::EmbeddedApplication, - unknown => TargetType::Unknown(unknown), - } + Self(value) } } impl From for u8 { fn from(value: TargetType) -> Self { - match value { - TargetType::Stream => 1, - TargetType::EmbeddedApplication => 2, - TargetType::Unknown(unknown) => unknown, - } + value.get() } } @@ -34,10 +47,20 @@ 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..e993058a969 100644 --- a/twilight-model/src/guild/mfa_level.rs +++ b/twilight-model/src/guild/mfa_level.rs @@ -1,32 +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), +pub struct MfaLevel(u8); + +impl MfaLevel { + pub const NONE: Self = Self::new(0); + pub const ELEVATED: Self = Self::new(1); + + /// Create a new MFA Level from a dynamic value. + /// + /// The provided value isn't validated. Known valid values are associated + /// constants such as [`ELEVATED`][`Self::ELEVATED`]. + pub const fn new(mfa_level: u8) -> Self { + Self(mfa_level) + } + + /// Retrieve the value of the MFA Level. + /// + /// # Examples + /// + /// ``` + /// use twilight_model::guild::MfaLevel; + /// + /// assert_eq!(1, MfaLevel::ELEVATED.get()); + /// ``` + pub const fn get(&self) -> u8 { + self.0 + } } impl From for MfaLevel { fn from(value: u8) -> Self { - match value { - 0 => MfaLevel::None, - 1 => MfaLevel::Elevated, - unknown => MfaLevel::Unknown(unknown), - } + Self(value) } } impl From for u8 { fn from(value: MfaLevel) -> Self { - match value { - MfaLevel::None => 0, - MfaLevel::Elevated => 1, - MfaLevel::Unknown(unknown) => unknown, - } + value.get() } } @@ -35,10 +46,17 @@ 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 69dded039f4..46d163921f0 100644 --- a/twilight-model/src/guild/mod.rs +++ b/twilight-model/src/guild/mod.rs @@ -894,12 +894,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), @@ -909,16 +909,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(), roles: Vec::new(), rules_channel_id: Some(Id::new(6)), @@ -930,7 +930,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), @@ -967,6 +967,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, @@ -978,9 +981,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"), @@ -1010,10 +1019,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" }, @@ -1032,6 +1043,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) }, @@ -1061,6 +1075,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..28b8750d765 100644 --- a/twilight-model/src/guild/nsfw_level.rs +++ b/twilight-model/src/guild/nsfw_level.rs @@ -1,37 +1,45 @@ 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), +pub struct NSFWLevel(u8); + +impl NSFWLevel { + pub const DEFAULT: Self = Self::new(0); + pub const EXPLICIT: Self = Self::new(1); + pub const SAFE: Self = Self::new(2); + pub const AGE_RESTRICTED: Self = Self::new(3); + + /// Create a new NSFW Level from a dynamic value. + /// + /// The provided value isn't validated. Known valid values are associated + /// constants such as [`EXPLICIT`][`Self::EXPLICIT`]. + pub const fn new(connection_visibility: u8) -> Self { + Self(connection_visibility) + } + + /// Retrieve the value of the NSFW Level. + /// + /// # Examples + /// + /// ``` + /// use twilight_model::guild::NSFWLevel; + /// + /// assert_eq!(2, NSFWLevel::SAFE.get()); + /// ``` + pub const fn get(&self) -> u8 { + self.0 + } } 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), - } + Self(value) } } 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, - } + value.get() } } @@ -40,12 +48,22 @@ 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 e554de490ce..cd2a89eeb25 100644 --- a/twilight-model/src/guild/partial_guild.rs +++ b/twilight-model/src/guild/partial_guild.rs @@ -80,33 +80,33 @@ 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, 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), @@ -134,6 +134,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, @@ -145,9 +148,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"), @@ -166,10 +175,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" }, @@ -188,6 +199,9 @@ mod tests { Token::Some, Token::U64(3), Token::Str("premium_tier"), + Token::NewtypeStruct { + name: "PremiumTier", + }, Token::U8(1), Token::Str("roles"), Token::Seq { len: Some(0) }, @@ -206,6 +220,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..9009ddd529a 100644 --- a/twilight-model/src/guild/premium_tier.rs +++ b/twilight-model/src/guild/premium_tier.rs @@ -1,43 +1,51 @@ 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), +pub struct PremiumTier(u8); + +impl PremiumTier { + pub const NONE: Self = Self::new(0); + pub const TIER_1: Self = Self::new(1); + pub const TIER_2: Self = Self::new(2); + pub const TIER_3: Self = Self::new(3); + + /// Create a new premium tier from a dynamic value. + /// + /// The provided value isn't validated. Known valid values are associated + /// constants such as [`TIER_!`][`Self::TIER_1`]. + pub const fn new(premium_tier: u8) -> Self { + Self(premium_tier) + } + + /// Retrieve the value of the premium tier. + /// + /// # Examples + /// + /// ``` + /// use twilight_model::guild::PremiumTier; + /// + /// assert_eq!(2, PremiumTier::TIER_2.get()); + /// ``` + pub const fn get(&self) -> u8 { + self.0 + } +} + +impl Default for PremiumTier { + fn default() -> Self { + Self::NONE + } } 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), - } + Self(value) } } 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, - } - } -} - -impl Default for PremiumTier { - fn default() -> Self { - Self::None + value.get() } } @@ -46,12 +54,27 @@ 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..68061e49926 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,118 +84,152 @@ 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 { +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); + + /// Create a new scheduled event entity type from a dynamic value. + /// + /// The provided value isn't validated. Known valid values are associated + /// constants such as [`STAGE_INSTANCE`][`Self::STAGE_INSTANCE`]. + pub const fn new(entity_type: u8) -> Self { + Self(entity_type) + } + + /// Retrieve the value of the scheduled event entity type. + /// + /// # Examples + /// + /// ``` + /// use twilight_model::guild::scheduled_event::EntityType; + /// + /// assert_eq!(2, EntityType::VOICE.get()); + /// ``` + pub const fn get(&self) -> u8 { + self.0 + } } impl From for EntityType { fn from(value: u8) -> Self { - match value { - 1 => EntityType::StageInstance, - 2 => EntityType::Voice, - 3 => EntityType::External, - unknown => EntityType::Unknown(unknown), - } + Self(value) } } impl From for u8 { fn from(value: EntityType) -> Self { - match value { - EntityType::StageInstance => 1, - EntityType::Voice => 2, - EntityType::External => 3, - EntityType::Unknown(unknown) => unknown, - } + value.get() } } /// Privacy level of an event. #[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)] -#[non_exhaustive] -#[serde(from = "u8", into = "u8")] -pub enum PrivacyLevel { +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); + + /// Create a new privacy level from a dynamic value. + /// + /// The provided value isn't validated. Known valid values are associated + /// constants such as [`GUILD_ONLY`][`Self::GUILD_ONLY`]. + pub const fn new(privacy_level: u8) -> Self { + Self(privacy_level) + } + + /// Retrieve the value of the privacy level. + /// + /// # Examples + /// + /// ``` + /// use twilight_model::guild::scheduled_event::PrivacyLevel; + /// + /// assert_eq!(2, PrivacyLevel::GUILD_ONLY.get()); + /// ``` + pub const fn get(&self) -> u8 { + self.0 + } } impl From for PrivacyLevel { fn from(value: u8) -> Self { - match value { - 2 => PrivacyLevel::GuildOnly, - unknown => PrivacyLevel::Unknown(unknown), - } + Self(value) } } impl From for u8 { fn from(value: PrivacyLevel) -> Self { - match value { - PrivacyLevel::GuildOnly => 2, - PrivacyLevel::Unknown(unknown) => unknown, - } + value.get() } } /// Status of an event. #[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)] -#[non_exhaustive] -#[serde(from = "u8", into = "u8")] -pub enum Status { +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); + + /// Create a new scheduled event status from a dynamic value. + /// + /// The provided value isn't validated. Known valid values are associated + /// constants such as [`SCHEDULED`][`Self::SCHEDULED`]. + pub const fn new(scheduled_event_status: u8) -> Self { + Self(scheduled_event_status) + } + + /// Retrieve the value of the scheduled event status. + /// + /// # Examples + /// + /// ``` + /// use twilight_model::guild::scheduled_event::Status; + /// + /// assert_eq!(2, Status::ACTIVE.get()); + /// ``` + pub const fn get(&self) -> u8 { + self.0 + } } 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), - } + Self(value) } } 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, - } + value.get() } } @@ -217,15 +251,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 +282,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 +296,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..1721a666e68 100644 --- a/twilight-model/src/guild/verification_level.rs +++ b/twilight-model/src/guild/verification_level.rs @@ -1,41 +1,46 @@ 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), +pub struct VerificationLevel(u8); + +impl VerificationLevel { + pub const NONE: Self = Self::new(0); + 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); + + /// Create a new verification level from a dynamic value. + /// + /// The provided value isn't validated. Known valid values are associated + /// constants such as [`MEDIUM`][`Self::MEDIUM`]. + pub const fn new(verification_level: u8) -> Self { + Self(verification_level) + } + + /// Retrieve the value of the verification level. + /// + /// # Examples + /// + /// ``` + /// use twilight_model::guild::VerificationLevel; + /// + /// assert_eq!(1, VerificationLevel::LOW.get()); + /// ``` + pub const fn get(&self) -> u8 { + self.0 + } } 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), - } + Self(value) } } 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, - } + value.get() } } @@ -44,13 +49,28 @@ 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..2a4e1000cbf 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,30 +75,69 @@ 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, Debug, 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); + + /// Create a new interaction response type from a dynamic value. + /// + /// The provided value isn't validated. Known valid values are associated + /// constants such as [`UPDATE_MESSAGE`][`Self::UPDATE_MESSAGE`]. + pub const fn new(interaction_response_type: u8) -> Self { + Self(interaction_response_type) + } + + /// Retrieve the value of the interaction response type. + /// + /// # Examples + /// + /// ``` + /// use twilight_model::http::interaction::InteractionResponseType; + /// + /// assert_eq!(9, InteractionResponseType::MODAL.get()); + /// ``` + pub const fn get(&self) -> u8 { + self.0 + } +} + +impl From for InteractionResponseType { + fn from(value: u8) -> Self { + Self(value) + } +} + +impl From for u8 { + fn from(value: InteractionResponseType) -> Self { + value.get() + } } #[cfg(test)] @@ -138,7 +176,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 +199,9 @@ mod tests { len: 2, }, Token::Str("type"), + Token::NewtypeStruct { + name: "InteractionResponseType", + }, Token::U8(4), Token::Str("data"), Token::Some, @@ -183,7 +224,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 +244,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..464d074e609 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,15 +19,48 @@ 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, Debug, 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); + + /// Create a new permission overwrite from a dynamic value. + /// + /// The provided value isn't validated. Known valid values are associated + /// constants such as [`MEMBER`][`Self::MEMBER`]. + pub const fn new(permission_overwrite: u8) -> Self { + Self(permission_overwrite) + } + + /// Retrieve the value of the permission overwrite. + /// + /// # Examples + /// + /// ``` + /// use twilight_model::http::permission_overwrite::PermissionOverwriteType; + /// + /// assert_eq!(0, PermissionOverwriteType::ROLE.get()); + /// ``` + pub const fn get(&self) -> u8 { + self.0 + } +} + +impl From for PermissionOverwriteType { + fn from(value: u8) -> Self { + Self(value) + } +} + +impl From for u8 { + fn from(value: PermissionOverwriteType) -> Self { + value.get() + } } #[cfg(test)] @@ -37,7 +69,7 @@ mod tests { 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 +94,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 +101,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 +121,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 +144,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 +168,9 @@ mod tests { Token::NewtypeStruct { name: "Id" }, Token::Str("1"), Token::Str("type"), + Token::NewtypeStruct { + name: "PermissionOverwriteType", + }, Token::U8(1), Token::StructEnd, ], @@ -142,8 +178,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/oauth/install_params.rs b/twilight-model/src/oauth/install_params.rs index 1bf8fe6a966..81401e53f28 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}; @@ -10,14 +11,16 @@ use serde::{Deserialize, Serialize}; pub struct InstallParams { /// Permissions to request for the bot role. pub permissions: Permissions, - /// Scopes to add the application to the guild with. - pub scopes: Vec, + /// List of [scopes] to add the application to the guild with. + /// + /// [scopes]: crate::oauth::Scope + pub scopes: Vec, } #[cfg(test)] mod tests { use super::InstallParams; - use crate::guild::Permissions; + use crate::{guild::Permissions, oauth::Scope}; use serde::{Deserialize, Serialize}; use serde_test::Token; use static_assertions::assert_impl_all; @@ -39,9 +42,9 @@ mod tests { let value = InstallParams { permissions: Permissions::empty(), scopes: Vec::from([ - "applications.commands".to_owned(), - "applications.commands.permissions.update".to_owned(), - "identify".to_owned(), + Scope::APPLICATIONS_COMMANDS, + Scope::APPLICATIONS_COMMANDS_PERMISSIONS_UPDATE, + Scope::IDENTIFY, ]), }; @@ -56,8 +59,11 @@ mod tests { Token::String("0"), Token::String("scopes"), Token::Seq { len: Some(3) }, + Token::NewtypeStruct { name: "Scope" }, Token::String("applications.commands"), + Token::NewtypeStruct { name: "Scope" }, Token::String("applications.commands.permissions.update"), + Token::NewtypeStruct { name: "Scope" }, Token::String("identify"), Token::SeqEnd, Token::StructEnd, diff --git a/twilight-model/src/oauth/mod.rs b/twilight-model/src/oauth/mod.rs index a4a891fad49..d806a553fc1 100644 --- a/twilight-model/src/oauth/mod.rs +++ b/twilight-model/src/oauth/mod.rs @@ -12,10 +12,11 @@ mod application; mod application_flags; mod install_params; mod partial_application; +mod scope; pub use self::{ application::Application, application_flags::ApplicationFlags, install_params::InstallParams, - partial_application::PartialApplication, + partial_application::PartialApplication, scope::Scope, }; #[allow(deprecated)] diff --git a/twilight-model/src/oauth/scope.rs b/twilight-model/src/oauth/scope.rs new file mode 100644 index 00000000000..d85e6d6b9a0 --- /dev/null +++ b/twilight-model/src/oauth/scope.rs @@ -0,0 +1,250 @@ +//! Known list of available OAuth 2 scopes. +//! +//! Refer to [Discord Docs/OAuth 2 Scopes] for a complete up-to-date list. +//! +//! [Discord Docs/OAuth 2 Scopes]: https://discord.com/developers/docs/topics/oauth2#shared-resources-oauth2-scopes + +use crate::util::known_string::KnownString; +use serde::{Deserialize, Serialize}; +use std::{ + fmt::{Debug, Formatter, Result as FmtResult}, + ops::Deref, + str::FromStr, +}; + +/// OAuth 2 scope. +/// +/// # Examples +/// +/// Match a requested scope and print what's being requested: +/// +/// ```no_run +/// use twilight_model::oauth::Scope; +/// +/// let scope = Scope::IDENTIFY; +/// +/// 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"); + + /// Create a scope from a dynamic value. + /// + /// The provided scope must be 64 bytes or smaller. + pub fn new(scope: &str) -> Option { + KnownString::from_str(scope).map(Self) + } + + /// Get the value of the scope. + /// + /// # Panics + /// + /// Panics if the scope isn't valid UTF-8. + pub fn get(&self) -> &str { + self.0.get() + } + + /// Create a scope from a set of bytes. + const fn from_bytes(input: &[u8]) -> Self { + Self(KnownString::from_bytes(input)) + } +} + +impl AsRef for Scope { + fn as_ref(&self) -> &str { + self.get() + } +} + +impl Debug for Scope { + fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult { + f.write_str(self.get()) + } +} + +impl Deref for Scope { + type Target = str; + + fn deref(&self) -> &Self::Target { + self.get() + } +} + +impl FromStr for Scope { + type Err = (); + + fn from_str(s: &str) -> Result { + Self::try_from(s) + } +} + +impl ToString for Scope { + fn to_string(&self) -> String { + KnownString::to_string(&self.0) + } +} + +impl TryFrom<&str> for Scope { + type Error = (); + + fn try_from(value: &str) -> Result { + Self::new(value).ok_or(()) + } +} + +#[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..2441f12312b 100644 --- a/twilight-model/src/oauth/team/membership_state.rs +++ b/twilight-model/src/oauth/team/membership_state.rs @@ -1,32 +1,43 @@ 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), +pub struct TeamMembershipState(u8); + +impl TeamMembershipState { + pub const INVITED: Self = Self::new(1); + pub const ACCEPTED: Self = Self::new(2); + + /// Create a new membership state from a dynamic value. + /// + /// The provided value isn't validated. Known valid values are associated + /// constants such as [`ACCEPTED`][`Self::ACCEPTED`]. + pub const fn new(membership_state: u8) -> Self { + Self(membership_state) + } + + /// Retrieve the value of the membership state. + /// + /// # Examples + /// + /// ``` + /// use twilight_model::oauth::team::TeamMembershipState; + /// + /// assert_eq!(1, TeamMembershipState::INVITED.get()); + /// ``` + pub const fn get(&self) -> u8 { + self.0 + } } impl From for TeamMembershipState { fn from(value: u8) -> Self { - match value { - 1 => TeamMembershipState::Invited, - 2 => TeamMembershipState::Accepted, - unknown => TeamMembershipState::Unknown(unknown), - } + Self(value) } } impl From for u8 { fn from(value: TeamMembershipState) -> Self { - match value { - TeamMembershipState::Invited => 1, - TeamMembershipState::Accepted => 2, - TeamMembershipState::Unknown(unknown) => unknown, - } + value.get() } } @@ -35,10 +46,25 @@ 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..995d444ab49 100644 --- a/twilight-model/src/user/connection_visibility.rs +++ b/twilight-model/src/user/connection_visibility.rs @@ -1,31 +1,46 @@ 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), +pub struct ConnectionVisibility(u8); + +impl ConnectionVisibility { + /// Connection isn't visible to anyone. + pub const NONE: Self = Self::new(0); + + /// Connection is visible to everyone. + pub const EVERYONE: Self = Self::new(1); + + /// Create a new connection visibility from a dynamic value. + /// + /// The provided value isn't validated. Known valid values are associated + /// constants such as [`EVERYONE`][`Self::EVERYONE`]. + pub const fn new(connection_visibility: u8) -> Self { + Self(connection_visibility) + } + + /// Retrieve the value of the connection visibility. + /// + /// # Examples + /// + /// ``` + /// use twilight_model::user::ConnectionVisibility; + /// + /// assert_eq!(1, ConnectionVisibility::EVERYONE.get()); + /// ``` + pub const fn get(&self) -> u8 { + self.0 + } } impl From for ConnectionVisibility { fn from(value: u8) -> Self { - match value { - 0 => ConnectionVisibility::None, - 1 => ConnectionVisibility::Everyone, - unknown => ConnectionVisibility::Unknown(unknown), - } + Self(value) } } impl From for u8 { fn from(value: ConnectionVisibility) -> Self { - match value { - ConnectionVisibility::None => 0, - ConnectionVisibility::Everyone => 1, - ConnectionVisibility::Unknown(unknown) => unknown, - } + value.get() } } @@ -34,10 +49,25 @@ 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..dcf146c8d91 100644 --- a/twilight-model/src/user/premium_type.rs +++ b/twilight-model/src/user/premium_type.rs @@ -4,42 +4,52 @@ use serde::{Deserialize, Serialize}; /// /// [`User`]: super::User #[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)] -#[non_exhaustive] -#[serde(from = "u8", into = "u8")] -pub enum PremiumType { +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); + + /// Create a new premium type from a dynamic value. + /// + /// The provided value isn't validated. Known valid values are associated + /// constants such as [`NITRO`][`Self::NITRO`]. + pub const fn new(premium_type: u8) -> Self { + Self(premium_type) + } + + /// Retrieve the value of the premium type. + /// + /// # Examples + /// + /// ``` + /// use twilight_model::user::PremiumType; + /// + /// assert_eq!(2, PremiumType::NITRO.get()); + /// ``` + pub const fn get(&self) -> u8 { + self.0 + } } 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), - } + Self(value) } } 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, - } + value.get() } } @@ -64,12 +74,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..3e3ed1126ad --- /dev/null +++ b/twilight-model/src/util/known_string.rs @@ -0,0 +1,296 @@ +//! 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 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 unobvious: it's because `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: +/// +/// ``` +/// use serde::{Deserialize, Serialize}; +/// +/// #[derive(Clone, Copy, Debug, 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 (subject to the length of the bytes array), 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 { + let string = str::from_utf8(&self.bytes).unwrap(); + + string.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; + use std::{fmt::Debug, hash::Hash, 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>, + ); + + #[test] + fn new() { + let string = KnownString::<64>::from_str("BOT").unwrap(); + + let mut with_null_bytes = [0; 64]; + with_null_bytes[0] = b'B'; + with_null_bytes[1] = b'O'; + with_null_bytes[2] = b'T'; + assert_eq!(&string.bytes, &with_null_bytes); + } + + #[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 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") + ); + } +} diff --git a/twilight-model/src/util/mod.rs b/twilight-model/src/util/mod.rs index b1d0a6e9f5f..4290f835a46 100644 --- a/twilight-model/src/util/mod.rs +++ b/twilight-model/src/util/mod.rs @@ -3,6 +3,8 @@ 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)] diff --git a/twilight-model/src/visitor.rs b/twilight-model/src/visitor.rs index eed27b8ba73..5a0296e2c61 100644 --- a/twilight-model/src/visitor.rs +++ b/twilight-model/src/visitor.rs @@ -1,10 +1,3 @@ -use serde::de::{Error as DeError, Visitor}; -use std::{ - convert::TryFrom, - 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 @@ -51,35 +44,3 @@ pub mod null_boolean { deserializer.deserialize_option(NullBooleanVisitor) } } - -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..e280ae03f3e 100644 --- a/twilight-model/src/voice/close_code.rs +++ b/twilight-model/src/voice/close_code.rs @@ -1,34 +1,78 @@ -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, Debug, 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); + + /// Create a new close code from a dynamic value. + /// + /// The provided value isn't validated. Known valid values are associated + /// constants such as [`DISCONNECTED`][`Self::DISCONNECTED`]. + pub const fn new(close_code: u16) -> Self { + Self(close_code) + } + + /// Retrieve the value of the close code. + /// + /// # Examples + /// + /// ``` + /// use twilight_model::voice::CloseCode; + /// + /// assert_eq!(4002, CloseCode::DECODE_ERROR.get()); + /// ``` + pub const fn get(&self) -> u16 { + self.0 + } +} + +impl From for CloseCode { + fn from(value: u16) -> Self { + Self(value) + } +} + +impl From for u16 { + fn from(value: CloseCode) -> Self { + value.get() + } } #[cfg(test)] @@ -36,18 +80,30 @@ 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..679d0540e73 100644 --- a/twilight-model/src/voice/opcode.rs +++ b/twilight-model/src/voice/opcode.rs @@ -1,32 +1,75 @@ -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, Debug, 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); + + /// Create a new opcode from a dynamic value. + /// + /// The provided value isn't validated. Known valid values are associated + /// constants such as [`IDENTIFY`][`Self::IDENTIFY`]. + pub const fn new(opcode: u8) -> Self { + Self(opcode) + } + + /// Retrieve the value of the opcode. + /// + /// # Examples + /// + /// ``` + /// use twilight_model::voice::OpCode; + /// + /// assert_eq!(5, OpCode::SPEAKING.get()); + /// ``` + pub const fn get(&self) -> u8 { + self.0 + } +} + +impl From for OpCode { + fn from(value: u8) -> Self { + Self(value) + } +} + +impl From for u8 { + fn from(value: OpCode) -> Self { + value.get() + } } #[cfg(test)] @@ -34,18 +77,28 @@ 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 07b31dcd186..3d7dc7ffc79 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, @@ -199,7 +199,7 @@ impl Standby { /// /// # Examples /// - /// Wait for a [`BanAdd`] event in guild 123: + /// Wait for a [`BAN_ADD`] event in guild 123: /// /// ```no_run /// # #[tokio::main] @@ -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(()) } /// ``` @@ -226,7 +226,7 @@ impl Standby { /// The returned future resolves to a [`Canceled`] error if the associated /// [`Standby`] instance is dropped. /// - /// [`BanAdd`]: twilight_model::gateway::payload::incoming::BanAdd + /// [`BAN_ADD`]: twilight_model::gateway::payload::incoming::BAN_ADD /// [`Canceled`]: future::Canceled /// [`wait_for_stream`]: Self::wait_for_stream pub fn wait_for bool + Send + Sync + 'static>( @@ -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, @@ -1142,14 +1142,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()), @@ -1358,7 +1358,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()); @@ -1480,7 +1480,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); @@ -1498,17 +1498,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), @@ -1517,7 +1518,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 e71f9b71437..a08dd9bd933 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, @@ -625,7 +625,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, @@ -709,7 +709,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, @@ -872,7 +872,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, @@ -956,7 +956,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, @@ -1122,7 +1122,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, @@ -1214,7 +1214,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, @@ -1298,7 +1298,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, @@ -1416,7 +1416,7 @@ mod tests { CommandBuilder::new( "permissions", "Get or edit permissions for a user or a role", - CommandType::ChatInput, + CommandType::CHAT_INPUT, ) .nsfw(true) .option( @@ -1466,7 +1466,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), @@ -1478,7 +1478,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, @@ -1492,7 +1492,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, @@ -1506,7 +1506,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, @@ -1525,7 +1525,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, @@ -1544,7 +1544,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, @@ -1558,7 +1558,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, @@ -1577,7 +1577,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, @@ -1599,7 +1599,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, @@ -1613,7 +1613,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, @@ -1627,7 +1627,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, @@ -1646,7 +1646,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, @@ -1665,7 +1665,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, @@ -1679,7 +1679,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, @@ -1698,7 +1698,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, @@ -1723,7 +1723,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 f9e1a279a05..5ba892831bb 100644 --- a/twilight-validate/src/channel.rs +++ b/twilight-validate/src/channel.rs @@ -138,7 +138,7 @@ pub const fn bitrate(value: u32) -> Result<(), ChannelValidationError> { 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 { @@ -249,11 +249,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 40037af0be6..8d97f9b795b 100644 --- a/twilight-validate/src/command.rs +++ b/twilight-validate/src/command.rs @@ -240,22 +240,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(()), } } @@ -294,9 +292,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(); @@ -326,7 +324,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> { @@ -380,7 +378,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(); @@ -517,7 +515,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, diff --git a/twilight-validate/src/component.rs b/twilight-validate/src/component.rs index 5370e9f99fc..bed2ee1c7f7 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" @@ -353,7 +353,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 +518,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 +527,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 +545,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 +573,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 +1069,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 +1085,7 @@ mod tests { name: "📚".into() }), label: Some("Read".into()), - style: ButtonStyle::Link, + style: ButtonStyle::LINK, url: Some("https://abebooks.com".into()), }; @@ -1142,7 +1140,7 @@ mod tests { disabled: false, emoji: None, label: None, - style: ButtonStyle::Primary, + style: ButtonStyle::PRIMARY, url: Some("https://twilight.rs".to_owned()), }; From d5b23058afabd6edf8612bacead40b68602b394b Mon Sep 17 00:00:00 2001 From: Zeyla Hellyer Date: Sat, 7 Jan 2023 19:18:34 -0500 Subject: [PATCH 02/10] uncomment crates in cargo.toml --- Cargo.toml | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 08cd73df5b1..005e3ef0ab8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,19 +1,19 @@ [workspace] members = [ - # "book/tests", - # "examples", - # "twilight", - # "twilight-cache-inmemory", - # "twilight-gateway", - # "twilight-gateway-queue", - # "twilight-http", - # "twilight-http-ratelimiting", - # "twilight-lavalink", - # "twilight-mention", + "book/tests", + "examples", + "twilight", + "twilight-cache-inmemory", + "twilight-gateway", + "twilight-gateway-queue", + "twilight-http", + "twilight-http-ratelimiting", + "twilight-lavalink", + "twilight-mention", "twilight-model", - # "twilight-standby", - # "twilight-util", - # "twilight-validate", + "twilight-standby", + "twilight-util", + "twilight-validate", ] resolver = "2" From f790c34b2a9ab2fa1765324a80c27d8649786582 Mon Sep 17 00:00:00 2001 From: Zeyla Hellyer Date: Sun, 8 Jan 2023 10:57:03 -0500 Subject: [PATCH 03/10] fix what ci has given me thus far --- .../section_7_first_party/section_4_util.md | 2 +- twilight-cache-inmemory/src/permission.rs | 28 +++++++++---------- twilight-model/src/oauth/scope.rs | 2 +- twilight-model/src/util/known_string.rs | 15 ++++++---- 4 files changed, 25 insertions(+), 22 deletions(-) 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 bfa07376f9c..c6be4d043ba 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/twilight-cache-inmemory/src/permission.rs b/twilight-cache-inmemory/src/permission.rs index e1d5c29f7bf..624a67ad414 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(), }; @@ -712,11 +712,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, @@ -725,16 +725,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(), roles: Vec::from([ // Give the `@everyone` role a guild level and channel level @@ -752,7 +752,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, @@ -779,7 +779,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, @@ -795,13 +795,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), @@ -831,7 +831,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, @@ -846,7 +846,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-model/src/oauth/scope.rs b/twilight-model/src/oauth/scope.rs index d85e6d6b9a0..0c5d09acf61 100644 --- a/twilight-model/src/oauth/scope.rs +++ b/twilight-model/src/oauth/scope.rs @@ -27,7 +27,7 @@ use std::{ /// 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)] diff --git a/twilight-model/src/util/known_string.rs b/twilight-model/src/util/known_string.rs index 3e3ed1126ad..3579bdc8d81 100644 --- a/twilight-model/src/util/known_string.rs +++ b/twilight-model/src/util/known_string.rs @@ -87,12 +87,15 @@ use std::{ /// } /// /// fn print_sound(self) { -/// println!("{}", match self { -/// Self::A => "ayy", -/// Self::B => "bee", -/// Self::C => "sea", -/// _ => "unknown", -/// }); +/// println!( +/// "{}", +/// match self { +/// Self::A => "ayy", +/// Self::B => "bee", +/// Self::C => "sea", +/// _ => "unknown", +/// } +/// ); /// } /// } /// ``` From 2b34e0fef4cc7f2404430807b09f54d1ed9754e6 Mon Sep 17 00:00:00 2001 From: Zeyla Hellyer Date: Sun, 8 Jan 2023 11:07:15 -0500 Subject: [PATCH 04/10] fix --- twilight-standby/src/lib.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/twilight-standby/src/lib.rs b/twilight-standby/src/lib.rs index 3d7dc7ffc79..babd18cca9d 100644 --- a/twilight-standby/src/lib.rs +++ b/twilight-standby/src/lib.rs @@ -199,7 +199,7 @@ impl Standby { /// /// # Examples /// - /// Wait for a [`BAN_ADD`] event in guild 123: + /// Wait for a [`BanAdd`] event in guild 123: /// /// ```no_run /// # #[tokio::main] @@ -226,7 +226,7 @@ impl Standby { /// The returned future resolves to a [`Canceled`] error if the associated /// [`Standby`] instance is dropped. /// - /// [`BAN_ADD`]: twilight_model::gateway::payload::incoming::BAN_ADD + /// [`BanAdd`]: twilight_model::gateway::payload::incoming::BanAdd /// [`Canceled`]: future::Canceled /// [`wait_for_stream`]: Self::wait_for_stream pub fn wait_for bool + Send + Sync + 'static>( From e569e3e1104eda835284af724307aac032c45804 Mon Sep 17 00:00:00 2001 From: Zeyla Hellyer Date: Sun, 8 Jan 2023 13:19:34 -0500 Subject: [PATCH 05/10] fix todos --- .../src/application/command/permissions.rs | 13 +++++++++++-- twilight-model/src/gateway/event/gateway.rs | 16 +--------------- 2 files changed, 12 insertions(+), 17 deletions(-) diff --git a/twilight-model/src/application/command/permissions.rs b/twilight-model/src/application/command/permissions.rs index a2c85fd0e2c..d48b7bf1560 100644 --- a/twilight-model/src/application/command/permissions.rs +++ b/twilight-model/src/application/command/permissions.rs @@ -46,6 +46,10 @@ pub enum CommandPermissionType { Role(Id), /// Affected member. User(Id), + Unknown { + kind: CommandPermissionDataType, + id: Id, + }, } impl CommandPermissionType { @@ -55,6 +59,7 @@ impl CommandPermissionType { Self::Channel(id) => id.cast(), Self::Role(id) => id.cast(), Self::User(id) => id.cast(), + Self::Unknown { id, .. } => id, } } @@ -64,6 +69,7 @@ impl CommandPermissionType { Self::Channel(_) => CommandPermissionDataType::CHANNEL, Self::Role(_) => CommandPermissionDataType::ROLE, Self::User(_) => CommandPermissionDataType::USER, + Self::Unknown { kind, .. } => kind, } } } @@ -79,7 +85,7 @@ struct CommandPermissionData { permission: bool, } -#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)] +#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)] pub struct CommandPermissionDataType(u8); impl CommandPermissionDataType { @@ -134,7 +140,10 @@ impl<'de> Deserialize<'de> for CommandPermission { CommandPermissionDataType::ROLE => CommandPermissionType::Role(data.id.cast()), CommandPermissionDataType::USER => CommandPermissionType::User(data.id.cast()), CommandPermissionDataType::CHANNEL => CommandPermissionType::Channel(data.id.cast()), - _ => todo!(), + other => CommandPermissionType::Unknown { + kind: other, + id: data.id, + }, }; tracing::trace!(id = %data.id, kind = ?data.kind); diff --git a/twilight-model/src/gateway/event/gateway.rs b/twilight-model/src/gateway/event/gateway.rs index 9b1511f33e7..b048548ca62 100644 --- a/twilight-model/src/gateway/event/gateway.rs +++ b/twilight-model/src/gateway/event/gateway.rs @@ -343,26 +343,12 @@ impl<'de> Visitor<'de> for GatewayEventVisitor<'_> { GatewayEvent::InvalidateSession(invalidate) } - OpCode::IDENTIFY => return Err(DeError::unknown_variant("Identify", VALID_OPCODES)), OpCode::RECONNECT => { Self::ignore_all(&mut map)?; GatewayEvent::Reconnect } - OpCode::REQUEST_GUILD_MEMBERS => { - return Err(DeError::unknown_variant( - "RequestGuildMembers", - VALID_OPCODES, - )) - } - OpCode::RESUME => return Err(DeError::unknown_variant("Resume", VALID_OPCODES)), - OpCode::PRESENCE_UPDATE => { - return Err(DeError::unknown_variant("PresenceUpdate", VALID_OPCODES)) - } - OpCode::VOICE_STATE_UPDATE => { - return Err(DeError::unknown_variant("VoiceStateUpdate", VALID_OPCODES)) - } - _ => todo!(), + other => return Err(DeError::unknown_variant(other.name(), VALID_OPCODES)), }) } } From 4d46ec21b24c7193a4b87e399fbb94759ab7fbe6 Mon Sep 17 00:00:00 2001 From: Zeyla Hellyer Date: Wed, 18 Jan 2023 11:34:47 -0500 Subject: [PATCH 06/10] debug impls for types --- .../src/application/command/command_type.rs | 28 +++- .../src/application/command/option.rs | 41 +++++- .../src/application/command/permissions.rs | 30 +++- .../interaction/interaction_type.rs | 30 +++- twilight-model/src/channel/channel_type.rs | 52 ++++--- twilight-model/src/channel/forum.rs | 50 +++++-- .../src/channel/message/activity.rs | 29 +++- .../src/channel/message/allowed_mentions.rs | 14 +- .../src/channel/message/component/button.rs | 30 +++- .../src/channel/message/component/kind.rs | 37 +++-- .../channel/message/component/text_input.rs | 27 +++- twilight-model/src/channel/message/kind.rs | 57 +++++++- .../channel/message/sticker/format_type.rs | 28 +++- .../src/channel/message/sticker/kind.rs | 27 +++- .../src/channel/permission_overwrite.rs | 29 +++- .../channel/stage_instance/privacy_level.rs | 26 +++- .../channel/thread/auto_archive_duration.rs | 33 ++++- .../src/channel/video_quality_mode.rs | 35 +++-- twilight-model/src/channel/webhook/kind.rs | 28 +++- twilight-model/src/gateway/close_code.rs | 54 ++++--- twilight-model/src/gateway/event/gateway.rs | 21 +-- twilight-model/src/gateway/event/kind.rs | 135 +++++++++--------- twilight-model/src/gateway/opcode.rs | 25 +++- .../src/gateway/presence/activity_type.rs | 31 +++- twilight-model/src/gateway/presence/status.rs | 16 ++- twilight-model/src/guild/afk_timeout.rs | 34 ++++- .../src/guild/audit_log/change_key.rs | 84 ++++++++++- .../src/guild/audit_log/event_type.rs | 81 ++++++++++- .../src/guild/auto_moderation/action.rs | 30 +++- .../src/guild/auto_moderation/event_type.rs | 28 +++- .../src/guild/auto_moderation/preset_type.rs | 30 +++- .../src/guild/auto_moderation/trigger_type.rs | 31 +++- .../default_message_notification_level.rs | 29 +++- .../src/guild/explicit_content_filter.rs | 30 +++- twilight-model/src/guild/feature.rs | 36 ++++- .../src/guild/integration_expire_behavior.rs | 29 +++- twilight-model/src/guild/integration_type.rs | 14 +- .../src/guild/invite/target_type.rs | 27 +++- twilight-model/src/guild/mfa_level.rs | 27 +++- twilight-model/src/guild/nsfw_level.rs | 29 +++- twilight-model/src/guild/premium_tier.rs | 29 +++- .../src/guild/scheduled_event/mod.rs | 81 ++++++++++- .../src/guild/verification_level.rs | 30 +++- twilight-model/src/http/interaction.rs | 36 ++++- .../src/http/permission_overwrite.rs | 29 +++- twilight-model/src/oauth/scope.rs | 41 +++++- .../src/oauth/team/membership_state.rs | 27 +++- .../src/user/connection_visibility.rs | 29 +++- twilight-model/src/user/premium_type.rs | 29 +++- twilight-model/src/util/known_string.rs | 2 +- twilight-model/src/voice/close_code.rs | 37 ++++- twilight-model/src/voice/opcode.rs | 36 ++++- 52 files changed, 1658 insertions(+), 200 deletions(-) diff --git a/twilight-model/src/application/command/command_type.rs b/twilight-model/src/application/command/command_type.rs index 9480d760424..015dc0840fe 100644 --- a/twilight-model/src/application/command/command_type.rs +++ b/twilight-model/src/application/command/command_type.rs @@ -1,7 +1,8 @@ use serde::{Deserialize, Serialize}; +use std::fmt::{Debug, Formatter, Result as FmtResult}; // Keep in sync with `twilight-validate::command`! -#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)] +#[derive(Clone, Copy, Deserialize, Eq, Hash, PartialEq, Serialize)] pub struct CommandType(u8); impl CommandType { @@ -40,6 +41,31 @@ impl CommandType { pub const fn get(&self) -> u8 { self.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::CHAT_INPUT => "CHAT_INPUT", + Self::MESSAGE => "MESSAGE", + Self::USER => "USER", + _ => return None, + }) + } +} + +impl Debug for CommandType { + fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult { + if let Some(name) = self.name() { + f.debug_struct("CommandType") + .field("name", &name) + .field("value", &self.0) + .finish() + } else { + f.debug_tuple("CommandType").field(&self.0).finish() + } + } } impl From for CommandType { diff --git a/twilight-model/src/application/command/option.rs b/twilight-model/src/application/command/option.rs index 7382886fe74..e6da3674357 100644 --- a/twilight-model/src/application/command/option.rs +++ b/twilight-model/src/application/command/option.rs @@ -1,6 +1,10 @@ use crate::channel::ChannelType; use serde::{Deserialize, Serialize}; -use std::{cmp::Eq, collections::HashMap}; +use std::{ + cmp::Eq, + collections::HashMap, + fmt::{Debug, Formatter, Result as FmtResult}, +}; /// Option for a [`Command`]. /// @@ -199,7 +203,7 @@ pub enum CommandOptionValue { } /// Type of a [`CommandOption`]. -#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)] +#[derive(Clone, Copy, Deserialize, Eq, Hash, PartialEq, Serialize)] pub struct CommandOptionType(u8); impl CommandOptionType { @@ -245,6 +249,39 @@ impl CommandOptionType { pub const fn get(&self) -> u8 { self.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::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 Debug for CommandOptionType { + fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult { + if let Some(name) = self.name() { + f.debug_struct("CommandOptionType") + .field("name", &name) + .field("value", &self.0) + .finish() + } else { + f.debug_tuple("CommandOptionType").field(&self.0).finish() + } + } } impl From for CommandOptionType { diff --git a/twilight-model/src/application/command/permissions.rs b/twilight-model/src/application/command/permissions.rs index d48b7bf1560..c7763d8e656 100644 --- a/twilight-model/src/application/command/permissions.rs +++ b/twilight-model/src/application/command/permissions.rs @@ -8,6 +8,7 @@ use crate::id::{ Id, }; use serde::{de::Deserializer, ser::Serializer, Deserialize, Serialize}; +use std::fmt::{Debug, Formatter, Result as FmtResult}; /// List of [`CommandPermission`]s for a command in a guild. #[derive(Clone, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)] @@ -85,7 +86,7 @@ struct CommandPermissionData { permission: bool, } -#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)] +#[derive(Clone, Copy, Deserialize, Eq, Hash, PartialEq, Serialize)] pub struct CommandPermissionDataType(u8); impl CommandPermissionDataType { @@ -115,6 +116,33 @@ impl CommandPermissionDataType { pub const fn get(&self) -> u8 { self.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::CHANNEL => "CHANNEL", + Self::ROLE => "ROLE", + Self::USER => "USER", + _ => return None, + }) + } +} + +impl Debug for CommandPermissionDataType { + fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult { + if let Some(name) = self.name() { + f.debug_struct("CommandPermissionDataType") + .field("name", &name) + .field("value", &self.0) + .finish() + } else { + f.debug_tuple("CommandPermissionDataType") + .field(&self.0) + .finish() + } + } } impl From for CommandPermissionDataType { diff --git a/twilight-model/src/application/interaction/interaction_type.rs b/twilight-model/src/application/interaction/interaction_type.rs index 22c256dc73c..8fac0af120b 100644 --- a/twilight-model/src/application/interaction/interaction_type.rs +++ b/twilight-model/src/application/interaction/interaction_type.rs @@ -1,11 +1,12 @@ use serde::{Deserialize, Serialize}; +use std::fmt::{Debug, Formatter, Result as FmtResult}; /// 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, Eq, Hash, PartialEq, Serialize)] +#[derive(Clone, Copy, Deserialize, Eq, Hash, PartialEq, Serialize)] pub struct InteractionType(u8); impl InteractionType { @@ -50,6 +51,33 @@ impl InteractionType { pub const fn get(&self) -> u8 { self.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::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 Debug for InteractionType { + fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult { + if let Some(name) = self.name() { + f.debug_struct("InteractionType") + .field("name", &name) + .field("value", &self.0) + .finish() + } else { + f.debug_tuple("InteractionType").field(&self.0).finish() + } + } } impl From for InteractionType { diff --git a/twilight-model/src/channel/channel_type.rs b/twilight-model/src/channel/channel_type.rs index 8262e9f399d..6052920ef3d 100644 --- a/twilight-model/src/channel/channel_type.rs +++ b/twilight-model/src/channel/channel_type.rs @@ -1,6 +1,7 @@ use serde::{Deserialize, Serialize}; +use std::fmt::{Debug, Formatter, Result as FmtResult}; -#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)] +#[derive(Clone, Copy, Deserialize, Eq, Hash, PartialEq, Serialize)] pub struct ChannelType(u8); impl ChannelType { @@ -53,6 +54,27 @@ impl ChannelType { self.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::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, + }) + } + /// Whether the channel type is that of a guild. /// /// The following channel types are considered guild channel types: @@ -94,23 +116,17 @@ impl ChannelType { 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::ANNOUNCEMENT_THREAD => "ANNOUNCEMENT_THREAD", - Self::GROUP => "GROUP", - Self::GUILD_CATEGORY => "GUILD_CATEGORY", - Self::GUILD_DIRECTORY => "GUILD_DIRECTORY", - Self::GUILD_FORUM => "GUILD_FORUM", - Self::GUILD_ANNOUNCEMENT => "GUILD_ANNOUNCEMENT", - Self::GUILD_STAGE_VOICE => "GUILD_STAGE_VOICE", - Self::GUILD_TEXT => "GUILD_TEXT", - Self::GUILD_VOICE => "GUILD_VOICE", - Self::PRIVATE => "PRIVATE", - Self::PRIVATE_THREAD => "PRIVATE_THREAD", - Self::PUBLIC_THREAD => "PUBLIC_THREAD", - _ => "UNKNOWN", +impl Debug for ChannelType { + fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult { + if let Some(name) = self.name() { + f.debug_struct("ChannelType") + .field("name", &name) + .field("value", &self.0) + .finish() + } else { + f.debug_tuple("ChannelType").field(&self.0).finish() } } } @@ -176,7 +192,7 @@ mod tests { ); assert_eq!(*kind, ChannelType::from(*num)); assert_eq!(*num, kind.get()); - assert_eq!(kind.name(), *name); + assert_eq!(kind.name(), Some(*name)); } } } diff --git a/twilight-model/src/channel/forum.rs b/twilight-model/src/channel/forum.rs index d970b0877bd..1ef8ade3e86 100644 --- a/twilight-model/src/channel/forum.rs +++ b/twilight-model/src/channel/forum.rs @@ -3,6 +3,7 @@ use crate::id::{ Id, }; use serde::{Deserialize, Serialize}; +use std::fmt::{Debug, Formatter, Result as FmtResult}; /// Emoji to use as the default way to react to a forum post. /// @@ -23,7 +24,7 @@ pub struct DefaultReaction { /// /// [channel]: super::Channel /// [forum]: super::ChannelType::GUILD_FORUM -#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)] +#[derive(Clone, Copy, Deserialize, Eq, Hash, PartialEq, Serialize)] pub struct ForumLayout(u8); impl ForumLayout { @@ -57,12 +58,28 @@ impl ForumLayout { self.0 } - pub const fn name(self) -> &'static str { - match self { + /// 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", - _ => "UNKNOWN", + _ => return None, + }) + } +} + +impl Debug for ForumLayout { + fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult { + if let Some(name) = self.name() { + f.debug_struct("ForumLayout") + .field("name", &name) + .field("value", &self.0) + .finish() + } else { + f.debug_tuple("ForumLayout").field(&self.0).finish() } } } @@ -83,7 +100,7 @@ impl From for u8 { /// /// [channel]: super::Channel /// [forum]: super::ChannelType::GUILD_FORUM -#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)] +#[derive(Clone, Copy, Deserialize, Eq, Hash, PartialEq, Serialize)] pub struct ForumSortOrder(u8); impl ForumSortOrder { @@ -114,11 +131,24 @@ impl ForumSortOrder { self.0 } - pub const fn name(self) -> &'static str { - match self { + pub const fn name(self) -> Option<&'static str> { + Some(match self { Self::CREATION_DATE => "CREATION_DATE", Self::LATEST_ACTIVITY => "LATEST_ACTIVITY", - _ => "UNKNOWN", + _ => return None, + }) + } +} + +impl Debug for ForumSortOrder { + fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult { + if let Some(name) = self.name() { + f.debug_struct("ForumSortOrder") + .field("name", &name) + .field("value", &self.0) + .finish() + } else { + f.debug_tuple("ForumSortOrder").field(&self.0).finish() } } } @@ -247,7 +277,7 @@ mod tests { Token::U8(*number), ], ); - assert_eq!(layout.name(), *name); + assert_eq!(layout.name(), Some(*name)); } } @@ -259,7 +289,7 @@ mod tests { ]; 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( diff --git a/twilight-model/src/channel/message/activity.rs b/twilight-model/src/channel/message/activity.rs index d3516e73c5f..5b733907543 100644 --- a/twilight-model/src/channel/message/activity.rs +++ b/twilight-model/src/channel/message/activity.rs @@ -1,4 +1,5 @@ use serde::{Deserialize, Serialize}; +use std::fmt::{Debug, Formatter, Result as FmtResult}; /// Activity associated with a message. #[derive(Clone, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)] @@ -12,7 +13,7 @@ pub struct MessageActivity { } /// Activity of this message. -#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)] +#[derive(Clone, Copy, Deserialize, Eq, Hash, PartialEq, Serialize)] pub struct MessageActivityType(u8); @@ -49,6 +50,32 @@ impl MessageActivityType { pub const fn get(&self) -> u8 { self.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::JOIN => "JOIN", + Self::SPECTATE => "SPECTATE", + Self::LISTEN => "LISTEN", + Self::JOIN_REQUEST => "JOIN_REQUEST", + _ => return None, + }) + } +} + +impl Debug for MessageActivityType { + fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult { + if let Some(name) = self.name() { + f.debug_struct("MessageActivityType") + .field("name", &name) + .field("value", &self.0) + .finish() + } else { + f.debug_tuple("MessageActivityType").field(&self.0).finish() + } + } } impl From for MessageActivityType { diff --git a/twilight-model/src/channel/message/allowed_mentions.rs b/twilight-model/src/channel/message/allowed_mentions.rs index 96d2462ae78..a0104eead45 100644 --- a/twilight-model/src/channel/message/allowed_mentions.rs +++ b/twilight-model/src/channel/message/allowed_mentions.rs @@ -78,6 +78,18 @@ impl MentionType { self.0.get() } + /// 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, + }) + } + /// Create a mention type from a set of bytes. const fn from_bytes(input: &[u8]) -> Self { Self(KnownString::from_bytes(input)) @@ -92,7 +104,7 @@ impl AsRef for MentionType { impl Debug for MentionType { fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult { - f.write_str(self.get()) + f.write_str(self.name().unwrap_or_else(|| self.get())) } } diff --git a/twilight-model/src/channel/message/component/button.rs b/twilight-model/src/channel/message/component/button.rs index a7dfc26881e..4547c78a2a6 100644 --- a/twilight-model/src/channel/message/component/button.rs +++ b/twilight-model/src/channel/message/component/button.rs @@ -1,5 +1,6 @@ use crate::channel::message::ReactionType; use serde::{Deserialize, Serialize}; +use std::fmt::{Debug, Formatter, Result as FmtResult}; /// Clickable [`Component`] below messages. /// @@ -31,7 +32,7 @@ pub struct Button { /// Style of a [`Button`]. // Keep in sync with `twilight-validate::component`! -#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)] +#[derive(Clone, Copy, Deserialize, Eq, Hash, PartialEq, Serialize)] pub struct ButtonStyle(u8); impl ButtonStyle { @@ -85,6 +86,33 @@ impl ButtonStyle { pub const fn get(&self) -> u8 { self.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::DANGER => "DANGER", + Self::LINK => "LINK", + Self::PRIMARY => "PRIMARY", + Self::SECONDARY => "SECONDARY", + Self::SUCCESS => "SUCCESS", + _ => return None, + }) + } +} + +impl Debug for ButtonStyle { + fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult { + if let Some(name) = self.name() { + f.debug_struct("ButtonStyle") + .field("name", &name) + .field("value", &self.0) + .finish() + } else { + f.debug_tuple("ButtonStyle").field(&self.0).finish() + } + } } impl From for ButtonStyle { diff --git a/twilight-model/src/channel/message/component/kind.rs b/twilight-model/src/channel/message/component/kind.rs index 3508b57ed5f..89889eb77ba 100644 --- a/twilight-model/src/channel/message/component/kind.rs +++ b/twilight-model/src/channel/message/component/kind.rs @@ -1,10 +1,10 @@ use serde::{Deserialize, Serialize}; -use std::fmt::{Display, Formatter, Result as FmtResult}; +use std::fmt::{Debug, Formatter, Result as FmtResult}; /// Type of [`Component`]. /// /// [`Component`]: super::Component -#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)] +#[derive(Clone, Copy, Deserialize, Eq, Hash, PartialEq, Serialize)] pub struct ComponentType(u8); impl ComponentType { @@ -49,28 +49,41 @@ impl ComponentType { self.0 } - /// 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 [`ACTION_ROW`] variant's name: + /// Check the [`ACTION_ROW`] constant's name: /// /// ``` /// use twilight_model::channel::message::component::ComponentType; /// - /// assert_eq!("ACTION_ROW", ComponentType::ACTION_ROW.name()); + /// assert_eq!(Some("ACTION_ROW"), ComponentType::ACTION_ROW.name()); /// ``` /// /// [`ACTION_ROW`]: Self::ACTION_ROW - pub const fn name(&self) -> &'static str { - match *self { + 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", - _ => "UNKNOWN", + _ => return None, + }) + } +} + +impl Debug for ComponentType { + fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult { + if let Some(name) = self.name() { + f.debug_struct("ComponentType") + .field("name", &name) + .field("value", &self.0) + .finish() + } else { + f.debug_tuple("ComponentType").field(&self.0).finish() } } } @@ -87,12 +100,6 @@ impl From for u8 { } } -impl Display for ComponentType { - fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult { - f.write_str(self.name()) - } -} - #[cfg(test)] mod tests { use super::ComponentType; diff --git a/twilight-model/src/channel/message/component/text_input.rs b/twilight-model/src/channel/message/component/text_input.rs index 462b4c8ea41..d9ba0b90931 100644 --- a/twilight-model/src/channel/message/component/text_input.rs +++ b/twilight-model/src/channel/message/component/text_input.rs @@ -1,4 +1,5 @@ use serde::{Deserialize, Serialize}; +use std::fmt::{Debug, Formatter, Result as FmtResult}; /// Pop-up [`Component`] that renders on modals. /// @@ -28,7 +29,7 @@ pub struct TextInput { } /// Style of an [`TextInput`]. -#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)] +#[derive(Clone, Copy, Deserialize, Eq, Hash, PartialEq, Serialize)] pub struct TextInputStyle(u8); impl TextInputStyle { @@ -58,6 +59,30 @@ impl TextInputStyle { pub const fn get(&self) -> u8 { self.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::PARAGRAPH => "PARAGRAPH", + Self::SHORT => "SHORT", + _ => return None, + }) + } +} + +impl Debug for TextInputStyle { + fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult { + if let Some(name) = self.name() { + f.debug_struct("TextInputStyle") + .field("name", &name) + .field("value", &self.0) + .finish() + } else { + f.debug_tuple("TextInputStyle").field(&self.0).finish() + } + } } impl From for TextInputStyle { diff --git a/twilight-model/src/channel/message/kind.rs b/twilight-model/src/channel/message/kind.rs index c30baf3de5f..ddde1fb9ba3 100644 --- a/twilight-model/src/channel/message/kind.rs +++ b/twilight-model/src/channel/message/kind.rs @@ -1,5 +1,6 @@ use crate::guild::Permissions; use serde::{Deserialize, Serialize}; +use std::fmt::{Debug, Formatter, Result as FmtResult}; /// Type of a [`Message`]. /// @@ -8,7 +9,7 @@ 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)] +#[derive(Clone, Copy, Deserialize, Eq, Hash, PartialEq, Serialize)] pub struct MessageType(u8); impl MessageType { @@ -111,6 +112,47 @@ impl MessageType { self.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::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, + }) + } + /// Whether the message can be deleted, not taking permissions into account. /// Some message types can't be deleted, even by server administrators. /// @@ -170,6 +212,19 @@ impl MessageType { } } +impl Debug for MessageType { + fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult { + if let Some(name) = self.name() { + f.debug_struct("MessageType") + .field("name", &name) + .field("value", &self.0) + .finish() + } else { + f.debug_tuple("MessageType").field(&self.0).finish() + } + } +} + impl From for MessageType { fn from(value: u8) -> Self { Self(value) diff --git a/twilight-model/src/channel/message/sticker/format_type.rs b/twilight-model/src/channel/message/sticker/format_type.rs index 778c025620f..afcf442cb20 100644 --- a/twilight-model/src/channel/message/sticker/format_type.rs +++ b/twilight-model/src/channel/message/sticker/format_type.rs @@ -1,9 +1,10 @@ use serde::{Deserialize, Serialize}; +use std::fmt::{Debug, Formatter, Result as FmtResult}; /// Format type of a [`Sticker`]. /// /// [`Sticker`]: super::Sticker -#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)] +#[derive(Clone, Copy, Deserialize, Eq, Hash, PartialEq, Serialize)] pub struct StickerFormatType(u8); impl StickerFormatType { @@ -36,6 +37,31 @@ impl StickerFormatType { pub const fn get(&self) -> u8 { self.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::APNG => "APNG", + Self::LOTTIE => "LOTTIE", + Self::PNG => "PNG", + _ => return None, + }) + } +} + +impl Debug for StickerFormatType { + fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult { + if let Some(name) = self.name() { + f.debug_struct("StickerFormatType") + .field("name", &name) + .field("value", &self.0) + .finish() + } else { + f.debug_tuple("StickerFormatType").field(&self.0).finish() + } + } } impl From for StickerFormatType { diff --git a/twilight-model/src/channel/message/sticker/kind.rs b/twilight-model/src/channel/message/sticker/kind.rs index c608001ba7c..30be530c688 100644 --- a/twilight-model/src/channel/message/sticker/kind.rs +++ b/twilight-model/src/channel/message/sticker/kind.rs @@ -1,9 +1,10 @@ use serde::{Deserialize, Serialize}; +use std::fmt::{Debug, Formatter, Result as FmtResult}; /// Type of a [`Sticker`]. /// /// [`Sticker`]: super::Sticker -#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)] +#[derive(Clone, Copy, Deserialize, Eq, Hash, PartialEq, Serialize)] pub struct StickerType(u8); impl StickerType { @@ -35,6 +36,30 @@ impl StickerType { pub const fn get(&self) -> u8 { self.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::STANDARD => "STANDARD", + Self::GUILD => "GUILD", + _ => return None, + }) + } +} + +impl Debug for StickerType { + fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult { + if let Some(name) = self.name() { + f.debug_struct("StickerType") + .field("name", &name) + .field("value", &self.0) + .finish() + } else { + f.debug_tuple("StickerType").field(&self.0).finish() + } + } } impl From for StickerType { diff --git a/twilight-model/src/channel/permission_overwrite.rs b/twilight-model/src/channel/permission_overwrite.rs index 759b4a456b4..de6deb728e2 100644 --- a/twilight-model/src/channel/permission_overwrite.rs +++ b/twilight-model/src/channel/permission_overwrite.rs @@ -3,6 +3,7 @@ use crate::{ id::{marker::GenericMarker, Id}, }; use serde::{Deserialize, Serialize}; +use std::fmt::{Debug, Formatter, Result as FmtResult}; /// Permission overwrite data for a role or member. #[derive(Clone, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)] @@ -16,7 +17,7 @@ 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)] +#[derive(Clone, Copy, Serialize, Eq, Hash, PartialEq, Deserialize)] pub struct PermissionOverwriteType(u8); impl PermissionOverwriteType { @@ -46,6 +47,32 @@ impl PermissionOverwriteType { pub const fn get(&self) -> u8 { self.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 Debug for PermissionOverwriteType { + fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult { + if let Some(name) = self.name() { + f.debug_struct("PermissionOverwriteType") + .field("name", &name) + .field("value", &self.0) + .finish() + } else { + f.debug_tuple("PermissionOverwriteType") + .field(&self.0) + .finish() + } + } } impl From for PermissionOverwriteType { diff --git a/twilight-model/src/channel/stage_instance/privacy_level.rs b/twilight-model/src/channel/stage_instance/privacy_level.rs index d4dd38e62a6..3fc49b96a1a 100644 --- a/twilight-model/src/channel/stage_instance/privacy_level.rs +++ b/twilight-model/src/channel/stage_instance/privacy_level.rs @@ -1,6 +1,7 @@ use serde::{Deserialize, Serialize}; +use std::fmt::{Debug, Formatter, Result as FmtResult}; -#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)] +#[derive(Clone, Copy, Deserialize, Eq, Hash, PartialEq, Serialize)] pub struct PrivacyLevel(u8); impl PrivacyLevel { @@ -26,6 +27,29 @@ impl PrivacyLevel { pub const fn get(&self) -> u8 { self.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::GUILD_ONLY => "GUILD_ONLY", + _ => return None, + }) + } +} + +impl Debug for PrivacyLevel { + fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult { + if let Some(name) = self.name() { + f.debug_struct("PrivacyLevel") + .field("name", &name) + .field("value", &self.0) + .finish() + } else { + f.debug_tuple("PrivacyLevel").field(&self.0).finish() + } + } } impl From for PrivacyLevel { diff --git a/twilight-model/src/channel/thread/auto_archive_duration.rs b/twilight-model/src/channel/thread/auto_archive_duration.rs index 497461176f2..78abb6cf025 100644 --- a/twilight-model/src/channel/thread/auto_archive_duration.rs +++ b/twilight-model/src/channel/thread/auto_archive_duration.rs @@ -1,7 +1,10 @@ use serde::{Deserialize, Serialize}; -use std::time::Duration; +use std::{ + fmt::{Debug, Formatter, Result as FmtResult}, + time::Duration, +}; -#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)] +#[derive(Clone, Copy, Deserialize, Eq, Hash, PartialEq, Serialize)] pub struct AutoArchiveDuration(u16); impl AutoArchiveDuration { @@ -33,6 +36,32 @@ impl AutoArchiveDuration { pub const fn get(&self) -> u16 { self.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::HOUR => "HOUR", + Self::DAY => "DAY", + Self::THREE_DAYS => "THREE_DAYS", + Self::WEEK => "WEEK", + _ => return None, + }) + } +} + +impl Debug for AutoArchiveDuration { + fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult { + if let Some(name) = self.name() { + f.debug_struct("AutoArchiveDuration") + .field("name", &name) + .field("value", &self.0) + .finish() + } else { + f.debug_tuple("AutoArchiveDuration").field(&self.0).finish() + } + } } impl From for AutoArchiveDuration { diff --git a/twilight-model/src/channel/video_quality_mode.rs b/twilight-model/src/channel/video_quality_mode.rs index 6be51cd13c1..daababf7c30 100644 --- a/twilight-model/src/channel/video_quality_mode.rs +++ b/twilight-model/src/channel/video_quality_mode.rs @@ -1,6 +1,7 @@ use serde::{Deserialize, Serialize}; +use std::fmt::{Debug, Formatter, Result as FmtResult}; -#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)] +#[derive(Clone, Copy, Deserialize, Eq, Hash, PartialEq, Serialize)] pub struct VideoQualityMode(u8); impl VideoQualityMode { @@ -10,14 +11,6 @@ impl VideoQualityMode { /// 720p. pub const FULL: Self = Self::new(2); - pub const fn name(self) -> &'static str { - match self { - Self::AUTO => "Auto", - Self::FULL => "Full", - _ => "UNKNOWN", - } - } - /// Create a new video quality mode from a dynamic value. /// /// The provided value isn't validated. Known valid values are associated @@ -38,6 +31,30 @@ impl VideoQualityMode { pub const fn get(&self) -> u8 { self.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::AUTO => "AUTO", + Self::FULL => "FULL", + _ => return None, + }) + } +} + +impl Debug for VideoQualityMode { + fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult { + if let Some(name) = self.name() { + f.debug_struct("VideoQualityMode") + .field("name", &name) + .field("value", &self.0) + .finish() + } else { + f.debug_tuple("VideoQualityMode").field(&self.0).finish() + } + } } impl From for VideoQualityMode { diff --git a/twilight-model/src/channel/webhook/kind.rs b/twilight-model/src/channel/webhook/kind.rs index 25fe75ba8b3..e2f7de3ff5e 100644 --- a/twilight-model/src/channel/webhook/kind.rs +++ b/twilight-model/src/channel/webhook/kind.rs @@ -1,6 +1,7 @@ use serde::{Deserialize, Serialize}; +use std::fmt::{Debug, Formatter, Result as FmtResult}; -#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)] +#[derive(Clone, Copy, Deserialize, Eq, Hash, PartialEq, Serialize)] pub struct WebhookType(u8); impl WebhookType { @@ -31,6 +32,31 @@ impl WebhookType { pub const fn get(&self) -> u8 { self.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::APPLICATION => "APPLICATION", + Self::CHANNEL_FOLLOWER => "CHANNEL_FOLLOWER", + Self::INCOMING => "INCOMING", + _ => return None, + }) + } +} + +impl Debug for WebhookType { + fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult { + if let Some(name) = self.name() { + f.debug_struct("WebhookType") + .field("name", &name) + .field("value", &self.0) + .finish() + } else { + f.debug_tuple("WebhookType").field(&self.0).finish() + } + } } impl Default for WebhookType { diff --git a/twilight-model/src/gateway/close_code.rs b/twilight-model/src/gateway/close_code.rs index f1c73e43042..69127469bd9 100644 --- a/twilight-model/src/gateway/close_code.rs +++ b/twilight-model/src/gateway/close_code.rs @@ -1,12 +1,12 @@ use serde::{Deserialize, Serialize}; -use std::fmt::{Display, Formatter, Result as FmtResult}; +use std::fmt::{Debug, Formatter, Result as FmtResult}; /// 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, Eq, Hash, PartialEq, Serialize)] +#[derive(Clone, Copy, Deserialize, Eq, Hash, PartialEq, Serialize)] pub struct CloseCode(u16); impl CloseCode { @@ -73,6 +73,29 @@ impl CloseCode { self.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::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, + }) + } + /// 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 @@ -90,25 +113,16 @@ impl CloseCode { } } -impl Display for CloseCode { +impl Debug for CloseCode { fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult { - f.write_str(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", - _ => "UNKNOWN", - }) + if let Some(name) = self.name() { + f.debug_struct("CloseCode") + .field("name", &name) + .field("value", &self.0) + .finish() + } else { + f.debug_tuple("CloseCode").field(&self.0).finish() + } } } diff --git a/twilight-model/src/gateway/event/gateway.rs b/twilight-model/src/gateway/event/gateway.rs index b048548ca62..e96376a3745 100644 --- a/twilight-model/src/gateway/event/gateway.rs +++ b/twilight-model/src/gateway/event/gateway.rs @@ -233,13 +233,13 @@ impl<'de> Visitor<'de> for GatewayEventVisitor<'_> { V: MapAccess<'de>, { static VALID_OPCODES: &[&str] = &[ - OpCode::DISPATCH.name(), - OpCode::HEARTBEAT.name(), - OpCode::HEARTBEAT_ACK.name(), - OpCode::HELLO.name(), - OpCode::IDENTIFY.name(), - OpCode::INVALID_SESSION.name(), - OpCode::RECONNECT.name(), + "DISPATCH", + "HEARTBEAT", + "HEARTBEAT_ACK", + "HELLO", + "IDENTIFY", + "INVALID_SESSION", + "RECONNECT", ]; let span = tracing::trace_span!("deserializing gateway event"); @@ -348,7 +348,12 @@ impl<'de> Visitor<'de> for GatewayEventVisitor<'_> { GatewayEvent::Reconnect } - other => return Err(DeError::unknown_variant(other.name(), VALID_OPCODES)), + other => { + return Err(DeError::unknown_variant( + other.name().unwrap_or("UNKNOWN"), + VALID_OPCODES, + )); + } }) } } diff --git a/twilight-model/src/gateway/event/kind.rs b/twilight-model/src/gateway/event/kind.rs index 3518867ec23..096ff0cffb7 100644 --- a/twilight-model/src/gateway/event/kind.rs +++ b/twilight-model/src/gateway/event/kind.rs @@ -172,73 +172,74 @@ impl EventType { self.0.get() } - pub fn name(self) -> Option<&'static str> { + /// 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_MODERATION_ACTION_EXECUTION => Self::AUTO_MODERATION_ACTION_EXECUTION.get(), - Self::AUTO_MODERATION_RULE_CREATE => Self::AUTO_MODERATION_RULE_CREATE.get(), - Self::AUTO_MODERATION_RULE_DELETE => Self::AUTO_MODERATION_RULE_DELETE.get(), - Self::AUTO_MODERATION_RULE_UPDATE => Self::AUTO_MODERATION_RULE_UPDATE.get(), - Self::BAN_ADD => Self::BAN_ADD.get(), - Self::BAN_REMOVE => Self::BAN_REMOVE.get(), - Self::CHANNEL_CREATE => Self::CHANNEL_CREATE.get(), - Self::CHANNEL_DELETE => Self::CHANNEL_DELETE.get(), - Self::CHANNEL_PINS_UPDATE => Self::CHANNEL_PINS_UPDATE.get(), - Self::CHANNEL_UPDATE => Self::CHANNEL_UPDATE.get(), - Self::COMMAND_PERMISSIONS_UPDATE => Self::COMMAND_PERMISSIONS_UPDATE.get(), - Self::GIFT_CODE_UPDATE => Self::GIFT_CODE_UPDATE.get(), - Self::GUILD_CREATE => Self::GUILD_CREATE.get(), - Self::GUILD_DELETE => Self::GUILD_DELETE.get(), - Self::GUILD_EMOJIS_UPDATE => Self::GUILD_EMOJIS_UPDATE.get(), - Self::GUILD_INTEGRATIONS_UPDATE => Self::GUILD_INTEGRATIONS_UPDATE.get(), - Self::GUILD_SCHEDULED_EVENT_CREATE => Self::GUILD_SCHEDULED_EVENT_CREATE.get(), - Self::GUILD_SCHEDULED_EVENT_DELETE => Self::GUILD_SCHEDULED_EVENT_DELETE.get(), - Self::GUILD_SCHEDULED_EVENT_UPDATE => Self::GUILD_SCHEDULED_EVENT_UPDATE.get(), - Self::GUILD_SCHEDULED_EVENT_USER_ADD => Self::GUILD_SCHEDULED_EVENT_USER_ADD.get(), - Self::GUILD_SCHEDULED_EVENT_USER_REMOVE => { - Self::GUILD_SCHEDULED_EVENT_USER_REMOVE.get() - } - Self::GUILD_STICKERS_UPDATE => Self::GUILD_STICKERS_UPDATE.get(), - Self::GUILD_UPDATE => Self::GUILD_UPDATE.get(), - Self::INTEGRATION_CREATE => Self::INTEGRATION_CREATE.get(), - Self::INTEGRATION_DELETE => Self::INTEGRATION_DELETE.get(), - Self::INTEGRATION_UPDATE => Self::INTEGRATION_UPDATE.get(), - Self::INTERACTION_CREATE => Self::INTERACTION_CREATE.get(), - Self::INVITE_CREATE => Self::INVITE_CREATE.get(), - Self::INVITE_DELETE => Self::INVITE_DELETE.get(), - Self::MEMBER_ADD => Self::MEMBER_ADD.get(), - Self::MEMBER_CHUNK => Self::MEMBER_CHUNK.get(), - Self::MEMBER_REMOVE => Self::MEMBER_REMOVE.get(), - Self::MEMBER_UPDATE => Self::MEMBER_UPDATE.get(), - Self::MESSAGE_CREATE => Self::MESSAGE_CREATE.get(), - Self::MESSAGE_DELETE => Self::MESSAGE_DELETE.get(), - Self::MESSAGE_DELETE_BULK => Self::MESSAGE_DELETE_BULK.get(), - Self::MESSAGE_UPDATE => Self::MESSAGE_UPDATE.get(), - Self::PRESENCES_REPLACE => Self::PRESENCES_REPLACE.get(), - Self::PRESENCE_UPDATE => Self::PRESENCE_UPDATE.get(), - Self::REACTION_ADD => Self::REACTION_ADD.get(), - Self::REACTION_REMOVE => Self::REACTION_REMOVE.get(), - Self::REACTION_REMOVE_ALL => Self::REACTION_REMOVE_ALL.get(), - Self::REACTION_REMOVE_EMOJI => Self::REACTION_REMOVE_EMOJI.get(), - Self::READY => Self::READY.get(), - Self::RESUMED => Self::RESUMED.get(), - Self::ROLE_CREATE => Self::ROLE_CREATE.get(), - Self::ROLE_DELETE => Self::ROLE_DELETE.get(), - Self::ROLE_UPDATE => Self::ROLE_UPDATE.get(), - Self::STAGE_INSTANCE_CREATE => Self::STAGE_INSTANCE_CREATE.get(), - Self::STAGE_INSTANCE_DELETE => Self::STAGE_INSTANCE_DELETE.get(), - Self::STAGE_INSTANCE_UPDATE => Self::STAGE_INSTANCE_UPDATE.get(), - Self::THREAD_CREATE => Self::THREAD_CREATE.get(), - Self::THREAD_DELETE => Self::THREAD_DELETE.get(), - Self::THREAD_LIST_SYNC => Self::THREAD_LIST_SYNC.get(), - Self::THREAD_MEMBERS_UPDATE => Self::THREAD_MEMBERS_UPDATE.get(), - Self::THREAD_MEMBER_UPDATE => Self::THREAD_MEMBER_UPDATE.get(), - Self::THREAD_UPDATE => Self::THREAD_UPDATE.get(), - Self::TYPING_START => Self::TYPING_START.get(), - Self::UNAVAILABLE_GUILD => Self::UNAVAILABLE_GUILD.get(), - Self::USER_UPDATE => Self::USER_UPDATE.get(), - Self::VOICE_SERVER_UPDATE => Self::VOICE_SERVER_UPDATE.get(), - Self::VOICE_STATE_UPDATE => Self::VOICE_STATE_UPDATE.get(), - Self::WEBHOOKS_UPDATE => Self::WEBHOOKS_UPDATE.get(), + Self::AUTO_MODERATION_ACTION_EXECUTION => "AUTO_MODERATION_ACTION_EXECUTION", + Self::AUTO_MODERATION_RULE_CREATE => "AUTO_MODERATION_RULE_CREATE", + Self::AUTO_MODERATION_RULE_DELETE => "AUTO_MODERATION_RULE_DELETE", + Self::AUTO_MODERATION_RULE_UPDATE => "AUTO_MODERATION_RULE_UPDATE", + Self::BAN_ADD => "BAN_ADD", + Self::BAN_REMOVE => "BAN_REMOVE", + Self::CHANNEL_CREATE => "CHANNEL_CREATE", + Self::CHANNEL_DELETE => "CHANNEL_DELETE", + Self::CHANNEL_PINS_UPDATE => "CHANNEL_PINS_UPDATE", + Self::CHANNEL_UPDATE => "CHANNEL_UPDATE", + Self::COMMAND_PERMISSIONS_UPDATE => "COMMAND_PERMISSIONS_UPDATE", + Self::GIFT_CODE_UPDATE => "GIFT_CODE_UPDATE", + Self::GUILD_CREATE => "GUILD_CREATE", + Self::GUILD_DELETE => "GUILD_DELETE", + Self::GUILD_EMOJIS_UPDATE => "GUILD_EMOJIS_UPDATE", + Self::GUILD_INTEGRATIONS_UPDATE => "GUILD_INTEGRATIONS_UPDATE", + Self::GUILD_SCHEDULED_EVENT_CREATE => "GUILD_SCHEDULED_EVENT_CREATE", + Self::GUILD_SCHEDULED_EVENT_DELETE => "GUILD_SCHEDULED_EVENT_DELETE", + Self::GUILD_SCHEDULED_EVENT_UPDATE => "GUILD_SCHEDULED_EVENT_UPDATE", + Self::GUILD_SCHEDULED_EVENT_USER_ADD => "GUILD_SCHEDULED_EVENT_USER_ADD", + Self::GUILD_SCHEDULED_EVENT_USER_REMOVE => "GUILD_SCHEDULED_EVENT_USER_REMOVE", + Self::GUILD_STICKERS_UPDATE => "GUILD_STICKERS_UPDATE", + Self::GUILD_UPDATE => "GUILD_UPDATE", + Self::INTEGRATION_CREATE => "INTEGRATION_CREATE", + Self::INTEGRATION_DELETE => "INTEGRATION_DELETE", + Self::INTEGRATION_UPDATE => "INTEGRATION_UPDATE", + Self::INTERACTION_CREATE => "INTERACTION_CREATE", + Self::INVITE_CREATE => "INVITE_CREATE", + Self::INVITE_DELETE => "INVITE_DELETE", + Self::MEMBER_ADD => "MEMBER_ADD", + Self::MEMBER_CHUNK => "MEMBER_CHUNK", + Self::MEMBER_REMOVE => "MEMBER_REMOVE", + Self::MEMBER_UPDATE => "MEMBER_UPDATE", + Self::MESSAGE_CREATE => "MESSAGE_CREATE", + Self::MESSAGE_DELETE => "MESSAGE_DELETE", + Self::MESSAGE_DELETE_BULK => "MESSAGE_DELETE_BULK", + Self::MESSAGE_UPDATE => "MESSAGE_UPDATE", + Self::PRESENCES_REPLACE => "PRESENCES_REPLACE", + Self::PRESENCE_UPDATE => "PRESENCE_UPDATE", + Self::REACTION_ADD => "REACTION_ADD", + Self::REACTION_REMOVE => "REACTION_REMOVE", + Self::REACTION_REMOVE_ALL => "REACTION_REMOVE_ALL", + Self::REACTION_REMOVE_EMOJI => "REACTION_REMOVE_EMOJI", + Self::READY => "READY", + Self::RESUMED => "RESUMED", + Self::ROLE_CREATE => "ROLE_CREATE", + Self::ROLE_DELETE => "ROLE_DELETE", + Self::ROLE_UPDATE => "ROLE_UPDATE", + Self::STAGE_INSTANCE_CREATE => "STAGE_INSTANCE_CREATE", + Self::STAGE_INSTANCE_DELETE => "STAGE_INSTANCE_DELETE", + Self::STAGE_INSTANCE_UPDATE => "STAGE_INSTANCE_UPDATE", + Self::THREAD_CREATE => "THREAD_CREATE", + Self::THREAD_DELETE => "THREAD_DELETE", + Self::THREAD_LIST_SYNC => "THREAD_LIST_SYNC", + Self::THREAD_MEMBERS_UPDATE => "THREAD_MEMBERS_UPDATE", + Self::THREAD_MEMBER_UPDATE => "THREAD_MEMBER_UPDATE", + Self::THREAD_UPDATE => "THREAD_UPDATE", + Self::TYPING_START => "TYPING_START", + Self::UNAVAILABLE_GUILD => "UNAVAILABLE_GUILD", + Self::USER_UPDATE => "USER_UPDATE", + Self::VOICE_SERVER_UPDATE => "VOICE_SERVER_UPDATE", + Self::VOICE_STATE_UPDATE => "VOICE_STATE_UPDATE", + Self::WEBHOOKS_UPDATE => "WEBHOOKS_UPDATE", _ => return None, }) } @@ -257,7 +258,7 @@ impl AsRef for EventType { impl Debug for EventType { fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult { - f.write_str(self.get()) + f.write_str(self.name().unwrap_or_else(|| self.get())) } } diff --git a/twilight-model/src/gateway/opcode.rs b/twilight-model/src/gateway/opcode.rs index c39dfe87cce..37643086c99 100644 --- a/twilight-model/src/gateway/opcode.rs +++ b/twilight-model/src/gateway/opcode.rs @@ -1,4 +1,5 @@ use serde::{Deserialize, Serialize}; +use std::fmt::{Debug, Formatter, Result as FmtResult}; /// Gateway event opcodes. /// @@ -10,7 +11,7 @@ use serde::{Deserialize, Serialize}; /// [`PRESENCE_UPDATE`]: Self::PRESENCE_UPDATE /// [`REQUEST_GUILD_MEMBERS`]: Self::REQUEST_GUILD_MEMBERS /// [`VOICE_STATE_UPDATE`]: Self::VOICE_STATE_UPDATE -#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)] +#[derive(Clone, Copy, Deserialize, Eq, Hash, PartialEq, Serialize)] pub struct OpCode(u8); impl OpCode { @@ -77,8 +78,11 @@ impl OpCode { self.0 } - pub const fn name(self) -> &'static str { - match self { + /// 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", @@ -90,7 +94,20 @@ impl OpCode { Self::REQUEST_GUILD_MEMBERS => "REQUEST_GUILD_MEMBERS", Self::RESUME => "RESUME", Self::VOICE_STATE_UPDATE => "VOICE_STATE_UPDATE", - _ => "UNKNOWN", + _ => return None, + }) + } +} + +impl Debug for OpCode { + fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult { + if let Some(name) = self.name() { + f.debug_struct("OpCode") + .field("name", &name) + .field("value", &self.0) + .finish() + } else { + f.debug_tuple("OpCode").field(&self.0).finish() } } } diff --git a/twilight-model/src/gateway/presence/activity_type.rs b/twilight-model/src/gateway/presence/activity_type.rs index 9e7ba38fc85..b0e98d5a527 100644 --- a/twilight-model/src/gateway/presence/activity_type.rs +++ b/twilight-model/src/gateway/presence/activity_type.rs @@ -1,6 +1,7 @@ use serde::{Deserialize, Serialize}; +use std::fmt::{Debug, Formatter, Result as FmtResult}; -#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)] +#[derive(Clone, Copy, Deserialize, Eq, Hash, PartialEq, Serialize)] pub struct ActivityType(u8); impl ActivityType { @@ -31,6 +32,34 @@ impl ActivityType { pub const fn get(&self) -> u8 { self.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::PLAYING => "PLAYING", + Self::STREAMING => "STREAMING", + Self::LISTENING => "LISTENING", + Self::WATCHING => "WATCHING", + Self::CUSTOM => "CUSTOM", + Self::COMPETING => "COMPETING", + _ => return None, + }) + } +} + +impl Debug for ActivityType { + fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult { + if let Some(name) = self.name() { + f.debug_struct("ActivityType") + .field("name", &name) + .field("value", &self.0) + .finish() + } else { + f.debug_tuple("ActivityType").field(&self.0).finish() + } + } } impl Default for ActivityType { diff --git a/twilight-model/src/gateway/presence/status.rs b/twilight-model/src/gateway/presence/status.rs index d10fb955a52..20c07aaf0d9 100644 --- a/twilight-model/src/gateway/presence/status.rs +++ b/twilight-model/src/gateway/presence/status.rs @@ -36,6 +36,20 @@ impl Status { self.0.get() } + /// 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, + }) + } + /// Create a status from a set of bytes. const fn from_bytes(input: &[u8]) -> Self { Self(KnownString::from_bytes(input)) @@ -50,7 +64,7 @@ impl AsRef for Status { impl Debug for Status { fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult { - f.write_str(self.get()) + f.write_str(self.name().unwrap_or_else(|| self.get())) } } diff --git a/twilight-model/src/guild/afk_timeout.rs b/twilight-model/src/guild/afk_timeout.rs index 866cc1fb7e0..08ab5708b2a 100644 --- a/twilight-model/src/guild/afk_timeout.rs +++ b/twilight-model/src/guild/afk_timeout.rs @@ -1,5 +1,8 @@ use serde::{Deserialize, Serialize}; -use std::time::Duration; +use std::{ + fmt::{Debug, Formatter, Result as FmtResult}, + time::Duration, +}; /// Duration of a user being AFK before being timed out from a voice channel. /// @@ -14,7 +17,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); @@ -46,6 +49,33 @@ impl AfkTimeout { pub const fn get(self) -> u16 { self.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::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, + }) + } +} + +impl Debug for AfkTimeout { + fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult { + if let Some(name) = self.name() { + f.debug_struct("AfkTimeout") + .field("name", &name) + .field("value", &self.0) + .finish() + } else { + f.debug_tuple("AfkTimeout").field(&self.0).finish() + } + } } impl From for AfkTimeout { diff --git a/twilight-model/src/guild/audit_log/change_key.rs b/twilight-model/src/guild/audit_log/change_key.rs index a94f452a618..b97f47d1dc9 100644 --- a/twilight-model/src/guild/audit_log/change_key.rs +++ b/twilight-model/src/guild/audit_log/change_key.rs @@ -256,6 +256,88 @@ impl AuditLogChangeKey { self.0.get() } + /// 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::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, + }) + } + /// Create a scope from a set of bytes. const fn from_bytes(input: &[u8]) -> Self { Self(KnownString::from_bytes(input)) @@ -270,7 +352,7 @@ impl AsRef for AuditLogChangeKey { impl Debug for AuditLogChangeKey { fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult { - f.write_str(self.get()) + f.write_str(self.name().unwrap_or_else(|| self.get())) } } diff --git a/twilight-model/src/guild/audit_log/event_type.rs b/twilight-model/src/guild/audit_log/event_type.rs index bb8ea560cd8..2989f46f5e0 100644 --- a/twilight-model/src/guild/audit_log/event_type.rs +++ b/twilight-model/src/guild/audit_log/event_type.rs @@ -1,9 +1,10 @@ use serde::{Deserialize, Serialize}; +use std::fmt::{Debug, Formatter, Result as FmtResult}; /// Action to cause an [`AuditLogEntry`]. /// /// [`AuditLogEntry`]: super::AuditLogEntry -#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)] +#[derive(Clone, Copy, Deserialize, Eq, Hash, PartialEq, Serialize)] pub struct AuditLogEventType(u16); impl AuditLogEventType { @@ -301,6 +302,84 @@ impl AuditLogEventType { pub const fn get(&self) -> u16 { self.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::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 Debug for AuditLogEventType { + fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult { + if let Some(name) = self.name() { + f.debug_struct("AuditLogEventType") + .field("name", &name) + .field("value", &self.0) + .finish() + } else { + f.debug_tuple("AuditLogEventType").field(&self.0).finish() + } + } } impl From for AuditLogEventType { diff --git a/twilight-model/src/guild/auto_moderation/action.rs b/twilight-model/src/guild/auto_moderation/action.rs index 5259f2e0b7a..d57c789efe7 100644 --- a/twilight-model/src/guild/auto_moderation/action.rs +++ b/twilight-model/src/guild/auto_moderation/action.rs @@ -1,5 +1,6 @@ use crate::id::{marker::ChannelMarker, Id}; use serde::{Deserialize, Serialize}; +use std::fmt::{Debug, Formatter, Result as FmtResult}; /// An action which will execute whenever a rule is triggered. #[derive(Clone, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)] @@ -28,7 +29,7 @@ pub struct AutoModerationActionMetadata { } /// Type of [`AutoModerationAction`]. -#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)] +#[derive(Clone, Copy, Deserialize, Eq, Hash, PartialEq, Serialize)] pub struct AutoModerationActionType(u8); impl AutoModerationActionType { @@ -68,6 +69,33 @@ impl AutoModerationActionType { pub const fn get(&self) -> u8 { self.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::BLOCK_MESSAGE => "BLOCK_MESSAGE", + Self::SEND_ALERT_MESSAGE => "SEND_ALERT_MESSAGE", + Self::TIMEOUT => "TIMEOUT", + _ => return None, + }) + } +} + +impl Debug for AutoModerationActionType { + fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult { + if let Some(name) = self.name() { + f.debug_struct("AutoModerationActionType") + .field("name", &name) + .field("value", &self.0) + .finish() + } else { + f.debug_tuple("AutoModerationActionType") + .field(&self.0) + .finish() + } + } } impl From for AutoModerationActionType { diff --git a/twilight-model/src/guild/auto_moderation/event_type.rs b/twilight-model/src/guild/auto_moderation/event_type.rs index e98ccc717da..87a3baf2c81 100644 --- a/twilight-model/src/guild/auto_moderation/event_type.rs +++ b/twilight-model/src/guild/auto_moderation/event_type.rs @@ -1,7 +1,8 @@ use serde::{Deserialize, Serialize}; +use std::fmt::{Debug, Formatter, Result as FmtResult}; /// Indicates in what event context a rule should be checked. -#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)] +#[derive(Clone, Copy, Deserialize, Eq, Hash, PartialEq, Serialize)] pub struct AutoModerationEventType(u8); impl AutoModerationEventType { @@ -28,6 +29,31 @@ impl AutoModerationEventType { pub const fn get(&self) -> u8 { self.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::MESSAGE_SEND => "MESSAGE_SEND", + _ => return None, + }) + } +} + +impl Debug for AutoModerationEventType { + fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult { + if let Some(name) = self.name() { + f.debug_struct("AutoModerationEventType") + .field("name", &name) + .field("value", &self.0) + .finish() + } else { + f.debug_tuple("AutoModerationEventType") + .field(&self.0) + .finish() + } + } } impl From for AutoModerationEventType { diff --git a/twilight-model/src/guild/auto_moderation/preset_type.rs b/twilight-model/src/guild/auto_moderation/preset_type.rs index e9b62ba46ec..519928bfd72 100644 --- a/twilight-model/src/guild/auto_moderation/preset_type.rs +++ b/twilight-model/src/guild/auto_moderation/preset_type.rs @@ -1,7 +1,8 @@ use serde::{Deserialize, Serialize}; +use std::fmt::{Debug, Formatter, Result as FmtResult}; /// Internally pre-defined wordsets which will be searched for in content. -#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)] +#[derive(Clone, Copy, Deserialize, Eq, Hash, PartialEq, Serialize)] pub struct AutoModerationKeywordPresetType(u8); impl AutoModerationKeywordPresetType { @@ -34,6 +35,33 @@ impl AutoModerationKeywordPresetType { pub const fn get(&self) -> u8 { self.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::PROFANITY => "PROFANITY", + Self::SEXUAL_CONTENT => "SEXUAL_CONTENT", + Self::SLURS => "SLURS", + _ => return None, + }) + } +} + +impl Debug for AutoModerationKeywordPresetType { + fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult { + if let Some(name) = self.name() { + f.debug_struct("AutoModerationKeywordPresetType") + .field("name", &name) + .field("value", &self.0) + .finish() + } else { + f.debug_tuple("AutoModerationKeywordPresetType") + .field(&self.0) + .finish() + } + } } impl From for AutoModerationKeywordPresetType { diff --git a/twilight-model/src/guild/auto_moderation/trigger_type.rs b/twilight-model/src/guild/auto_moderation/trigger_type.rs index 96103adad7d..10a23227d5a 100644 --- a/twilight-model/src/guild/auto_moderation/trigger_type.rs +++ b/twilight-model/src/guild/auto_moderation/trigger_type.rs @@ -1,7 +1,8 @@ use serde::{Deserialize, Serialize}; +use std::fmt::{Debug, Formatter, Result as FmtResult}; /// Characterizes the type of content which can trigger the rule. -#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)] +#[derive(Clone, Copy, Deserialize, Eq, Hash, PartialEq, Serialize)] pub struct AutoModerationTriggerType(u8); impl AutoModerationTriggerType { @@ -43,6 +44,34 @@ impl AutoModerationTriggerType { pub const fn get(&self) -> u8 { self.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::KEYWORD => "KEYWORD", + Self::KEYWORD_PRESET => "KEYWORD_PRESET", + Self::MENTION_SPAM => "MENTION_SPAM", + Self::SPAM => "SPAM", + _ => return None, + }) + } +} + +impl Debug for AutoModerationTriggerType { + fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult { + if let Some(name) = self.name() { + f.debug_struct("AutoModerationTriggerType") + .field("name", &name) + .field("value", &self.0) + .finish() + } else { + f.debug_tuple("AutoModerationTriggerType") + .field(&self.0) + .finish() + } + } } impl From for AutoModerationTriggerType { diff --git a/twilight-model/src/guild/default_message_notification_level.rs b/twilight-model/src/guild/default_message_notification_level.rs index 07da9ae99a2..dec1718204c 100644 --- a/twilight-model/src/guild/default_message_notification_level.rs +++ b/twilight-model/src/guild/default_message_notification_level.rs @@ -1,6 +1,7 @@ use serde::{Deserialize, Serialize}; +use std::fmt::{Debug, Formatter, Result as FmtResult}; -#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)] +#[derive(Clone, Copy, Deserialize, Eq, Hash, PartialEq, Serialize)] pub struct DefaultMessageNotificationLevel(u8); impl DefaultMessageNotificationLevel { @@ -28,6 +29,32 @@ impl DefaultMessageNotificationLevel { pub const fn get(&self) -> u8 { self.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::ALL => "ALL", + Self::MENTIONS => "MENTIONS", + _ => return None, + }) + } +} + +impl Debug for DefaultMessageNotificationLevel { + fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult { + if let Some(name) = self.name() { + f.debug_struct("DefaultMessageNotificationLevel") + .field("name", &name) + .field("value", &self.0) + .finish() + } else { + f.debug_tuple("DefaultMessageNotificationLevel") + .field(&self.0) + .finish() + } + } } impl From for DefaultMessageNotificationLevel { diff --git a/twilight-model/src/guild/explicit_content_filter.rs b/twilight-model/src/guild/explicit_content_filter.rs index 98e05b1a15c..d12e418bb39 100644 --- a/twilight-model/src/guild/explicit_content_filter.rs +++ b/twilight-model/src/guild/explicit_content_filter.rs @@ -1,6 +1,7 @@ use serde::{Deserialize, Serialize}; +use std::fmt::{Debug, Formatter, Result as FmtResult}; -#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)] +#[derive(Clone, Copy, Deserialize, Eq, Hash, PartialEq, Serialize)] pub struct ExplicitContentFilter(u8); impl ExplicitContentFilter { @@ -28,6 +29,33 @@ impl ExplicitContentFilter { pub const fn get(&self) -> u8 { self.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::ALL_MEMBERS => "ALL_MEMBERS", + Self::MEMBERS_WITHOUT_ROLE => "MEMBERS_WITHOUT_ROLE", + Self::NONE => "NONE", + _ => return None, + }) + } +} + +impl Debug for ExplicitContentFilter { + fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult { + if let Some(name) = self.name() { + f.debug_struct("ExplicitContentFilter") + .field("name", &name) + .field("value", &self.0) + .finish() + } else { + f.debug_tuple("ExplicitContentFilter") + .field(&self.0) + .finish() + } + } } impl From for ExplicitContentFilter { diff --git a/twilight-model/src/guild/feature.rs b/twilight-model/src/guild/feature.rs index ff3148019b7..edf4dfbc69f 100644 --- a/twilight-model/src/guild/feature.rs +++ b/twilight-model/src/guild/feature.rs @@ -108,6 +108,40 @@ impl GuildFeature { self.0.get() } + /// 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, + }) + } + /// Create a guild feature from a set of bytes. const fn from_bytes(input: &[u8]) -> Self { Self(KnownString::from_bytes(input)) @@ -122,7 +156,7 @@ impl AsRef for GuildFeature { impl Debug for GuildFeature { fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult { - f.write_str(self.get()) + f.write_str(self.name().unwrap_or_else(|| self.get())) } } diff --git a/twilight-model/src/guild/integration_expire_behavior.rs b/twilight-model/src/guild/integration_expire_behavior.rs index 5351b668d26..c5f0874a64d 100644 --- a/twilight-model/src/guild/integration_expire_behavior.rs +++ b/twilight-model/src/guild/integration_expire_behavior.rs @@ -1,7 +1,8 @@ use serde::{Deserialize, Serialize}; +use std::fmt::{Debug, Formatter, Result as FmtResult}; /// Behavior to perform when the user's integration expires. -#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)] +#[derive(Clone, Copy, Deserialize, Eq, Hash, PartialEq, Serialize)] pub struct IntegrationExpireBehavior(u8); impl IntegrationExpireBehavior { @@ -31,6 +32,32 @@ impl IntegrationExpireBehavior { pub const fn get(&self) -> u8 { self.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::KICK => "KICK", + Self::REMOVE_ROLE => "REMOVE_ROLE", + _ => return None, + }) + } +} + +impl Debug for IntegrationExpireBehavior { + fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult { + if let Some(name) = self.name() { + f.debug_struct("IntegrationExpireBehavior") + .field("name", &name) + .field("value", &self.0) + .finish() + } else { + f.debug_tuple("IntegrationExpireBehavior") + .field(&self.0) + .finish() + } + } } impl From for IntegrationExpireBehavior { diff --git a/twilight-model/src/guild/integration_type.rs b/twilight-model/src/guild/integration_type.rs index b9e4cf2c2cc..1d5d7aeddb3 100644 --- a/twilight-model/src/guild/integration_type.rs +++ b/twilight-model/src/guild/integration_type.rs @@ -40,6 +40,18 @@ impl GuildIntegrationType { self.0.get() } + /// 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, + }) + } + /// Create a guild integration type from a set of bytes. const fn from_bytes(input: &[u8]) -> Self { Self(KnownString::from_bytes(input)) @@ -54,7 +66,7 @@ impl AsRef for GuildIntegrationType { impl Debug for GuildIntegrationType { fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult { - f.write_str(self.get()) + f.write_str(self.name().unwrap_or_else(|| self.get())) } } diff --git a/twilight-model/src/guild/invite/target_type.rs b/twilight-model/src/guild/invite/target_type.rs index fd5f9f317b6..f3e384f1540 100644 --- a/twilight-model/src/guild/invite/target_type.rs +++ b/twilight-model/src/guild/invite/target_type.rs @@ -1,6 +1,7 @@ use serde::{Deserialize, Serialize}; +use std::fmt::{Debug, Formatter, Result as FmtResult}; -#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)] +#[derive(Clone, Copy, Deserialize, Eq, Hash, PartialEq, Serialize)] pub struct TargetType(u8); impl TargetType { @@ -28,6 +29,30 @@ impl TargetType { pub const fn get(&self) -> u8 { self.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::EMBEDDED_APPLICATION => "EMBEDDED_APPLICATION", + Self::STREAM => "STREAM", + _ => return None, + }) + } +} + +impl Debug for TargetType { + fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult { + if let Some(name) = self.name() { + f.debug_struct("TargetType") + .field("name", &name) + .field("value", &self.0) + .finish() + } else { + f.debug_tuple("TargetType").field(&self.0).finish() + } + } } impl From for TargetType { diff --git a/twilight-model/src/guild/mfa_level.rs b/twilight-model/src/guild/mfa_level.rs index e993058a969..9dba4fe80f2 100644 --- a/twilight-model/src/guild/mfa_level.rs +++ b/twilight-model/src/guild/mfa_level.rs @@ -1,6 +1,7 @@ use serde::{Deserialize, Serialize}; +use std::fmt::{Debug, Formatter, Result as FmtResult}; -#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)] +#[derive(Clone, Copy, Deserialize, Eq, Hash, PartialEq, Serialize)] pub struct MfaLevel(u8); impl MfaLevel { @@ -27,6 +28,30 @@ impl MfaLevel { pub const fn get(&self) -> u8 { self.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::ELEVATED => "ELEVATED", + Self::NONE => "NONE", + _ => return None, + }) + } +} + +impl Debug for MfaLevel { + fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult { + if let Some(name) = self.name() { + f.debug_struct("MfaLevel") + .field("name", &name) + .field("value", &self.0) + .finish() + } else { + f.debug_tuple("MfaLevel").field(&self.0).finish() + } + } } impl From for MfaLevel { diff --git a/twilight-model/src/guild/nsfw_level.rs b/twilight-model/src/guild/nsfw_level.rs index 28b8750d765..5bf980c85a9 100644 --- a/twilight-model/src/guild/nsfw_level.rs +++ b/twilight-model/src/guild/nsfw_level.rs @@ -1,6 +1,7 @@ use serde::{Deserialize, Serialize}; +use std::fmt::{Debug, Formatter, Result as FmtResult}; -#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)] +#[derive(Clone, Copy, Deserialize, Eq, Hash, PartialEq, Serialize)] pub struct NSFWLevel(u8); impl NSFWLevel { @@ -29,6 +30,32 @@ impl NSFWLevel { pub const fn get(&self) -> u8 { self.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::AGE_RESTRICTED => "AGE_RESTRICTED", + Self::DEFAULT => "DEFAULT", + Self::EXPLICIT => "EXPLICIT", + Self::SAFE => "SAFE", + _ => return None, + }) + } +} + +impl Debug for NSFWLevel { + fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult { + if let Some(name) = self.name() { + f.debug_struct("NSFWLevel") + .field("name", &name) + .field("value", &self.0) + .finish() + } else { + f.debug_tuple("NSFWLevel").field(&self.0).finish() + } + } } impl From for NSFWLevel { diff --git a/twilight-model/src/guild/premium_tier.rs b/twilight-model/src/guild/premium_tier.rs index 9009ddd529a..2508a61e9e4 100644 --- a/twilight-model/src/guild/premium_tier.rs +++ b/twilight-model/src/guild/premium_tier.rs @@ -1,6 +1,7 @@ use serde::{Deserialize, Serialize}; +use std::fmt::{Debug, Formatter, Result as FmtResult}; -#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)] +#[derive(Clone, Copy, Deserialize, Eq, Hash, PartialEq, Serialize)] pub struct PremiumTier(u8); impl PremiumTier { @@ -29,6 +30,32 @@ impl PremiumTier { pub const fn get(&self) -> u8 { self.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::NONE => "NONE", + Self::TIER_1 => "TIER_1", + Self::TIER_2 => "TIER_2", + Self::TIER_3 => "TIER_3", + _ => return None, + }) + } +} + +impl Debug for PremiumTier { + fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult { + if let Some(name) = self.name() { + f.debug_struct("PremiumTier") + .field("name", &name) + .field("value", &self.0) + .finish() + } else { + f.debug_tuple("PremiumTier").field(&self.0).finish() + } + } } impl Default for PremiumTier { diff --git a/twilight-model/src/guild/scheduled_event/mod.rs b/twilight-model/src/guild/scheduled_event/mod.rs index 68061e49926..94cf22c4a72 100644 --- a/twilight-model/src/guild/scheduled_event/mod.rs +++ b/twilight-model/src/guild/scheduled_event/mod.rs @@ -16,6 +16,7 @@ use crate::{ util::{ImageHash, Timestamp}, }; use serde::{Deserialize, Serialize}; +use std::fmt::{Debug, Formatter, Result as FmtResult}; /// Representation of a scheduled event. /// @@ -89,7 +90,7 @@ pub struct EntityMetadata { } /// Type of event. -#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)] +#[derive(Clone, Copy, Deserialize, Eq, Hash, PartialEq, Serialize)] pub struct EntityType(u8); impl EntityType { @@ -122,6 +123,31 @@ impl EntityType { pub const fn get(&self) -> u8 { self.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::EXTERNAL => "EXTERNAL", + Self::STAGE_INSTANCE => "STAGE_INSTANCE", + Self::VOICE => "VOICE", + _ => return None, + }) + } +} + +impl Debug for EntityType { + fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult { + if let Some(name) = self.name() { + f.debug_struct("EntityType") + .field("name", &name) + .field("value", &self.0) + .finish() + } else { + f.debug_tuple("EntityType").field(&self.0).finish() + } + } } impl From for EntityType { @@ -137,7 +163,7 @@ impl From for u8 { } /// Privacy level of an event. -#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)] +#[derive(Clone, Copy, Deserialize, Eq, Hash, PartialEq, Serialize)] pub struct PrivacyLevel(u8); impl PrivacyLevel { @@ -164,6 +190,29 @@ impl PrivacyLevel { pub const fn get(&self) -> u8 { self.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::GUILD_ONLY => "GUILD_ONLY", + _ => return None, + }) + } +} + +impl Debug for PrivacyLevel { + fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult { + if let Some(name) = self.name() { + f.debug_struct("PrivacyLevel") + .field("name", &name) + .field("value", &self.0) + .finish() + } else { + f.debug_tuple("PrivacyLevel").field(&self.0).finish() + } + } } impl From for PrivacyLevel { @@ -179,7 +228,7 @@ impl From for u8 { } /// Status of an event. -#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)] +#[derive(Clone, Copy, Deserialize, Eq, Hash, PartialEq, Serialize)] pub struct Status(u8); impl Status { @@ -219,6 +268,32 @@ impl Status { pub const fn get(&self) -> u8 { self.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::ACTIVE => "ACTIVE", + Self::SCHEDULED => "SCHEDULED", + Self::COMPLETED => "COMPLETED", + Self::CANCELLED => "CANCELLED", + _ => return None, + }) + } +} + +impl Debug for Status { + fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult { + if let Some(name) = self.name() { + f.debug_struct("Status") + .field("name", &name) + .field("value", &self.0) + .finish() + } else { + f.debug_tuple("Status").field(&self.0).finish() + } + } } impl From for Status { diff --git a/twilight-model/src/guild/verification_level.rs b/twilight-model/src/guild/verification_level.rs index 1721a666e68..2feae93b128 100644 --- a/twilight-model/src/guild/verification_level.rs +++ b/twilight-model/src/guild/verification_level.rs @@ -1,6 +1,7 @@ use serde::{Deserialize, Serialize}; +use std::fmt::{Debug, Formatter, Result as FmtResult}; -#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, Ord, PartialEq, PartialOrd, Serialize)] +#[derive(Clone, Copy, Deserialize, Eq, Hash, Ord, PartialEq, PartialOrd, Serialize)] pub struct VerificationLevel(u8); impl VerificationLevel { @@ -30,6 +31,33 @@ impl VerificationLevel { pub const fn get(&self) -> u8 { self.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::NONE => "NONE", + Self::LOW => "LOW", + Self::MEDIUM => "MEDIUM", + Self::HIGH => "HIGH", + Self::VERY_HIGH => "VERY_HIGH", + _ => return None, + }) + } +} + +impl Debug for VerificationLevel { + fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult { + if let Some(name) = self.name() { + f.debug_struct("VerificationLevel") + .field("name", &name) + .field("value", &self.0) + .finish() + } else { + f.debug_tuple("VerificationLevel").field(&self.0).finish() + } + } } impl From for VerificationLevel { diff --git a/twilight-model/src/http/interaction.rs b/twilight-model/src/http/interaction.rs index 2a4e1000cbf..7268edc4d06 100644 --- a/twilight-model/src/http/interaction.rs +++ b/twilight-model/src/http/interaction.rs @@ -6,6 +6,7 @@ use crate::{ channel::message::{AllowedMentions, Component, Embed, MessageFlags}, }; use serde::{Deserialize, Serialize}; +use std::fmt::{Debug, Formatter, Result as FmtResult}; /// Interaction response sent to Discord. /// @@ -75,7 +76,7 @@ pub struct InteractionResponseData { } /// Type of interaction response. -#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)] +#[derive(Clone, Copy, Deserialize, Eq, Hash, PartialEq, Serialize)] pub struct InteractionResponseType(u8); impl InteractionResponseType { @@ -126,6 +127,39 @@ impl InteractionResponseType { pub const fn get(&self) -> u8 { self.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::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 Debug for InteractionResponseType { + fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult { + if let Some(name) = self.name() { + f.debug_struct("InteractionResponseType") + .field("name", &name) + .field("value", &self.0) + .finish() + } else { + f.debug_tuple("InteractionResponseType") + .field(&self.0) + .finish() + } + } } impl From for InteractionResponseType { diff --git a/twilight-model/src/http/permission_overwrite.rs b/twilight-model/src/http/permission_overwrite.rs index 464d074e609..62d3bea0ae7 100644 --- a/twilight-model/src/http/permission_overwrite.rs +++ b/twilight-model/src/http/permission_overwrite.rs @@ -5,6 +5,7 @@ use crate::{ id::{marker::GenericMarker, Id}, }; use serde::{Deserialize, Serialize}; +use std::fmt::{Debug, Formatter, Result as FmtResult}; /// Permission overwrite data for a role or member. #[derive(Clone, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)] @@ -19,7 +20,7 @@ pub struct PermissionOverwrite { } /// Type of a permission overwrite target. -#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)] +#[derive(Clone, Copy, Deserialize, Eq, Hash, PartialEq, Serialize)] pub struct PermissionOverwriteType(u8); impl PermissionOverwriteType { @@ -49,6 +50,32 @@ impl PermissionOverwriteType { pub const fn get(&self) -> u8 { self.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 Debug for PermissionOverwriteType { + fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult { + if let Some(name) = self.name() { + f.debug_struct("PermissionOverwriteType") + .field("name", &name) + .field("value", &self.0) + .finish() + } else { + f.debug_tuple("PermissionOverwriteType") + .field(&self.0) + .finish() + } + } } impl From for PermissionOverwriteType { diff --git a/twilight-model/src/oauth/scope.rs b/twilight-model/src/oauth/scope.rs index 0c5d09acf61..7eabb76f1b6 100644 --- a/twilight-model/src/oauth/scope.rs +++ b/twilight-model/src/oauth/scope.rs @@ -176,6 +176,45 @@ impl Scope { self.0.get() } + /// 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, + }) + } + /// Create a scope from a set of bytes. const fn from_bytes(input: &[u8]) -> Self { Self(KnownString::from_bytes(input)) @@ -190,7 +229,7 @@ impl AsRef for Scope { impl Debug for Scope { fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult { - f.write_str(self.get()) + f.write_str(self.name().unwrap_or_else(|| self.get())) } } diff --git a/twilight-model/src/oauth/team/membership_state.rs b/twilight-model/src/oauth/team/membership_state.rs index 2441f12312b..dab9e5e7a56 100644 --- a/twilight-model/src/oauth/team/membership_state.rs +++ b/twilight-model/src/oauth/team/membership_state.rs @@ -1,6 +1,7 @@ use serde::{Deserialize, Serialize}; +use std::fmt::{Debug, Formatter, Result as FmtResult}; -#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)] +#[derive(Clone, Copy, Deserialize, Eq, Hash, PartialEq, Serialize)] pub struct TeamMembershipState(u8); impl TeamMembershipState { @@ -27,6 +28,30 @@ impl TeamMembershipState { pub const fn get(&self) -> u8 { self.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::ACCEPTED => "ACCEPTED", + Self::INVITED => "INVITED", + _ => return None, + }) + } +} + +impl Debug for TeamMembershipState { + fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult { + if let Some(name) = self.name() { + f.debug_struct("TeamMembershipState") + .field("name", &name) + .field("value", &self.0) + .finish() + } else { + f.debug_tuple("TeamMembershipState").field(&self.0).finish() + } + } } impl From for TeamMembershipState { diff --git a/twilight-model/src/user/connection_visibility.rs b/twilight-model/src/user/connection_visibility.rs index 995d444ab49..0004abf249d 100644 --- a/twilight-model/src/user/connection_visibility.rs +++ b/twilight-model/src/user/connection_visibility.rs @@ -1,6 +1,7 @@ use serde::{Deserialize, Serialize}; +use std::fmt::{Debug, Formatter, Result as FmtResult}; -#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)] +#[derive(Clone, Copy, Deserialize, Eq, Hash, PartialEq, Serialize)] pub struct ConnectionVisibility(u8); impl ConnectionVisibility { @@ -30,6 +31,32 @@ impl ConnectionVisibility { pub const fn get(&self) -> u8 { self.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::EVERYONE => "EVERYONE", + Self::NONE => "NONE", + _ => return None, + }) + } +} + +impl Debug for ConnectionVisibility { + fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult { + if let Some(name) = self.name() { + f.debug_struct("ConnectionVisibility") + .field("name", &name) + .field("value", &self.0) + .finish() + } else { + f.debug_tuple("ConnectionVisibility") + .field(&self.0) + .finish() + } + } } impl From for ConnectionVisibility { diff --git a/twilight-model/src/user/premium_type.rs b/twilight-model/src/user/premium_type.rs index dcf146c8d91..9ef873fb5bb 100644 --- a/twilight-model/src/user/premium_type.rs +++ b/twilight-model/src/user/premium_type.rs @@ -1,9 +1,10 @@ use serde::{Deserialize, Serialize}; +use std::fmt::{Debug, Formatter, Result as FmtResult}; /// Type of premium tier for a [`User`]. /// /// [`User`]: super::User -#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)] +#[derive(Clone, Copy, Deserialize, Eq, Hash, PartialEq, Serialize)] pub struct PremiumType(u8); impl PremiumType { @@ -39,6 +40,32 @@ impl PremiumType { pub const fn get(&self) -> u8 { self.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::NITRO_BASIC => "NITRO_BASIC", + Self::NITRO_CLASSIC => "NITRO_CLASSIC", + Self::NITRO => "NITRO", + Self::NONE => "NONE", + _ => return None, + }) + } +} + +impl Debug for PremiumType { + fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult { + if let Some(name) = self.name() { + f.debug_struct("PremiumType") + .field("name", &name) + .field("value", &self.0) + .finish() + } else { + f.debug_tuple("PremiumType").field(&self.0).finish() + } + } } impl From for PremiumType { diff --git a/twilight-model/src/util/known_string.rs b/twilight-model/src/util/known_string.rs index 3579bdc8d81..c91be9fbb8a 100644 --- a/twilight-model/src/util/known_string.rs +++ b/twilight-model/src/util/known_string.rs @@ -74,7 +74,7 @@ use std::{ /// ``` /// use serde::{Deserialize, Serialize}; /// -/// #[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)] +/// #[derive(Clone, Copy, Deserialize, Eq, Hash, PartialEq, Serialize)] /// pub struct Letter([u8; 1]); /// /// impl Letter { diff --git a/twilight-model/src/voice/close_code.rs b/twilight-model/src/voice/close_code.rs index e280ae03f3e..f9c454647b3 100644 --- a/twilight-model/src/voice/close_code.rs +++ b/twilight-model/src/voice/close_code.rs @@ -1,7 +1,8 @@ use serde::{Deserialize, Serialize}; +use std::fmt::{Debug, Formatter, Result as FmtResult}; /// Voice gateway close event codes. -#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)] +#[derive(Clone, Copy, Deserialize, Eq, Hash, PartialEq, Serialize)] pub struct CloseCode(u16); impl CloseCode { @@ -61,6 +62,40 @@ impl CloseCode { pub const fn get(&self) -> u16 { self.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::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 Debug for CloseCode { + fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult { + if let Some(name) = self.name() { + f.debug_struct("CloseCode") + .field("name", &name) + .field("value", &self.0) + .finish() + } else { + f.debug_tuple("CloseCode").field(&self.0).finish() + } + } } impl From for CloseCode { diff --git a/twilight-model/src/voice/opcode.rs b/twilight-model/src/voice/opcode.rs index 679d0540e73..18cc3baade8 100644 --- a/twilight-model/src/voice/opcode.rs +++ b/twilight-model/src/voice/opcode.rs @@ -1,7 +1,8 @@ use serde::{Deserialize, Serialize}; +use std::fmt::{Debug, Formatter, Result as FmtResult}; /// Voice gateway opcodes. -#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)] +#[derive(Clone, Copy, Deserialize, Eq, Hash, PartialEq, Serialize)] pub struct OpCode(u8); impl OpCode { @@ -58,6 +59,39 @@ impl OpCode { pub const fn get(&self) -> u8 { self.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::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 Debug for OpCode { + fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult { + if let Some(name) = self.name() { + f.debug_struct("OpCode") + .field("name", &name) + .field("value", &self.0) + .finish() + } else { + f.debug_tuple("OpCode").field(&self.0).finish() + } + } } impl From for OpCode { From f0792df8ce1fa268fa409cce679f23bf576146a5 Mon Sep 17 00:00:00 2001 From: Zeyla Hellyer Date: Wed, 18 Jan 2023 11:43:27 -0500 Subject: [PATCH 07/10] fix ci --- twilight-gateway/src/error.rs | 8 ++++++-- twilight-validate/src/channel.rs | 6 +++++- twilight-validate/src/component.rs | 14 ++++++++++++-- 3 files changed, 23 insertions(+), 5 deletions(-) diff --git a/twilight-gateway/src/error.rs b/twilight-gateway/src/error.rs index 89fb00095fd..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 => { @@ -340,7 +344,7 @@ mod tests { ReceiveMessageErrorType::FatallyClosed { close_code: CloseCode::INVALID_INTENTS, }, - "shard fatally closed: Invalid Intents", + "shard fatally closed: INVALID_INTENTS", ), (ReceiveMessageErrorType::Io, "websocket connection error"), ( diff --git a/twilight-validate/src/channel.rs b/twilight-validate/src/channel.rs index 5ba892831bb..1de0586dbbf 100644 --- a/twilight-validate/src/channel.rs +++ b/twilight-validate/src/channel.rs @@ -75,7 +75,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") } diff --git a/twilight-validate/src/component.rs b/twilight-validate/src/component.rs index bed2ee1c7f7..cf5c2ac4f0c 100644 --- a/twilight-validate/src/component.rs +++ b/twilight-validate/src/component.rs @@ -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") } From 9f572a8d08508773ff74e4d94eddc11c2848ad16 Mon Sep 17 00:00:00 2001 From: Zeyla Hellyer Date: Thu, 19 Jan 2023 09:48:33 -0500 Subject: [PATCH 08/10] known_string docs --- twilight-model/src/util/known_string.rs | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/twilight-model/src/util/known_string.rs b/twilight-model/src/util/known_string.rs index c91be9fbb8a..7ccee64bf0b 100644 --- a/twilight-model/src/util/known_string.rs +++ b/twilight-model/src/util/known_string.rs @@ -10,7 +10,8 @@ use std::{ str::{self, FromStr}, }; -/// Bytes container with some abstractions intended for storing strings. +/// 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 @@ -60,9 +61,9 @@ use std::{ /// } /// ``` /// -/// The reason for this is unobvious: it's because `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: +/// 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)]` From 48eaf6c2c946331aee92bdb1eedc82e317cfd89b Mon Sep 17 00:00:00 2001 From: Zeyla Hellyer Date: Thu, 19 Jan 2023 09:48:39 -0500 Subject: [PATCH 09/10] test known_string --- twilight-model/src/util/known_string.rs | 97 +++++++++++++++++++------ 1 file changed, 73 insertions(+), 24 deletions(-) diff --git a/twilight-model/src/util/known_string.rs b/twilight-model/src/util/known_string.rs index 7ccee64bf0b..5068652f082 100644 --- a/twilight-model/src/util/known_string.rs +++ b/twilight-model/src/util/known_string.rs @@ -72,7 +72,7 @@ use std::{ /// 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)] @@ -102,8 +102,8 @@ use std::{ /// ``` /// /// As a bonus, we get the efficiency of storing on the stack, low allocation -/// sizes (subject to the length of the bytes array), and we get to derive Copy, -/// which means match statements look pleasant. +/// 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], @@ -166,9 +166,9 @@ impl KnownString { /// /// Panics if the value is not UTF-8 valid. pub fn get(&self) -> &str { - let string = str::from_utf8(&self.bytes).unwrap(); - - string.trim_matches(char::from(0)) + str::from_utf8(&self.bytes) + .expect("string is not utf8 valid") + .trim_matches(char::from(0)) } } @@ -241,8 +241,8 @@ mod tests { use super::KnownString; use serde::{Deserialize, Serialize}; use serde_test::Token; - use static_assertions::assert_impl_all; - use std::{fmt::Debug, hash::Hash, str::FromStr, string::ToString}; + 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, @@ -259,31 +259,51 @@ mod tests { ToString, TryFrom<&'static str>, ); + const_assert_eq!(16, mem::size_of::>()); #[test] - fn new() { - let string = KnownString::<64>::from_str("BOT").unwrap(); - - let mut with_null_bytes = [0; 64]; - with_null_bytes[0] = b'B'; - with_null_bytes[1] = b'O'; - with_null_bytes[2] = b'T'; - assert_eq!(&string.bytes, &with_null_bytes); + fn as_ref() { + let string = KnownString::<3>::from_bytes(b"bot"); + + assert_eq!("bot", string.as_ref()); } #[test] - fn get() { - assert_eq!( - "identify", - KnownString::<64>::from_str("identify").unwrap().get() - ); + fn debug() { + let string = KnownString::<3>::from_bytes(b"bot"); + + assert_eq!("bot", format!("{string:?}")); } #[test] - fn serde() { - let string = KnownString::<64>::from_str("test").unwrap(); + 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]); + } - serde_test::assert_tokens(&string, &[Token::Str("test")]); + #[test] + #[should_panic] + 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] @@ -297,4 +317,33 @@ mod tests { 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); + } } From 870d338171d047453fb21ac08aeb28d61093c1b7 Mon Sep 17 00:00:00 2001 From: Zeyla Hellyer Date: Sun, 5 Feb 2023 22:31:33 -0800 Subject: [PATCH 10/10] temp use a macro for the diff --- .../src/application/command/command_type.rs | 47 +----- .../src/application/command/option.rs | 52 +------ .../src/application/command/permissions.rs | 49 +----- .../interaction/interaction_type.rs | 47 +----- twilight-model/src/channel/channel_type.rs | 47 +----- twilight-model/src/channel/forum.rs | 93 +----------- .../src/channel/message/activity.rs | 47 +----- .../src/channel/message/allowed_mentions.rs | 68 +-------- .../src/channel/message/component/button.rs | 47 +----- .../src/channel/message/component/kind.rs | 47 +----- .../channel/message/component/text_input.rs | 47 +----- twilight-model/src/channel/message/kind.rs | 47 +----- .../channel/message/sticker/format_type.rs | 47 +----- .../src/channel/message/sticker/kind.rs | 47 +----- .../src/channel/permission_overwrite.rs | 49 +----- .../channel/stage_instance/privacy_level.rs | 47 +----- .../channel/thread/auto_archive_duration.rs | 53 +------ .../src/channel/video_quality_mode.rs | 47 +----- twilight-model/src/channel/webhook/kind.rs | 47 +----- twilight-model/src/gateway/close_code.rs | 47 +----- twilight-model/src/gateway/event/kind.rs | 68 +-------- twilight-model/src/gateway/opcode.rs | 47 +----- .../src/gateway/presence/activity_type.rs | 47 +----- twilight-model/src/gateway/presence/status.rs | 68 +-------- twilight-model/src/guild/afk_timeout.rs | 39 +---- .../src/guild/audit_log/change_key.rs | 68 +-------- .../src/guild/audit_log/event_type.rs | 47 +----- .../src/guild/auto_moderation/action.rs | 49 +----- .../src/guild/auto_moderation/event_type.rs | 49 +----- .../src/guild/auto_moderation/preset_type.rs | 49 +----- .../src/guild/auto_moderation/trigger_type.rs | 49 +----- .../default_message_notification_level.rs | 49 +----- .../src/guild/explicit_content_filter.rs | 51 +------ twilight-model/src/guild/feature.rs | 68 +-------- .../src/guild/integration_expire_behavior.rs | 49 +----- twilight-model/src/guild/integration_type.rs | 68 +-------- .../src/guild/invite/target_type.rs | 47 +----- twilight-model/src/guild/mfa_level.rs | 48 +----- twilight-model/src/guild/nsfw_level.rs | 50 +------ twilight-model/src/guild/premium_tier.rs | 50 +------ .../src/guild/scheduled_event/mod.rs | 139 +----------------- .../src/guild/verification_level.rs | 51 +------ twilight-model/src/http/interaction.rs | 49 +----- .../src/http/permission_overwrite.rs | 49 +----- twilight-model/src/lib.rs | 4 +- twilight-model/src/oauth/scope.rs | 68 +-------- .../src/oauth/team/membership_state.rs | 48 +----- .../src/user/connection_visibility.rs | 49 +----- twilight-model/src/user/premium_type.rs | 47 +----- twilight-model/src/util/mod.rs | 116 +++++++++++++++ twilight-model/src/voice/close_code.rs | 47 +----- twilight-model/src/voice/opcode.rs | 47 +----- 52 files changed, 191 insertions(+), 2605 deletions(-) diff --git a/twilight-model/src/application/command/command_type.rs b/twilight-model/src/application/command/command_type.rs index 015dc0840fe..1979a8a0230 100644 --- a/twilight-model/src/application/command/command_type.rs +++ b/twilight-model/src/application/command/command_type.rs @@ -1,5 +1,4 @@ use serde::{Deserialize, Serialize}; -use std::fmt::{Debug, Formatter, Result as FmtResult}; // Keep in sync with `twilight-validate::command`! #[derive(Clone, Copy, Deserialize, Eq, Hash, PartialEq, Serialize)] @@ -21,27 +20,6 @@ impl CommandType { /// Appears when a user right clicks or taps on a message. pub const MESSAGE: Self = Self::new(3); - /// Create a new command type from a dynamic value. - /// - /// The provided value isn't validated. Known valid values are associated - /// constants such as [`CHAT_INPUT`][`Self::CHAT_INPUT`]. - pub const fn new(command_type: u8) -> Self { - Self(command_type) - } - - /// Retrieve the value of the command type. - /// - /// # Examples - /// - /// ``` - /// use twilight_model::application::command::CommandType; - /// - /// assert_eq!(3, CommandType::MESSAGE.get()); - /// ``` - pub const fn get(&self) -> u8 { - self.0 - } - /// Name of the associated constant. /// /// Returns `None` if the value doesn't have a defined constant. @@ -55,30 +33,7 @@ impl CommandType { } } -impl Debug for CommandType { - fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult { - if let Some(name) = self.name() { - f.debug_struct("CommandType") - .field("name", &name) - .field("value", &self.0) - .finish() - } else { - f.debug_tuple("CommandType").field(&self.0).finish() - } - } -} - -impl From for CommandType { - fn from(value: u8) -> Self { - Self(value) - } -} - -impl From for u8 { - fn from(value: CommandType) -> Self { - value.get() - } -} +impl_typed!(CommandType, u8); #[cfg(test)] mod tests { diff --git a/twilight-model/src/application/command/option.rs b/twilight-model/src/application/command/option.rs index a205c2fcdfd..af8b3091a04 100644 --- a/twilight-model/src/application/command/option.rs +++ b/twilight-model/src/application/command/option.rs @@ -1,10 +1,6 @@ use crate::channel::ChannelType; use serde::{Deserialize, Serialize}; -use std::{ - cmp::Eq, - collections::HashMap, - fmt::{Debug, Formatter, Result as FmtResult}, -}; +use std::{cmp::Eq, collections::HashMap}; /// Option for a [`Command`]. /// @@ -229,27 +225,6 @@ impl CommandOptionType { pub const ATTACHMENT: Self = Self::new(11); - /// Create a new command option type from a dynamic value. - /// - /// The provided value isn't validated. Known valid values are associated - /// constants such as [`STRING`][`Self::STRING`]. - pub const fn new(command_option_type: u8) -> Self { - Self(command_option_type) - } - - /// Retrieve the value of the command option type. - /// - /// # Examples - /// - /// ``` - /// use twilight_model::application::command::CommandOptionType; - /// - /// assert_eq!(4, CommandOptionType::INTEGER.get()); - /// ``` - pub const fn get(&self) -> u8 { - self.0 - } - /// Name of the associated constant. /// /// Returns `None` if the value doesn't have a defined constant. @@ -271,30 +246,7 @@ impl CommandOptionType { } } -impl Debug for CommandOptionType { - fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult { - if let Some(name) = self.name() { - f.debug_struct("CommandOptionType") - .field("name", &name) - .field("value", &self.0) - .finish() - } else { - f.debug_tuple("CommandOptionType").field(&self.0).finish() - } - } -} - -impl From for CommandOptionType { - fn from(value: u8) -> Self { - Self(value) - } -} - -impl From for u8 { - fn from(value: CommandOptionType) -> Self { - value.get() - } -} +impl_typed!(CommandOptionType, u8); #[cfg(test)] mod tests { diff --git a/twilight-model/src/application/command/permissions.rs b/twilight-model/src/application/command/permissions.rs index c7763d8e656..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 std::fmt::{Debug, Formatter, Result as FmtResult}; /// List of [`CommandPermission`]s for a command in a guild. #[derive(Clone, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)] @@ -96,27 +95,6 @@ impl CommandPermissionDataType { pub const CHANNEL: Self = Self::new(3); - /// Create a new command permission data type from a dynamic value. - /// - /// The provided value isn't validated. Known valid values are associated - /// constants such as [`ROLE`][`Self::ROLE`]. - pub const fn new(command_permission_data_type: u8) -> Self { - Self(command_permission_data_type) - } - - /// Retrieve the value of the command permission data type. - /// - /// # Examples - /// - /// ``` - /// use twilight_model::application::command::permissions::CommandPermissionDataType; - /// - /// assert_eq!(3, CommandPermissionDataType::CHANNEL.get()); - /// ``` - pub const fn get(&self) -> u8 { - self.0 - } - /// Name of the associated constant. /// /// Returns `None` if the value doesn't have a defined constant. @@ -130,32 +108,7 @@ impl CommandPermissionDataType { } } -impl Debug for CommandPermissionDataType { - fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult { - if let Some(name) = self.name() { - f.debug_struct("CommandPermissionDataType") - .field("name", &name) - .field("value", &self.0) - .finish() - } else { - f.debug_tuple("CommandPermissionDataType") - .field(&self.0) - .finish() - } - } -} - -impl From for CommandPermissionDataType { - fn from(value: u8) -> Self { - Self(value) - } -} - -impl From for u8 { - fn from(value: CommandPermissionDataType) -> Self { - value.get() - } -} +impl_typed!(CommandPermissionDataType, u8); impl<'de> Deserialize<'de> for CommandPermission { fn deserialize>(deserializer: D) -> Result { diff --git a/twilight-model/src/application/interaction/interaction_type.rs b/twilight-model/src/application/interaction/interaction_type.rs index 8fac0af120b..5dd3c081b82 100644 --- a/twilight-model/src/application/interaction/interaction_type.rs +++ b/twilight-model/src/application/interaction/interaction_type.rs @@ -1,5 +1,4 @@ use serde::{Deserialize, Serialize}; -use std::fmt::{Debug, Formatter, Result as FmtResult}; /// Type of interaction. /// @@ -31,27 +30,6 @@ impl InteractionType { /// Interaction involves a modal submit. pub const MODAL_SUBMIT: Self = Self::new(5); - /// Create a new interaction type from a dynamic value. - /// - /// The provided value isn't validated. Known valid values are associated - /// constants such as [`MODAL_SUBMIT`][`Self::MODAL_SUBMIT`]. - pub const fn new(connection_visibility: u8) -> Self { - Self(connection_visibility) - } - - /// Retrieve the value of the interaction type. - /// - /// # Examples - /// - /// ``` - /// use twilight_model::application::interaction::InteractionType; - /// - /// assert_eq!(3, InteractionType::MESSAGE_COMPONENT.get()); - /// ``` - pub const fn get(&self) -> u8 { - self.0 - } - /// Name of the associated constant. /// /// Returns `None` if the value doesn't have a defined constant. @@ -67,30 +45,7 @@ impl InteractionType { } } -impl Debug for InteractionType { - fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult { - if let Some(name) = self.name() { - f.debug_struct("InteractionType") - .field("name", &name) - .field("value", &self.0) - .finish() - } else { - f.debug_tuple("InteractionType").field(&self.0).finish() - } - } -} - -impl From for InteractionType { - fn from(value: u8) -> Self { - Self(value) - } -} - -impl From for u8 { - fn from(value: InteractionType) -> Self { - value.get() - } -} +impl_typed!(InteractionType, u8); #[cfg(test)] mod tests { diff --git a/twilight-model/src/channel/channel_type.rs b/twilight-model/src/channel/channel_type.rs index 6052920ef3d..bd1a36b4f1d 100644 --- a/twilight-model/src/channel/channel_type.rs +++ b/twilight-model/src/channel/channel_type.rs @@ -1,5 +1,4 @@ use serde::{Deserialize, Serialize}; -use std::fmt::{Debug, Formatter, Result as FmtResult}; #[derive(Clone, Copy, Deserialize, Eq, Hash, PartialEq, Serialize)] pub struct ChannelType(u8); @@ -33,27 +32,6 @@ impl ChannelType { /// Channel that can only contain threads. pub const GUILD_FORUM: Self = Self::new(15); - /// Create a new channel type from a dynamic value. - /// - /// The provided value isn't validated. Known valid values are associated - /// constants such as [`GUILD_TEXT`][`Self::GUILD_TEXT`]. - pub const fn new(channel_type: u8) -> Self { - Self(channel_type) - } - - /// Retrieve the value of the channel type. - /// - /// # Examples - /// - /// ``` - /// use twilight_model::channel::ChannelType; - /// - /// assert_eq!(15, ChannelType::GUILD_FORUM.get()); - /// ``` - pub const fn get(&self) -> u8 { - self.0 - } - /// Name of the associated constant. /// /// Returns `None` if the value doesn't have a defined constant. @@ -118,30 +96,7 @@ impl ChannelType { } } -impl Debug for ChannelType { - fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult { - if let Some(name) = self.name() { - f.debug_struct("ChannelType") - .field("name", &name) - .field("value", &self.0) - .finish() - } else { - f.debug_tuple("ChannelType").field(&self.0).finish() - } - } -} - -impl From for ChannelType { - fn from(value: u8) -> Self { - Self(value) - } -} - -impl From for u8 { - fn from(value: ChannelType) -> Self { - value.get() - } -} +impl_typed!(ChannelType, u8); #[cfg(test)] mod tests { diff --git a/twilight-model/src/channel/forum.rs b/twilight-model/src/channel/forum.rs index 0c8ffd40e72..6c95475f91d 100644 --- a/twilight-model/src/channel/forum.rs +++ b/twilight-model/src/channel/forum.rs @@ -3,7 +3,6 @@ use crate::id::{ Id, }; use serde::{Deserialize, Serialize}; -use std::fmt::{Debug, Formatter, Result as FmtResult}; /// Emoji to use as the default way to react to a forum post. /// @@ -37,27 +36,6 @@ impl ForumLayout { /// No default has been set for the forum channel. pub const NOT_SET: Self = Self::new(0); - /// Create a new forum layout from a dynamic value. - /// - /// The provided value isn't validated. Known valid values are associated - /// constants such as [`GALLERY_VIEW`][`Self::GALLERY_VIEW`]. - pub const fn new(forum_layout: u8) -> Self { - Self(forum_layout) - } - - /// Retrieve the value of the forum layout. - /// - /// # Examples - /// - /// ``` - /// use twilight_model::channel::forum::ForumLayout; - /// - /// assert_eq!(1, ForumLayout::LIST_VIEW.get()); - /// ``` - pub const fn get(&self) -> u8 { - self.0 - } - /// Name of the associated constant. /// /// Returns `None` if the value doesn't have a defined constant. @@ -71,30 +49,7 @@ impl ForumLayout { } } -impl Debug for ForumLayout { - fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult { - if let Some(name) = self.name() { - f.debug_struct("ForumLayout") - .field("name", &name) - .field("value", &self.0) - .finish() - } else { - f.debug_tuple("ForumLayout").field(&self.0).finish() - } - } -} - -impl From for ForumLayout { - fn from(value: u8) -> Self { - Self(value) - } -} - -impl From for u8 { - fn from(value: ForumLayout) -> Self { - value.get() - } -} +impl_typed!(ForumLayout, u8); /// Layout of a [channel] that is a [forum]. /// @@ -110,27 +65,6 @@ impl ForumSortOrder { /// Sort forum posts by creation time (from most recent to oldest). pub const CREATION_DATE: Self = Self::new(1); - /// Create a new forum sort order from a dynamic value. - /// - /// The provided value isn't validated. Known valid values are associated - /// constants such as [`CREATION_DATE`][`Self::CREATION_DATE`]. - pub const fn new(forum_sort_order: u8) -> Self { - Self(forum_sort_order) - } - - /// Retrieve the value of the forum sort order. - /// - /// # Examples - /// - /// ``` - /// use twilight_model::channel::forum::ForumSortOrder; - /// - /// assert_eq!(0, ForumSortOrder::LATEST_ACTIVITY.get()); - /// ``` - pub const fn get(&self) -> u8 { - self.0 - } - pub const fn name(self) -> Option<&'static str> { Some(match self { Self::CREATION_DATE => "CREATION_DATE", @@ -140,30 +74,7 @@ impl ForumSortOrder { } } -impl Debug for ForumSortOrder { - fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult { - if let Some(name) = self.name() { - f.debug_struct("ForumSortOrder") - .field("name", &name) - .field("value", &self.0) - .finish() - } else { - f.debug_tuple("ForumSortOrder").field(&self.0).finish() - } - } -} - -impl From for ForumSortOrder { - fn from(value: u8) -> Self { - Self(value) - } -} - -impl From for u8 { - fn from(value: ForumSortOrder) -> Self { - value.get() - } -} +impl_typed!(ForumSortOrder, u8); /// Tag that is able to be applied to a thread in a [`GUILD_FORUM`] [`Channel`]. /// diff --git a/twilight-model/src/channel/message/activity.rs b/twilight-model/src/channel/message/activity.rs index 5b733907543..3166fc0d44c 100644 --- a/twilight-model/src/channel/message/activity.rs +++ b/twilight-model/src/channel/message/activity.rs @@ -1,5 +1,4 @@ use serde::{Deserialize, Serialize}; -use std::fmt::{Debug, Formatter, Result as FmtResult}; /// Activity associated with a message. #[derive(Clone, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)] @@ -30,27 +29,6 @@ impl MessageActivityType { /// Request to join the party. pub const JOIN_REQUEST: Self = Self::new(5); - /// Create a new message activity type from a dynamic value. - /// - /// The provided value isn't validated. Known valid values are associated - /// constants such as [`SPECTATE`][`Self::SPECTATE`]. - pub const fn new(message_activity_type: u8) -> Self { - Self(message_activity_type) - } - - /// Retrieve the value of the message activity type. - /// - /// # Examples - /// - /// ``` - /// use twilight_model::channel::message::MessageActivityType; - /// - /// assert_eq!(1, MessageActivityType::JOIN.get()); - /// ``` - pub const fn get(&self) -> u8 { - self.0 - } - /// Name of the associated constant. /// /// Returns `None` if the value doesn't have a defined constant. @@ -65,30 +43,7 @@ impl MessageActivityType { } } -impl Debug for MessageActivityType { - fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult { - if let Some(name) = self.name() { - f.debug_struct("MessageActivityType") - .field("name", &name) - .field("value", &self.0) - .finish() - } else { - f.debug_tuple("MessageActivityType").field(&self.0).finish() - } - } -} - -impl From for MessageActivityType { - fn from(value: u8) -> Self { - Self(value) - } -} - -impl From for u8 { - fn from(value: MessageActivityType) -> Self { - value.get() - } -} +impl_typed!(MessageActivityType, u8); #[cfg(test)] mod tests { diff --git a/twilight-model/src/channel/message/allowed_mentions.rs b/twilight-model/src/channel/message/allowed_mentions.rs index a0104eead45..8d9ee4b0765 100644 --- a/twilight-model/src/channel/message/allowed_mentions.rs +++ b/twilight-model/src/channel/message/allowed_mentions.rs @@ -6,11 +6,6 @@ use crate::{ util::{is_false, known_string::KnownString}, }; use serde::{Deserialize, Serialize}; -use std::{ - fmt::{Debug, Formatter, Result as FmtResult}, - ops::Deref, - str::FromStr, -}; /// Allowed mentions (pings). /// @@ -62,22 +57,6 @@ impl MentionType { /// User mentions. pub const USERS: Self = Self::from_bytes(b"users"); - /// 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 { - 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() - } - /// Name of the associated constant. /// /// Returns `None` if the value doesn't have a defined constant. @@ -89,54 +68,9 @@ impl MentionType { _ => return None, }) } - - /// Create a mention type from a set of bytes. - const fn from_bytes(input: &[u8]) -> Self { - Self(KnownString::from_bytes(input)) - } -} - -impl AsRef for MentionType { - fn as_ref(&self) -> &str { - self.get() - } -} - -impl Debug for MentionType { - fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult { - f.write_str(self.name().unwrap_or_else(|| self.get())) - } -} - -impl Deref for MentionType { - type Target = str; - - fn deref(&self) -> &Self::Target { - self.get() - } } -impl FromStr for MentionType { - type Err = (); - - fn from_str(s: &str) -> Result { - Self::try_from(s) - } -} - -impl ToString for MentionType { - fn to_string(&self) -> String { - KnownString::to_string(&self.0) - } -} - -impl TryFrom<&str> for MentionType { - type Error = (); - - fn try_from(value: &str) -> Result { - Self::new(value).ok_or(()) - } -} +impl_typed!(MentionType, String); #[cfg(test)] mod tests { diff --git a/twilight-model/src/channel/message/component/button.rs b/twilight-model/src/channel/message/component/button.rs index 4547c78a2a6..8d809433c3a 100644 --- a/twilight-model/src/channel/message/component/button.rs +++ b/twilight-model/src/channel/message/component/button.rs @@ -1,6 +1,5 @@ use crate::channel::message::ReactionType; use serde::{Deserialize, Serialize}; -use std::fmt::{Debug, Formatter, Result as FmtResult}; /// Clickable [`Component`] below messages. /// @@ -66,27 +65,6 @@ impl ButtonStyle { /// field. pub const LINK: Self = Self::new(5); - /// Create a new button style from a dynamic value. - /// - /// The provided value isn't validated. Known valid values are associated - /// constants such as [`DANGER`][`Self::DANGER`]. - pub const fn new(button_style: u8) -> Self { - Self(button_style) - } - - /// Retrieve the value of the button style. - /// - /// # Examples - /// - /// ``` - /// use twilight_model::channel::message::component::ButtonStyle; - /// - /// assert_eq!(3, ButtonStyle::SUCCESS.get()); - /// ``` - pub const fn get(&self) -> u8 { - self.0 - } - /// Name of the associated constant. /// /// Returns `None` if the value doesn't have a defined constant. @@ -102,30 +80,7 @@ impl ButtonStyle { } } -impl Debug for ButtonStyle { - fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult { - if let Some(name) = self.name() { - f.debug_struct("ButtonStyle") - .field("name", &name) - .field("value", &self.0) - .finish() - } else { - f.debug_tuple("ButtonStyle").field(&self.0).finish() - } - } -} - -impl From for ButtonStyle { - fn from(value: u8) -> Self { - Self(value) - } -} - -impl From for u8 { - fn from(value: ButtonStyle) -> Self { - value.get() - } -} +impl_typed!(ButtonStyle, u8); #[cfg(test)] mod tests { diff --git a/twilight-model/src/channel/message/component/kind.rs b/twilight-model/src/channel/message/component/kind.rs index 89889eb77ba..b057330b822 100644 --- a/twilight-model/src/channel/message/component/kind.rs +++ b/twilight-model/src/channel/message/component/kind.rs @@ -1,5 +1,4 @@ use serde::{Deserialize, Serialize}; -use std::fmt::{Debug, Formatter, Result as FmtResult}; /// Type of [`Component`]. /// @@ -28,27 +27,6 @@ impl ComponentType { /// [`TextInput`]: super::TextInput pub const TEXT_INPUT: Self = Self::new(4); - /// Create a new command type from a dynamic value. - /// - /// The provided value isn't validated. Known valid values are associated - /// constants such as [`ACTION_ROW`][`Self::ACTION_ROW`]. - pub const fn new(command_type: u8) -> Self { - Self(command_type) - } - - /// Retrieve the value of the command type. - /// - /// # Examples - /// - /// ``` - /// use twilight_model::channel::message::component::ComponentType; - /// - /// assert_eq!(2, ComponentType::BUTTON.get()); - /// ``` - pub const fn get(&self) -> u8 { - self.0 - } - /// Name of the associated constant. /// /// Returns `None` if the value doesn't have a defined constant. @@ -75,30 +53,7 @@ impl ComponentType { } } -impl Debug for ComponentType { - fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult { - if let Some(name) = self.name() { - f.debug_struct("ComponentType") - .field("name", &name) - .field("value", &self.0) - .finish() - } else { - f.debug_tuple("ComponentType").field(&self.0).finish() - } - } -} - -impl From for ComponentType { - fn from(value: u8) -> Self { - Self(value) - } -} - -impl From for u8 { - fn from(value: ComponentType) -> Self { - value.get() - } -} +impl_typed!(ComponentType, u8); #[cfg(test)] mod tests { diff --git a/twilight-model/src/channel/message/component/text_input.rs b/twilight-model/src/channel/message/component/text_input.rs index d9ba0b90931..94aea992207 100644 --- a/twilight-model/src/channel/message/component/text_input.rs +++ b/twilight-model/src/channel/message/component/text_input.rs @@ -1,5 +1,4 @@ use serde::{Deserialize, Serialize}; -use std::fmt::{Debug, Formatter, Result as FmtResult}; /// Pop-up [`Component`] that renders on modals. /// @@ -39,27 +38,6 @@ impl TextInputStyle { /// Intended for much longer inputs. pub const PARAGRAPH: Self = Self::new(2); - /// Create a new text input style from a dynamic value. - /// - /// The provided value isn't validated. Known valid values are associated - /// constants such as [`SHORT`][`Self::SHORT`]. - pub const fn new(text_input_style: u8) -> Self { - Self(text_input_style) - } - - /// Retrieve the value of the text input style. - /// - /// # Examples - /// - /// ``` - /// use twilight_model::channel::message::component::TextInputStyle; - /// - /// assert_eq!(2, TextInputStyle::PARAGRAPH.get()); - /// ``` - pub const fn get(&self) -> u8 { - self.0 - } - /// Name of the associated constant. /// /// Returns `None` if the value doesn't have a defined constant. @@ -72,30 +50,7 @@ impl TextInputStyle { } } -impl Debug for TextInputStyle { - fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult { - if let Some(name) = self.name() { - f.debug_struct("TextInputStyle") - .field("name", &name) - .field("value", &self.0) - .finish() - } else { - f.debug_tuple("TextInputStyle").field(&self.0).finish() - } - } -} - -impl From for TextInputStyle { - fn from(value: u8) -> Self { - Self(value) - } -} - -impl From for u8 { - fn from(value: TextInputStyle) -> Self { - value.get() - } -} +impl_typed!(TextInputStyle, u8); #[cfg(test)] mod tests { diff --git a/twilight-model/src/channel/message/kind.rs b/twilight-model/src/channel/message/kind.rs index 7b2b55e5c9d..d1c64e7ee17 100644 --- a/twilight-model/src/channel/message/kind.rs +++ b/twilight-model/src/channel/message/kind.rs @@ -1,6 +1,5 @@ use crate::guild::Permissions; use serde::{Deserialize, Serialize}; -use std::fmt::{Debug, Formatter, Result as FmtResult}; /// Type of a [`Message`]. /// @@ -94,27 +93,6 @@ impl MessageType { /// System message denoting a guild application premium subscription. pub const GUILD_APPLICATION_PREMIUM_SUBSCRIPTION: Self = Self::new(32); - /// Create a new message type from a dynamic value. - /// - /// The provided value isn't validated. Known valid values are associated - /// constants such as [`REGULAR`][`Self::REGULAR`]. - pub const fn new(message_type: u8) -> Self { - Self(message_type) - } - - /// Retrieve the value of the message type. - /// - /// # Examples - /// - /// ``` - /// use twilight_model::channel::message::MessageType; - /// - /// assert_eq!(19, MessageType::REPLY.get()); - /// ``` - pub const fn get(&self) -> u8 { - self.0 - } - /// Name of the associated constant. /// /// Returns `None` if the value doesn't have a defined constant. @@ -216,30 +194,7 @@ impl MessageType { } } -impl Debug for MessageType { - fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult { - if let Some(name) = self.name() { - f.debug_struct("MessageType") - .field("name", &name) - .field("value", &self.0) - .finish() - } else { - f.debug_tuple("MessageType").field(&self.0).finish() - } - } -} - -impl From for MessageType { - fn from(value: u8) -> Self { - Self(value) - } -} - -impl From for u8 { - fn from(value: MessageType) -> Self { - value.get() - } -} +impl_typed!(MessageType, u8); #[cfg(test)] mod tests { diff --git a/twilight-model/src/channel/message/sticker/format_type.rs b/twilight-model/src/channel/message/sticker/format_type.rs index 767e334a461..0cedfa27552 100644 --- a/twilight-model/src/channel/message/sticker/format_type.rs +++ b/twilight-model/src/channel/message/sticker/format_type.rs @@ -1,5 +1,4 @@ use serde::{Deserialize, Serialize}; -use std::fmt::{Debug, Formatter, Result as FmtResult}; /// Format type of a [`Sticker`]. /// @@ -20,27 +19,6 @@ impl StickerFormatType { /// Sticker format is a GIF. pub const GIF: Self = Self::new(4); - /// Create a new sticker format type from a dynamic value. - /// - /// The provided value isn't validated. Known valid values are associated - /// constants such as [`LOTTIE`][`Self::LOTTIE`]. - pub const fn new(command_type: u8) -> Self { - Self(command_type) - } - - /// Retrieve the value of the sticker format type. - /// - /// # Examples - /// - /// ``` - /// use twilight_model::channel::message::sticker::StickerFormatType; - /// - /// assert_eq!(1, StickerFormatType::PNG.get()); - /// ``` - pub const fn get(&self) -> u8 { - self.0 - } - /// Name of the associated constant. /// /// Returns `None` if the value doesn't have a defined constant. @@ -54,30 +32,7 @@ impl StickerFormatType { } } -impl Debug for StickerFormatType { - fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult { - if let Some(name) = self.name() { - f.debug_struct("StickerFormatType") - .field("name", &name) - .field("value", &self.0) - .finish() - } else { - f.debug_tuple("StickerFormatType").field(&self.0).finish() - } - } -} - -impl From for StickerFormatType { - fn from(value: u8) -> Self { - Self(value) - } -} - -impl From for u8 { - fn from(value: StickerFormatType) -> Self { - value.get() - } -} +impl_typed!(StickerFormatType, u8); #[cfg(test)] mod tests { diff --git a/twilight-model/src/channel/message/sticker/kind.rs b/twilight-model/src/channel/message/sticker/kind.rs index 30be530c688..a90125786b6 100644 --- a/twilight-model/src/channel/message/sticker/kind.rs +++ b/twilight-model/src/channel/message/sticker/kind.rs @@ -1,5 +1,4 @@ use serde::{Deserialize, Serialize}; -use std::fmt::{Debug, Formatter, Result as FmtResult}; /// Type of a [`Sticker`]. /// @@ -16,27 +15,6 @@ impl StickerType { /// Sticker uploaded to a boosted guild for the guild's members. pub const GUILD: Self = Self::new(2); - /// Create a new sticker type from a dynamic value. - /// - /// The provided value isn't validated. Known valid values are associated - /// constants such as [`STANDARD`][`Self::STANDARD`]. - pub const fn new(sticker_type: u8) -> Self { - Self(sticker_type) - } - - /// Retrieve the value of the sticker type. - /// - /// # Examples - /// - /// ``` - /// use twilight_model::channel::message::sticker::StickerType; - /// - /// assert_eq!(2, StickerType::GUILD.get()); - /// ``` - pub const fn get(&self) -> u8 { - self.0 - } - /// Name of the associated constant. /// /// Returns `None` if the value doesn't have a defined constant. @@ -49,30 +27,7 @@ impl StickerType { } } -impl Debug for StickerType { - fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult { - if let Some(name) = self.name() { - f.debug_struct("StickerType") - .field("name", &name) - .field("value", &self.0) - .finish() - } else { - f.debug_tuple("StickerType").field(&self.0).finish() - } - } -} - -impl From for StickerType { - fn from(value: u8) -> Self { - Self(value) - } -} - -impl From for u8 { - fn from(value: StickerType) -> Self { - value.get() - } -} +impl_typed!(StickerType, u8); #[cfg(test)] mod tests { diff --git a/twilight-model/src/channel/permission_overwrite.rs b/twilight-model/src/channel/permission_overwrite.rs index de6deb728e2..8b848242345 100644 --- a/twilight-model/src/channel/permission_overwrite.rs +++ b/twilight-model/src/channel/permission_overwrite.rs @@ -3,7 +3,6 @@ use crate::{ id::{marker::GenericMarker, Id}, }; use serde::{Deserialize, Serialize}; -use std::fmt::{Debug, Formatter, Result as FmtResult}; /// Permission overwrite data for a role or member. #[derive(Clone, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)] @@ -27,27 +26,6 @@ impl PermissionOverwriteType { /// Permission overwrite targets an individual role. pub const ROLE: Self = Self::new(0); - /// Create a new permission overwrite type from a dynamic value. - /// - /// The provided value isn't validated. Known valid values are associated - /// constants such as [`MEMBER`][`Self::MEMBER`]. - pub const fn new(permission_overwrite_type: u8) -> Self { - Self(permission_overwrite_type) - } - - /// Retrieve the value of the permission overwrite type. - /// - /// # Examples - /// - /// ``` - /// use twilight_model::channel::permission_overwrite::PermissionOverwriteType; - /// - /// assert_eq!(0, PermissionOverwriteType::ROLE.get()); - /// ``` - pub const fn get(&self) -> u8 { - self.0 - } - /// Name of the associated constant. /// /// Returns `None` if the value doesn't have a defined constant. @@ -60,32 +38,7 @@ impl PermissionOverwriteType { } } -impl Debug for PermissionOverwriteType { - fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult { - if let Some(name) = self.name() { - f.debug_struct("PermissionOverwriteType") - .field("name", &name) - .field("value", &self.0) - .finish() - } else { - f.debug_tuple("PermissionOverwriteType") - .field(&self.0) - .finish() - } - } -} - -impl From for PermissionOverwriteType { - fn from(value: u8) -> Self { - Self(value) - } -} - -impl From for u8 { - fn from(value: PermissionOverwriteType) -> Self { - value.get() - } -} +impl_typed!(PermissionOverwriteType, u8); #[cfg(test)] mod tests { diff --git a/twilight-model/src/channel/stage_instance/privacy_level.rs b/twilight-model/src/channel/stage_instance/privacy_level.rs index 3fc49b96a1a..080b3e6e9cb 100644 --- a/twilight-model/src/channel/stage_instance/privacy_level.rs +++ b/twilight-model/src/channel/stage_instance/privacy_level.rs @@ -1,5 +1,4 @@ use serde::{Deserialize, Serialize}; -use std::fmt::{Debug, Formatter, Result as FmtResult}; #[derive(Clone, Copy, Deserialize, Eq, Hash, PartialEq, Serialize)] pub struct PrivacyLevel(u8); @@ -7,27 +6,6 @@ pub struct PrivacyLevel(u8); impl PrivacyLevel { pub const GUILD_ONLY: Self = Self::new(2); - /// Create a new privacy level from a dynamic value. - /// - /// The provided value isn't validated. Known valid values are associated - /// constants such as [`GUILD_ONLY`][`Self::GUILD_ONLY`]. - pub const fn new(privacy_level: u8) -> Self { - Self(privacy_level) - } - - /// Retrieve the value of the privacy level. - /// - /// # Examples - /// - /// ``` - /// use twilight_model::channel::stage_instance::PrivacyLevel; - /// - /// assert_eq!(2, PrivacyLevel::GUILD_ONLY.get()); - /// ``` - pub const fn get(&self) -> u8 { - self.0 - } - /// Name of the associated constant. /// /// Returns `None` if the value doesn't have a defined constant. @@ -39,30 +17,7 @@ impl PrivacyLevel { } } -impl Debug for PrivacyLevel { - fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult { - if let Some(name) = self.name() { - f.debug_struct("PrivacyLevel") - .field("name", &name) - .field("value", &self.0) - .finish() - } else { - f.debug_tuple("PrivacyLevel").field(&self.0).finish() - } - } -} - -impl From for PrivacyLevel { - fn from(value: u8) -> Self { - Self(value) - } -} - -impl From for u8 { - fn from(value: PrivacyLevel) -> Self { - value.get() - } -} +impl_typed!(PrivacyLevel, u8); #[cfg(test)] mod tests { diff --git a/twilight-model/src/channel/thread/auto_archive_duration.rs b/twilight-model/src/channel/thread/auto_archive_duration.rs index 78abb6cf025..4c86799411d 100644 --- a/twilight-model/src/channel/thread/auto_archive_duration.rs +++ b/twilight-model/src/channel/thread/auto_archive_duration.rs @@ -1,8 +1,5 @@ use serde::{Deserialize, Serialize}; -use std::{ - fmt::{Debug, Formatter, Result as FmtResult}, - time::Duration, -}; +use std::time::Duration; #[derive(Clone, Copy, Deserialize, Eq, Hash, PartialEq, Serialize)] pub struct AutoArchiveDuration(u16); @@ -16,27 +13,6 @@ impl AutoArchiveDuration { pub const WEEK: Self = Self::new(Self::DAY.get() * 7); - /// Create a new auto archive duration from a dynamic value. - /// - /// The provided value isn't validated. Known valid values are associated - /// constants, such as [`HOUR`][`Self::HOUR`] or [`WEEK`][`Self::WEEK`]. - pub const fn new(auto_archive_duration: u16) -> Self { - Self(auto_archive_duration) - } - - /// Retrieve the value of the auto archive duration. - /// - /// # Examples - /// - /// ``` - /// use twilight_model::channel::thread::AutoArchiveDuration; - /// - /// assert_eq!(60, AutoArchiveDuration::HOUR.get()); - /// ``` - pub const fn get(&self) -> u16 { - self.0 - } - /// Name of the associated constant. /// /// Returns `None` if the value doesn't have a defined constant. @@ -51,37 +27,14 @@ impl AutoArchiveDuration { } } -impl Debug for AutoArchiveDuration { - fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult { - if let Some(name) = self.name() { - f.debug_struct("AutoArchiveDuration") - .field("name", &name) - .field("value", &self.0) - .finish() - } else { - f.debug_tuple("AutoArchiveDuration").field(&self.0).finish() - } - } -} - -impl From for AutoArchiveDuration { - fn from(value: u16) -> Self { - Self::new(value) - } -} - -impl From for u16 { - fn from(value: AutoArchiveDuration) -> Self { - value.get() - } -} - impl From for Duration { fn from(value: AutoArchiveDuration) -> Self { Self::from_secs(u64::from(value.get()) * 60) } } +impl_typed!(AutoArchiveDuration, u16); + #[cfg(test)] mod tests { use super::AutoArchiveDuration; diff --git a/twilight-model/src/channel/video_quality_mode.rs b/twilight-model/src/channel/video_quality_mode.rs index daababf7c30..0149a7c28f7 100644 --- a/twilight-model/src/channel/video_quality_mode.rs +++ b/twilight-model/src/channel/video_quality_mode.rs @@ -1,5 +1,4 @@ use serde::{Deserialize, Serialize}; -use std::fmt::{Debug, Formatter, Result as FmtResult}; #[derive(Clone, Copy, Deserialize, Eq, Hash, PartialEq, Serialize)] pub struct VideoQualityMode(u8); @@ -11,27 +10,6 @@ impl VideoQualityMode { /// 720p. pub const FULL: Self = Self::new(2); - /// Create a new video quality mode from a dynamic value. - /// - /// The provided value isn't validated. Known valid values are associated - /// constants such as [`AUTO`][`Self::AUTO`]. - pub const fn new(video_quality_mode: u8) -> Self { - Self(video_quality_mode) - } - - /// Retrieve the value of the video quality mode. - /// - /// # Examples - /// - /// ``` - /// use twilight_model::channel::VideoQualityMode; - /// - /// assert_eq!(2, VideoQualityMode::FULL.get()); - /// ``` - pub const fn get(&self) -> u8 { - self.0 - } - /// Name of the associated constant. /// /// Returns `None` if the value doesn't have a defined constant. @@ -44,30 +22,7 @@ impl VideoQualityMode { } } -impl Debug for VideoQualityMode { - fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult { - if let Some(name) = self.name() { - f.debug_struct("VideoQualityMode") - .field("name", &name) - .field("value", &self.0) - .finish() - } else { - f.debug_tuple("VideoQualityMode").field(&self.0).finish() - } - } -} - -impl From for VideoQualityMode { - fn from(value: u8) -> Self { - Self(value) - } -} - -impl From for u8 { - fn from(value: VideoQualityMode) -> Self { - value.get() - } -} +impl_typed!(VideoQualityMode, u8); #[cfg(test)] mod tests { diff --git a/twilight-model/src/channel/webhook/kind.rs b/twilight-model/src/channel/webhook/kind.rs index e2f7de3ff5e..61a84a6dc83 100644 --- a/twilight-model/src/channel/webhook/kind.rs +++ b/twilight-model/src/channel/webhook/kind.rs @@ -1,5 +1,4 @@ use serde::{Deserialize, Serialize}; -use std::fmt::{Debug, Formatter, Result as FmtResult}; #[derive(Clone, Copy, Deserialize, Eq, Hash, PartialEq, Serialize)] pub struct WebhookType(u8); @@ -12,27 +11,6 @@ impl WebhookType { /// Webhooks used with interactions. pub const APPLICATION: Self = Self::new(3); - /// Create a new webhook type from a dynamic value. - /// - /// The provided value isn't validated. Known valid values are associated - /// constants such as [`INCOMING`][`Self::INCOMING`]. - pub const fn new(webhook_type: u8) -> Self { - Self(webhook_type) - } - - /// Retrieve the value of the webhook type. - /// - /// # Examples - /// - /// ``` - /// use twilight_model::channel::WebhookType; - /// - /// assert_eq!(2, WebhookType::CHANNEL_FOLLOWER.get()); - /// ``` - pub const fn get(&self) -> u8 { - self.0 - } - /// Name of the associated constant. /// /// Returns `None` if the value doesn't have a defined constant. @@ -46,36 +24,13 @@ impl WebhookType { } } -impl Debug for WebhookType { - fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult { - if let Some(name) = self.name() { - f.debug_struct("WebhookType") - .field("name", &name) - .field("value", &self.0) - .finish() - } else { - f.debug_tuple("WebhookType").field(&self.0).finish() - } - } -} - impl Default for WebhookType { fn default() -> Self { Self::INCOMING } } -impl From for WebhookType { - fn from(value: u8) -> Self { - Self(value) - } -} - -impl From for u8 { - fn from(value: WebhookType) -> Self { - value.get() - } -} +impl_typed!(WebhookType, u8); #[cfg(test)] mod tests { diff --git a/twilight-model/src/gateway/close_code.rs b/twilight-model/src/gateway/close_code.rs index 69127469bd9..9c7ff7a8a54 100644 --- a/twilight-model/src/gateway/close_code.rs +++ b/twilight-model/src/gateway/close_code.rs @@ -1,5 +1,4 @@ use serde::{Deserialize, Serialize}; -use std::fmt::{Debug, Formatter, Result as FmtResult}; /// Gateway close event codes. /// @@ -52,27 +51,6 @@ impl CloseCode { /// A disallowed intent was sent, may need allowlisting. pub const DISALLOWED_INTENTS: Self = Self::new(4014); - /// Create a new command type from a dynamic value. - /// - /// The provided value isn't validated. Known valid values are associated - /// constants such as [`UNKNOWN_OPCODE`][`Self::UNKNOWN_OPCODE`]. - pub const fn new(command_type: u16) -> Self { - Self(command_type) - } - - /// Retrieve the value of the command type. - /// - /// # Examples - /// - /// ``` - /// use twilight_model::gateway::CloseCode; - /// - /// assert_eq!(4014, CloseCode::DISALLOWED_INTENTS.get()); - /// ``` - pub const fn get(&self) -> u16 { - self.0 - } - /// Name of the associated constant. /// /// Returns `None` if the value doesn't have a defined constant. @@ -113,30 +91,7 @@ impl CloseCode { } } -impl Debug for CloseCode { - fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult { - if let Some(name) = self.name() { - f.debug_struct("CloseCode") - .field("name", &name) - .field("value", &self.0) - .finish() - } else { - f.debug_tuple("CloseCode").field(&self.0).finish() - } - } -} - -impl From for CloseCode { - fn from(value: u16) -> Self { - Self(value) - } -} - -impl From for u16 { - fn from(value: CloseCode) -> Self { - value.get() - } -} +impl_typed!(CloseCode, u16); #[cfg(test)] mod tests { diff --git a/twilight-model/src/gateway/event/kind.rs b/twilight-model/src/gateway/event/kind.rs index 6a32484c536..688c1d33581 100644 --- a/twilight-model/src/gateway/event/kind.rs +++ b/twilight-model/src/gateway/event/kind.rs @@ -1,10 +1,5 @@ use crate::util::known_string::KnownString; use serde::{Deserialize, Serialize}; -use std::{ - fmt::{Debug, Formatter, Result as FmtResult}, - ops::Deref, - str::FromStr, -}; /// The type of an event. #[derive(Clone, Copy, Deserialize, Eq, Hash, PartialEq, Serialize)] @@ -159,22 +154,6 @@ impl EventType { pub const WEBHOOKS_UPDATE: Self = Self::from_bytes(b"WEBHOOKS_UPDATE"); - /// Create a event type from a dynamic value. - /// - /// The provided event type must be 64 bytes or smaller. - pub fn new(event_type: &str) -> Option { - KnownString::from_str(event_type).map(Self) - } - - /// Get the value of the event type. - /// - /// # Panics - /// - /// Panics if the event type isn't valid UTF-8. - pub fn get(&self) -> &str { - self.0.get() - } - /// Name of the associated constant. /// /// Returns `None` if the value doesn't have a defined constant. @@ -247,54 +226,9 @@ impl EventType { _ => None, } } - - /// Create a event type from a set of bytes. - const fn from_bytes(input: &[u8]) -> Self { - Self(KnownString::from_bytes(input)) - } -} - -impl AsRef for EventType { - fn as_ref(&self) -> &str { - self.get() - } -} - -impl Debug for EventType { - fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult { - f.write_str(self.name().unwrap_or_else(|| self.get())) - } } -impl Deref for EventType { - type Target = str; - - fn deref(&self) -> &Self::Target { - self.get() - } -} - -impl FromStr for EventType { - type Err = (); - - fn from_str(s: &str) -> Result { - Self::try_from(s) - } -} - -impl ToString for EventType { - fn to_string(&self) -> String { - KnownString::to_string(&self.0) - } -} - -impl TryFrom<&str> for EventType { - type Error = (); - - fn try_from(value: &str) -> Result { - Self::new(value).ok_or(()) - } -} +impl_typed!(EventType, String); #[cfg(test)] mod tests { diff --git a/twilight-model/src/gateway/opcode.rs b/twilight-model/src/gateway/opcode.rs index 37643086c99..1541be30fc0 100644 --- a/twilight-model/src/gateway/opcode.rs +++ b/twilight-model/src/gateway/opcode.rs @@ -1,5 +1,4 @@ use serde::{Deserialize, Serialize}; -use std::fmt::{Debug, Formatter, Result as FmtResult}; /// Gateway event opcodes. /// @@ -57,27 +56,6 @@ impl OpCode { /// [`HEARTBEAT`]: Self::HEARTBEAT pub const HEARTBEAT_ACK: Self = Self::new(11); - /// Create a new opcode from a dynamic value. - /// - /// The provided value isn't validated. Known valid values are associated - /// constants such as [`DISPATCH`][`Self::DISPATCH`]. - pub const fn new(opcode: u8) -> Self { - Self(opcode) - } - - /// Retrieve the value of the opcode. - /// - /// # Examples - /// - /// ``` - /// use twilight_model::gateway::OpCode; - /// - /// assert_eq!(2, OpCode::IDENTIFY.get()); - /// ``` - pub const fn get(&self) -> u8 { - self.0 - } - /// Name of the associated constant. /// /// Returns `None` if the value doesn't have a defined constant. @@ -99,30 +77,7 @@ impl OpCode { } } -impl Debug for OpCode { - fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult { - if let Some(name) = self.name() { - f.debug_struct("OpCode") - .field("name", &name) - .field("value", &self.0) - .finish() - } else { - f.debug_tuple("OpCode").field(&self.0).finish() - } - } -} - -impl From for OpCode { - fn from(value: u8) -> Self { - Self(value) - } -} - -impl From for u8 { - fn from(value: OpCode) -> Self { - value.get() - } -} +impl_typed!(OpCode, u8); #[cfg(test)] mod tests { diff --git a/twilight-model/src/gateway/presence/activity_type.rs b/twilight-model/src/gateway/presence/activity_type.rs index b0e98d5a527..53ffe49b153 100644 --- a/twilight-model/src/gateway/presence/activity_type.rs +++ b/twilight-model/src/gateway/presence/activity_type.rs @@ -1,5 +1,4 @@ use serde::{Deserialize, Serialize}; -use std::fmt::{Debug, Formatter, Result as FmtResult}; #[derive(Clone, Copy, Deserialize, Eq, Hash, PartialEq, Serialize)] pub struct ActivityType(u8); @@ -12,27 +11,6 @@ impl ActivityType { pub const CUSTOM: Self = Self::new(4); pub const COMPETING: Self = Self::new(5); - /// Create a new activity type from a dynamic value. - /// - /// The provided value isn't validated. Known valid values are associated - /// constants such as [`WATCHING`][`Self::WATCHING`]. - pub const fn new(activity_type: u8) -> Self { - Self(activity_type) - } - - /// Retrieve the value of the activity type. - /// - /// # Examples - /// - /// ``` - /// use twilight_model::gateway::presence::ActivityType; - /// - /// assert_eq!(2, ActivityType::LISTENING.get()); - /// ``` - pub const fn get(&self) -> u8 { - self.0 - } - /// Name of the associated constant. /// /// Returns `None` if the value doesn't have a defined constant. @@ -49,36 +27,13 @@ impl ActivityType { } } -impl Debug for ActivityType { - fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult { - if let Some(name) = self.name() { - f.debug_struct("ActivityType") - .field("name", &name) - .field("value", &self.0) - .finish() - } else { - f.debug_tuple("ActivityType").field(&self.0).finish() - } - } -} - impl Default for ActivityType { fn default() -> Self { Self::PLAYING } } -impl From for ActivityType { - fn from(value: u8) -> Self { - Self(value) - } -} - -impl From for u8 { - fn from(value: ActivityType) -> Self { - value.get() - } -} +impl_typed!(ActivityType, u8); #[cfg(test)] mod tests { diff --git a/twilight-model/src/gateway/presence/status.rs b/twilight-model/src/gateway/presence/status.rs index 20c07aaf0d9..865d8600902 100644 --- a/twilight-model/src/gateway/presence/status.rs +++ b/twilight-model/src/gateway/presence/status.rs @@ -1,10 +1,5 @@ use crate::util::known_string::KnownString; use serde::{Deserialize, Serialize}; -use std::{ - fmt::{Debug, Formatter, Result as FmtResult}, - ops::Deref, - str::FromStr, -}; #[derive(Clone, Copy, Deserialize, Eq, Hash, PartialEq, Serialize)] pub struct Status(KnownString<16>); @@ -20,22 +15,6 @@ impl Status { pub const ONLINE: Self = Self::from_bytes(b"online"); - /// Create a status from a dynamic value. - /// - /// The provided status must be 64 bytes or smaller. - pub fn new(status: &str) -> Option { - KnownString::from_str(status).map(Self) - } - - /// Get the value of the status. - /// - /// # Panics - /// - /// Panics if the status isn't valid UTF-8. - pub fn get(&self) -> &str { - self.0.get() - } - /// Name of the associated constant. /// /// Returns `None` if the value doesn't have a defined constant. @@ -49,54 +28,9 @@ impl Status { _ => return None, }) } - - /// Create a status from a set of bytes. - const fn from_bytes(input: &[u8]) -> Self { - Self(KnownString::from_bytes(input)) - } -} - -impl AsRef for Status { - fn as_ref(&self) -> &str { - self.get() - } -} - -impl Debug for Status { - fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult { - f.write_str(self.name().unwrap_or_else(|| self.get())) - } } -impl Deref for Status { - type Target = str; - - fn deref(&self) -> &Self::Target { - self.get() - } -} - -impl FromStr for Status { - type Err = (); - - fn from_str(s: &str) -> Result { - Self::try_from(s) - } -} - -impl ToString for Status { - fn to_string(&self) -> String { - KnownString::to_string(&self.0) - } -} - -impl TryFrom<&str> for Status { - type Error = (); - - fn try_from(value: &str) -> Result { - Self::new(value).ok_or(()) - } -} +impl_typed!(Status, String); #[cfg(test)] mod tests { diff --git a/twilight-model/src/guild/afk_timeout.rs b/twilight-model/src/guild/afk_timeout.rs index 08ab5708b2a..08073ba6443 100644 --- a/twilight-model/src/guild/afk_timeout.rs +++ b/twilight-model/src/guild/afk_timeout.rs @@ -1,8 +1,5 @@ use serde::{Deserialize, Serialize}; -use std::{ - fmt::{Debug, Formatter, Result as FmtResult}, - time::Duration, -}; +use std::time::Duration; /// Duration of a user being AFK before being timed out from a voice channel. /// @@ -37,19 +34,6 @@ impl AfkTimeout { /// AFK timeout of one hour. pub const ONE_HOUR: Self = Self(3600); - /// Retrieve the duration of the AFK timeout in seconds. - /// - /// # Examples - /// - /// ``` - /// use twilight_model::guild::AfkTimeout; - /// - /// assert_eq!(60, AfkTimeout::ONE_MINUTE.get()); - /// ``` - pub const fn get(self) -> u16 { - self.0 - } - /// Name of the associated constant. /// /// Returns `None` if the value doesn't have a defined constant. @@ -65,25 +49,6 @@ impl AfkTimeout { } } -impl Debug for AfkTimeout { - fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult { - if let Some(name) = self.name() { - f.debug_struct("AfkTimeout") - .field("name", &name) - .field("value", &self.0) - .finish() - } else { - f.debug_tuple("AfkTimeout").field(&self.0).finish() - } - } -} - -impl From for AfkTimeout { - fn from(value: u16) -> Self { - Self(value) - } -} - impl From for Duration { fn from(value: AfkTimeout) -> Self { Self::from_secs(u64::from(value.get())) @@ -102,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_key.rs b/twilight-model/src/guild/audit_log/change_key.rs index b97f47d1dc9..db082fddaaa 100644 --- a/twilight-model/src/guild/audit_log/change_key.rs +++ b/twilight-model/src/guild/audit_log/change_key.rs @@ -1,10 +1,5 @@ use crate::util::known_string::KnownString; use serde::{Deserialize, Serialize}; -use std::{ - fmt::{Debug, Formatter, Result as FmtResult}, - ops::Deref, - str::FromStr, -}; /// Type of [`AuditLogChange`]. /// @@ -240,22 +235,6 @@ impl AuditLogChangeKey { /// Whether a widget is enabled. pub const WIDGET_ENABLED: Self = Self::from_bytes(b"widget_enabled"); - /// Create a scope from a dynamic value. - /// - /// The provided scope must be 64 bytes or smaller. - pub fn new(scope: &str) -> Option { - KnownString::from_str(scope).map(Self) - } - - /// Get the value of the scope. - /// - /// # Panics - /// - /// Panics if the scope isn't valid UTF-8. - pub fn get(&self) -> &str { - self.0.get() - } - /// Name of the associated constant. /// /// Returns `None` if the value doesn't have a defined constant. @@ -337,54 +316,9 @@ impl AuditLogChangeKey { _ => return None, }) } - - /// Create a scope from a set of bytes. - const fn from_bytes(input: &[u8]) -> Self { - Self(KnownString::from_bytes(input)) - } -} - -impl AsRef for AuditLogChangeKey { - fn as_ref(&self) -> &str { - self.get() - } -} - -impl Debug for AuditLogChangeKey { - fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult { - f.write_str(self.name().unwrap_or_else(|| self.get())) - } } -impl Deref for AuditLogChangeKey { - type Target = str; - - fn deref(&self) -> &Self::Target { - self.get() - } -} - -impl FromStr for AuditLogChangeKey { - type Err = (); - - fn from_str(s: &str) -> Result { - Self::try_from(s) - } -} - -impl ToString for AuditLogChangeKey { - fn to_string(&self) -> String { - KnownString::to_string(&self.0) - } -} - -impl TryFrom<&str> for AuditLogChangeKey { - type Error = (); - - fn try_from(value: &str) -> Result { - Self::new(value).ok_or(()) - } -} +impl_typed!(AuditLogChangeKey, String); #[cfg(test)] mod tests { diff --git a/twilight-model/src/guild/audit_log/event_type.rs b/twilight-model/src/guild/audit_log/event_type.rs index 2989f46f5e0..88f1b1f13b5 100644 --- a/twilight-model/src/guild/audit_log/event_type.rs +++ b/twilight-model/src/guild/audit_log/event_type.rs @@ -1,5 +1,4 @@ use serde::{Deserialize, Serialize}; -use std::fmt::{Debug, Formatter, Result as FmtResult}; /// Action to cause an [`AuditLogEntry`]. /// @@ -282,27 +281,6 @@ impl AuditLogEventType { /// A member has been timed out by Automod. pub const AUTO_MODERATION_USER_COMMUNICATION_DISABLED: Self = Self::new(145); - /// Create a new audit log event type from a dynamic value. - /// - /// The provided value isn't validated. Known valid values are associated - /// constants such as [`GUILD_UPDATE`][`Self::GUILD_UPDATE`]. - pub const fn new(audit_log_event_type: u16) -> Self { - Self(audit_log_event_type) - } - - /// Retrieve the value of the audit log event type. - /// - /// # Examples - /// - /// ``` - /// use twilight_model::guild::audit_log::AuditLogEventType; - /// - /// assert_eq!(40, AuditLogEventType::INVITE_CREATE.get()); - /// ``` - pub const fn get(&self) -> u16 { - self.0 - } - /// Name of the associated constant. /// /// Returns `None` if the value doesn't have a defined constant. @@ -369,30 +347,7 @@ impl AuditLogEventType { } } -impl Debug for AuditLogEventType { - fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult { - if let Some(name) = self.name() { - f.debug_struct("AuditLogEventType") - .field("name", &name) - .field("value", &self.0) - .finish() - } else { - f.debug_tuple("AuditLogEventType").field(&self.0).finish() - } - } -} - -impl From for AuditLogEventType { - fn from(value: u16) -> Self { - Self(value) - } -} - -impl From for u16 { - fn from(value: AuditLogEventType) -> Self { - value.get() - } -} +impl_typed!(AuditLogEventType, u16); #[cfg(test)] mod tests { diff --git a/twilight-model/src/guild/auto_moderation/action.rs b/twilight-model/src/guild/auto_moderation/action.rs index d57c789efe7..866268cb2c8 100644 --- a/twilight-model/src/guild/auto_moderation/action.rs +++ b/twilight-model/src/guild/auto_moderation/action.rs @@ -1,6 +1,5 @@ use crate::id::{marker::ChannelMarker, Id}; use serde::{Deserialize, Serialize}; -use std::fmt::{Debug, Formatter, Result as FmtResult}; /// An action which will execute whenever a rule is triggered. #[derive(Clone, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)] @@ -49,27 +48,6 @@ impl AutoModerationActionType { /// [`Permissions::MODERATE_MEMBERS`]: crate::guild::Permissions::MODERATE_MEMBERS pub const TIMEOUT: Self = Self::new(3); - /// Create a new auto moderation action type from a dynamic value. - /// - /// The provided value isn't validated. Known valid values are associated - /// constants such as [`BLOCK_MESSAGE`][`Self::BLOCK_MESSAGE`]. - pub const fn new(auto_moderation_action_type: u8) -> Self { - Self(auto_moderation_action_type) - } - - /// Retrieve the value of the auto moderation action type. - /// - /// # Examples - /// - /// ``` - /// use twilight_model::guild::auto_moderation::AutoModerationActionType; - /// - /// assert_eq!(3, AutoModerationActionType::TIMEOUT.get()); - /// ``` - pub const fn get(&self) -> u8 { - self.0 - } - /// Name of the associated constant. /// /// Returns `None` if the value doesn't have a defined constant. @@ -83,32 +61,7 @@ impl AutoModerationActionType { } } -impl Debug for AutoModerationActionType { - fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult { - if let Some(name) = self.name() { - f.debug_struct("AutoModerationActionType") - .field("name", &name) - .field("value", &self.0) - .finish() - } else { - f.debug_tuple("AutoModerationActionType") - .field(&self.0) - .finish() - } - } -} - -impl From for AutoModerationActionType { - fn from(value: u8) -> Self { - Self(value) - } -} - -impl From for u8 { - fn from(value: AutoModerationActionType) -> Self { - value.get() - } -} +impl_typed!(AutoModerationActionType, u8); #[cfg(test)] mod tests { diff --git a/twilight-model/src/guild/auto_moderation/event_type.rs b/twilight-model/src/guild/auto_moderation/event_type.rs index 87a3baf2c81..763893ea303 100644 --- a/twilight-model/src/guild/auto_moderation/event_type.rs +++ b/twilight-model/src/guild/auto_moderation/event_type.rs @@ -1,5 +1,4 @@ use serde::{Deserialize, Serialize}; -use std::fmt::{Debug, Formatter, Result as FmtResult}; /// Indicates in what event context a rule should be checked. #[derive(Clone, Copy, Deserialize, Eq, Hash, PartialEq, Serialize)] @@ -9,27 +8,6 @@ impl AutoModerationEventType { /// When a member sends or edits a message in a guild. pub const MESSAGE_SEND: Self = Self::new(1); - /// Create a new auto moderation event type from a dynamic value. - /// - /// The provided value isn't validated. Known valid values are associated - /// constants such as [`MESSAGE_SEND`][`Self::MESSAGE_SEND`]. - pub const fn new(auto_moderation_event_type: u8) -> Self { - Self(auto_moderation_event_type) - } - - /// Retrieve the value of the auto moderation event type. - /// - /// # Examples - /// - /// ``` - /// use twilight_model::guild::auto_moderation::AutoModerationEventType; - /// - /// assert_eq!(1, AutoModerationEventType::MESSAGE_SEND.get()); - /// ``` - pub const fn get(&self) -> u8 { - self.0 - } - /// Name of the associated constant. /// /// Returns `None` if the value doesn't have a defined constant. @@ -41,32 +19,7 @@ impl AutoModerationEventType { } } -impl Debug for AutoModerationEventType { - fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult { - if let Some(name) = self.name() { - f.debug_struct("AutoModerationEventType") - .field("name", &name) - .field("value", &self.0) - .finish() - } else { - f.debug_tuple("AutoModerationEventType") - .field(&self.0) - .finish() - } - } -} - -impl From for AutoModerationEventType { - fn from(value: u8) -> Self { - Self(value) - } -} - -impl From for u8 { - fn from(value: AutoModerationEventType) -> Self { - value.get() - } -} +impl_typed!(AutoModerationEventType, u8); #[cfg(test)] mod tests { diff --git a/twilight-model/src/guild/auto_moderation/preset_type.rs b/twilight-model/src/guild/auto_moderation/preset_type.rs index 519928bfd72..7fba53c73cb 100644 --- a/twilight-model/src/guild/auto_moderation/preset_type.rs +++ b/twilight-model/src/guild/auto_moderation/preset_type.rs @@ -1,5 +1,4 @@ use serde::{Deserialize, Serialize}; -use std::fmt::{Debug, Formatter, Result as FmtResult}; /// Internally pre-defined wordsets which will be searched for in content. #[derive(Clone, Copy, Deserialize, Eq, Hash, PartialEq, Serialize)] @@ -15,27 +14,6 @@ impl AutoModerationKeywordPresetType { /// Personal insults or words that may be considered hate speech. pub const SLURS: Self = Self::new(3); - /// Create a new auto moderation keyword preset type from a dynamic value. - /// - /// The provided value isn't validated. Known valid values are associated - /// constants such as [`PROFANITY`][`Self::PROFANITY`]. - pub const fn new(auto_moderation_keyword_preset_type: u8) -> Self { - Self(auto_moderation_keyword_preset_type) - } - - /// Retrieve the value of the auto moderation keyword preset type. - /// - /// # Examples - /// - /// ``` - /// use twilight_model::guild::auto_moderation::AutoModerationKeywordPresetType; - /// - /// assert_eq!(2, AutoModerationKeywordPresetType::SEXUAL_CONTENT.get()); - /// ``` - pub const fn get(&self) -> u8 { - self.0 - } - /// Name of the associated constant. /// /// Returns `None` if the value doesn't have a defined constant. @@ -49,32 +27,7 @@ impl AutoModerationKeywordPresetType { } } -impl Debug for AutoModerationKeywordPresetType { - fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult { - if let Some(name) = self.name() { - f.debug_struct("AutoModerationKeywordPresetType") - .field("name", &name) - .field("value", &self.0) - .finish() - } else { - f.debug_tuple("AutoModerationKeywordPresetType") - .field(&self.0) - .finish() - } - } -} - -impl From for AutoModerationKeywordPresetType { - fn from(value: u8) -> Self { - Self(value) - } -} - -impl From for u8 { - fn from(value: AutoModerationKeywordPresetType) -> Self { - value.get() - } -} +impl_typed!(AutoModerationKeywordPresetType, u8); #[cfg(test)] mod tests { diff --git a/twilight-model/src/guild/auto_moderation/trigger_type.rs b/twilight-model/src/guild/auto_moderation/trigger_type.rs index 10a23227d5a..4aa552eadab 100644 --- a/twilight-model/src/guild/auto_moderation/trigger_type.rs +++ b/twilight-model/src/guild/auto_moderation/trigger_type.rs @@ -1,5 +1,4 @@ use serde::{Deserialize, Serialize}; -use std::fmt::{Debug, Formatter, Result as FmtResult}; /// Characterizes the type of content which can trigger the rule. #[derive(Clone, Copy, Deserialize, Eq, Hash, PartialEq, Serialize)] @@ -24,27 +23,6 @@ impl AutoModerationTriggerType { /// Check if content contains more unique mentions than allowed. pub const MENTION_SPAM: Self = Self::new(5); - /// Create a new auto moderation trigger type from a dynamic value. - /// - /// The provided value isn't validated. Known valid values are associated - /// constants such as [`SPAM`][`Self::SPAM`]. - pub const fn new(auto_moderation_trigger_type: u8) -> Self { - Self(auto_moderation_trigger_type) - } - - /// Retrieve the value of the auto moderation trigger type. - /// - /// # Examples - /// - /// ``` - /// use twilight_model::guild::auto_moderation::AutoModerationTriggerType; - /// - /// assert_eq!(5, AutoModerationTriggerType::MENTION_SPAM.get()); - /// ``` - pub const fn get(&self) -> u8 { - self.0 - } - /// Name of the associated constant. /// /// Returns `None` if the value doesn't have a defined constant. @@ -59,32 +37,7 @@ impl AutoModerationTriggerType { } } -impl Debug for AutoModerationTriggerType { - fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult { - if let Some(name) = self.name() { - f.debug_struct("AutoModerationTriggerType") - .field("name", &name) - .field("value", &self.0) - .finish() - } else { - f.debug_tuple("AutoModerationTriggerType") - .field(&self.0) - .finish() - } - } -} - -impl From for AutoModerationTriggerType { - fn from(value: u8) -> Self { - Self(value) - } -} - -impl From for u8 { - fn from(value: AutoModerationTriggerType) -> Self { - value.get() - } -} +impl_typed!(AutoModerationTriggerType, u8); #[cfg(test)] mod tests { diff --git a/twilight-model/src/guild/default_message_notification_level.rs b/twilight-model/src/guild/default_message_notification_level.rs index dec1718204c..c6e78e1ae5a 100644 --- a/twilight-model/src/guild/default_message_notification_level.rs +++ b/twilight-model/src/guild/default_message_notification_level.rs @@ -1,5 +1,4 @@ use serde::{Deserialize, Serialize}; -use std::fmt::{Debug, Formatter, Result as FmtResult}; #[derive(Clone, Copy, Deserialize, Eq, Hash, PartialEq, Serialize)] pub struct DefaultMessageNotificationLevel(u8); @@ -9,27 +8,6 @@ impl DefaultMessageNotificationLevel { pub const MENTIONS: Self = Self::new(1); - /// Create a new command type from a dynamic value. - /// - /// The provided value isn't validated. Known valid values are associated - /// constants such as [`ALL`][`Self::ALL`]. - pub const fn new(default_message_notification_level: u8) -> Self { - Self(default_message_notification_level) - } - - /// Retrieve the value of the command type. - /// - /// # Examples - /// - /// ``` - /// use twilight_model::guild::DefaultMessageNotificationLevel; - /// - /// assert_eq!(1, DefaultMessageNotificationLevel::MENTIONS.get()); - /// ``` - pub const fn get(&self) -> u8 { - self.0 - } - /// Name of the associated constant. /// /// Returns `None` if the value doesn't have a defined constant. @@ -42,32 +20,7 @@ impl DefaultMessageNotificationLevel { } } -impl Debug for DefaultMessageNotificationLevel { - fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult { - if let Some(name) = self.name() { - f.debug_struct("DefaultMessageNotificationLevel") - .field("name", &name) - .field("value", &self.0) - .finish() - } else { - f.debug_tuple("DefaultMessageNotificationLevel") - .field(&self.0) - .finish() - } - } -} - -impl From for DefaultMessageNotificationLevel { - fn from(value: u8) -> Self { - Self(value) - } -} - -impl From for u8 { - fn from(value: DefaultMessageNotificationLevel) -> Self { - value.get() - } -} +impl_typed!(DefaultMessageNotificationLevel, u8); #[cfg(test)] mod tests { diff --git a/twilight-model/src/guild/explicit_content_filter.rs b/twilight-model/src/guild/explicit_content_filter.rs index d12e418bb39..f96179e629d 100644 --- a/twilight-model/src/guild/explicit_content_filter.rs +++ b/twilight-model/src/guild/explicit_content_filter.rs @@ -1,34 +1,14 @@ use serde::{Deserialize, Serialize}; -use std::fmt::{Debug, Formatter, Result as FmtResult}; #[derive(Clone, Copy, Deserialize, Eq, Hash, PartialEq, Serialize)] pub struct ExplicitContentFilter(u8); impl ExplicitContentFilter { pub const NONE: Self = Self::new(0); - pub const MEMBERS_WITHOUT_ROLE: Self = Self::new(1); - pub const ALL_MEMBERS: Self = Self::new(2); - /// Create a new explicit content filter from a dynamic value. - /// - /// The provided value isn't validated. Known valid values are associated - /// constants such as [`NONE`][`Self::NONE`]. - pub const fn new(explicit_content_filter: u8) -> Self { - Self(explicit_content_filter) - } + pub const MEMBERS_WITHOUT_ROLE: Self = Self::new(1); - /// Retrieve the value of the explicit content filter. - /// - /// # Examples - /// - /// ``` - /// use twilight_model::guild::ExplicitContentFilter; - /// - /// assert_eq!(2, ExplicitContentFilter::ALL_MEMBERS.get()); - /// ``` - pub const fn get(&self) -> u8 { - self.0 - } + pub const ALL_MEMBERS: Self = Self::new(2); /// Name of the associated constant. /// @@ -43,32 +23,7 @@ impl ExplicitContentFilter { } } -impl Debug for ExplicitContentFilter { - fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult { - if let Some(name) = self.name() { - f.debug_struct("ExplicitContentFilter") - .field("name", &name) - .field("value", &self.0) - .finish() - } else { - f.debug_tuple("ExplicitContentFilter") - .field(&self.0) - .finish() - } - } -} - -impl From for ExplicitContentFilter { - fn from(value: u8) -> Self { - Self(value) - } -} - -impl From for u8 { - fn from(value: ExplicitContentFilter) -> Self { - value.get() - } -} +impl_typed!(ExplicitContentFilter, u8); #[cfg(test)] mod tests { diff --git a/twilight-model/src/guild/feature.rs b/twilight-model/src/guild/feature.rs index edf4dfbc69f..42e23bc4483 100644 --- a/twilight-model/src/guild/feature.rs +++ b/twilight-model/src/guild/feature.rs @@ -1,10 +1,5 @@ use crate::util::known_string::KnownString; use serde::{Deserialize, Serialize}; -use std::{ - fmt::{Debug, Formatter, Result as FmtResult}, - ops::Deref, - str::FromStr, -}; /// Special and optional guild features. /// @@ -92,22 +87,6 @@ impl GuildFeature { /// Has enabled the welcome screen. pub const WELCOME_SCREEN_ENABLED: Self = Self::from_bytes(b"WELCOME_SCREEN_ENABLED"); - /// Create a guild feature from a dynamic value. - /// - /// The provided guild feature must be 64 bytes or smaller. - pub fn new(guild_feature: &str) -> Option { - KnownString::from_str(guild_feature).map(Self) - } - - /// Get the value of the guild feature. - /// - /// # Panics - /// - /// Panics if the guild feature isn't valid UTF-8. - pub fn get(&self) -> &str { - self.0.get() - } - /// Name of the associated constant. /// /// Returns `None` if the value doesn't have a defined constant. @@ -141,54 +120,9 @@ impl GuildFeature { _ => return None, }) } - - /// Create a guild feature from a set of bytes. - const fn from_bytes(input: &[u8]) -> Self { - Self(KnownString::from_bytes(input)) - } -} - -impl AsRef for GuildFeature { - fn as_ref(&self) -> &str { - self.get() - } -} - -impl Debug for GuildFeature { - fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult { - f.write_str(self.name().unwrap_or_else(|| self.get())) - } } -impl Deref for GuildFeature { - type Target = str; - - fn deref(&self) -> &Self::Target { - self.get() - } -} - -impl FromStr for GuildFeature { - type Err = (); - - fn from_str(s: &str) -> Result { - Self::try_from(s) - } -} - -impl ToString for GuildFeature { - fn to_string(&self) -> String { - KnownString::to_string(&self.0) - } -} - -impl TryFrom<&str> for GuildFeature { - type Error = (); - - fn try_from(value: &str) -> Result { - Self::new(value).ok_or(()) - } -} +impl_typed!(GuildFeature, String); #[cfg(test)] mod tests { diff --git a/twilight-model/src/guild/integration_expire_behavior.rs b/twilight-model/src/guild/integration_expire_behavior.rs index c5f0874a64d..068d38e5374 100644 --- a/twilight-model/src/guild/integration_expire_behavior.rs +++ b/twilight-model/src/guild/integration_expire_behavior.rs @@ -1,5 +1,4 @@ use serde::{Deserialize, Serialize}; -use std::fmt::{Debug, Formatter, Result as FmtResult}; /// Behavior to perform when the user's integration expires. #[derive(Clone, Copy, Deserialize, Eq, Hash, PartialEq, Serialize)] @@ -12,27 +11,6 @@ impl IntegrationExpireBehavior { /// Kick the user when the integration expires. pub const KICK: Self = Self::new(1); - /// Create a new integration expire behavior from a dynamic value. - /// - /// The provided value isn't validated. Known valid values are associated - /// constants such as [`REMOVE_ROLE`][`Self::REMOVE_ROLE`]. - pub const fn new(integration_expire_behavior: u8) -> Self { - Self(integration_expire_behavior) - } - - /// Retrieve the value of the integration expire behavior. - /// - /// # Examples - /// - /// ``` - /// use twilight_model::guild::IntegrationExpireBehavior; - /// - /// assert_eq!(1, IntegrationExpireBehavior::KICK.get()); - /// ``` - pub const fn get(&self) -> u8 { - self.0 - } - /// Name of the associated constant. /// /// Returns `None` if the value doesn't have a defined constant. @@ -45,32 +23,7 @@ impl IntegrationExpireBehavior { } } -impl Debug for IntegrationExpireBehavior { - fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult { - if let Some(name) = self.name() { - f.debug_struct("IntegrationExpireBehavior") - .field("name", &name) - .field("value", &self.0) - .finish() - } else { - f.debug_tuple("IntegrationExpireBehavior") - .field(&self.0) - .finish() - } - } -} - -impl From for IntegrationExpireBehavior { - fn from(value: u8) -> Self { - Self(value) - } -} - -impl From for u8 { - fn from(value: IntegrationExpireBehavior) -> Self { - value.get() - } -} +impl_typed!(IntegrationExpireBehavior, u8); #[cfg(test)] mod tests { diff --git a/twilight-model/src/guild/integration_type.rs b/twilight-model/src/guild/integration_type.rs index 1d5d7aeddb3..96f5dbb1399 100644 --- a/twilight-model/src/guild/integration_type.rs +++ b/twilight-model/src/guild/integration_type.rs @@ -1,10 +1,5 @@ use crate::util::known_string::KnownString; use serde::{Deserialize, Serialize}; -use std::{ - fmt::{Debug, Formatter, Result as FmtResult}, - ops::Deref, - str::FromStr, -}; /// Special and optional guild features. /// @@ -24,22 +19,6 @@ impl GuildIntegrationType { /// Integration is a Youtube connection. pub const YOUTUBE: Self = Self::from_bytes(b"youtube"); - /// Create a guild integration type from a dynamic value. - /// - /// The provided guild integration type must be 64 bytes or smaller. - pub fn new(guild_integration_type: &str) -> Option { - KnownString::from_str(guild_integration_type).map(Self) - } - - /// Get the value of the guild integration type. - /// - /// # Panics - /// - /// Panics if the guild integration type isn't valid UTF-8. - pub fn get(&self) -> &str { - self.0.get() - } - /// Name of the associated constant. /// /// Returns `None` if the value doesn't have a defined constant. @@ -51,54 +30,9 @@ impl GuildIntegrationType { _ => return None, }) } - - /// Create a guild integration type from a set of bytes. - const fn from_bytes(input: &[u8]) -> Self { - Self(KnownString::from_bytes(input)) - } -} - -impl AsRef for GuildIntegrationType { - fn as_ref(&self) -> &str { - self.get() - } -} - -impl Debug for GuildIntegrationType { - fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult { - f.write_str(self.name().unwrap_or_else(|| self.get())) - } } -impl Deref for GuildIntegrationType { - type Target = str; - - fn deref(&self) -> &Self::Target { - self.get() - } -} - -impl FromStr for GuildIntegrationType { - type Err = (); - - fn from_str(s: &str) -> Result { - Self::try_from(s) - } -} - -impl ToString for GuildIntegrationType { - fn to_string(&self) -> String { - KnownString::to_string(&self.0) - } -} - -impl TryFrom<&str> for GuildIntegrationType { - type Error = (); - - fn try_from(value: &str) -> Result { - Self::new(value).ok_or(()) - } -} +impl_typed!(GuildIntegrationType, String); #[cfg(test)] mod tests { diff --git a/twilight-model/src/guild/invite/target_type.rs b/twilight-model/src/guild/invite/target_type.rs index f3e384f1540..bba6b76f607 100644 --- a/twilight-model/src/guild/invite/target_type.rs +++ b/twilight-model/src/guild/invite/target_type.rs @@ -1,5 +1,4 @@ use serde::{Deserialize, Serialize}; -use std::fmt::{Debug, Formatter, Result as FmtResult}; #[derive(Clone, Copy, Deserialize, Eq, Hash, PartialEq, Serialize)] pub struct TargetType(u8); @@ -9,27 +8,6 @@ impl TargetType { pub const EMBEDDED_APPLICATION: Self = Self::new(2); - /// Create a new command type from a dynamic value. - /// - /// The provided value isn't validated. Known valid values are associated - /// constants such as [`STREAM`][`Self::STREAM`]. - pub const fn new(target_type: u8) -> Self { - Self(target_type) - } - - /// Retrieve the value of the command type. - /// - /// # Examples - /// - /// ``` - /// use twilight_model::guild::invite::TargetType; - /// - /// assert_eq!(2, TargetType::EMBEDDED_APPLICATION.get()); - /// ``` - pub const fn get(&self) -> u8 { - self.0 - } - /// Name of the associated constant. /// /// Returns `None` if the value doesn't have a defined constant. @@ -42,30 +20,7 @@ impl TargetType { } } -impl Debug for TargetType { - fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult { - if let Some(name) = self.name() { - f.debug_struct("TargetType") - .field("name", &name) - .field("value", &self.0) - .finish() - } else { - f.debug_tuple("TargetType").field(&self.0).finish() - } - } -} - -impl From for TargetType { - fn from(value: u8) -> Self { - Self(value) - } -} - -impl From for u8 { - fn from(value: TargetType) -> Self { - value.get() - } -} +impl_typed!(TargetType, u8); #[cfg(test)] mod tests { diff --git a/twilight-model/src/guild/mfa_level.rs b/twilight-model/src/guild/mfa_level.rs index 9dba4fe80f2..17ef81b7956 100644 --- a/twilight-model/src/guild/mfa_level.rs +++ b/twilight-model/src/guild/mfa_level.rs @@ -1,33 +1,12 @@ use serde::{Deserialize, Serialize}; -use std::fmt::{Debug, Formatter, Result as FmtResult}; #[derive(Clone, Copy, Deserialize, Eq, Hash, PartialEq, Serialize)] pub struct MfaLevel(u8); impl MfaLevel { pub const NONE: Self = Self::new(0); - pub const ELEVATED: Self = Self::new(1); - /// Create a new MFA Level from a dynamic value. - /// - /// The provided value isn't validated. Known valid values are associated - /// constants such as [`ELEVATED`][`Self::ELEVATED`]. - pub const fn new(mfa_level: u8) -> Self { - Self(mfa_level) - } - - /// Retrieve the value of the MFA Level. - /// - /// # Examples - /// - /// ``` - /// use twilight_model::guild::MfaLevel; - /// - /// assert_eq!(1, MfaLevel::ELEVATED.get()); - /// ``` - pub const fn get(&self) -> u8 { - self.0 - } + pub const ELEVATED: Self = Self::new(1); /// Name of the associated constant. /// @@ -41,30 +20,7 @@ impl MfaLevel { } } -impl Debug for MfaLevel { - fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult { - if let Some(name) = self.name() { - f.debug_struct("MfaLevel") - .field("name", &name) - .field("value", &self.0) - .finish() - } else { - f.debug_tuple("MfaLevel").field(&self.0).finish() - } - } -} - -impl From for MfaLevel { - fn from(value: u8) -> Self { - Self(value) - } -} - -impl From for u8 { - fn from(value: MfaLevel) -> Self { - value.get() - } -} +impl_typed!(MfaLevel, u8); #[cfg(test)] mod tests { diff --git a/twilight-model/src/guild/nsfw_level.rs b/twilight-model/src/guild/nsfw_level.rs index 5bf980c85a9..2eaf31eef32 100644 --- a/twilight-model/src/guild/nsfw_level.rs +++ b/twilight-model/src/guild/nsfw_level.rs @@ -1,35 +1,16 @@ use serde::{Deserialize, Serialize}; -use std::fmt::{Debug, Formatter, Result as FmtResult}; #[derive(Clone, Copy, Deserialize, Eq, Hash, PartialEq, Serialize)] pub struct NSFWLevel(u8); impl NSFWLevel { pub const DEFAULT: Self = Self::new(0); + pub const EXPLICIT: Self = Self::new(1); - pub const SAFE: Self = Self::new(2); - pub const AGE_RESTRICTED: Self = Self::new(3); - /// Create a new NSFW Level from a dynamic value. - /// - /// The provided value isn't validated. Known valid values are associated - /// constants such as [`EXPLICIT`][`Self::EXPLICIT`]. - pub const fn new(connection_visibility: u8) -> Self { - Self(connection_visibility) - } + pub const SAFE: Self = Self::new(2); - /// Retrieve the value of the NSFW Level. - /// - /// # Examples - /// - /// ``` - /// use twilight_model::guild::NSFWLevel; - /// - /// assert_eq!(2, NSFWLevel::SAFE.get()); - /// ``` - pub const fn get(&self) -> u8 { - self.0 - } + pub const AGE_RESTRICTED: Self = Self::new(3); /// Name of the associated constant. /// @@ -45,30 +26,7 @@ impl NSFWLevel { } } -impl Debug for NSFWLevel { - fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult { - if let Some(name) = self.name() { - f.debug_struct("NSFWLevel") - .field("name", &name) - .field("value", &self.0) - .finish() - } else { - f.debug_tuple("NSFWLevel").field(&self.0).finish() - } - } -} - -impl From for NSFWLevel { - fn from(value: u8) -> Self { - Self(value) - } -} - -impl From for u8 { - fn from(value: NSFWLevel) -> Self { - value.get() - } -} +impl_typed!(NSFWLevel, u8); #[cfg(test)] mod tests { diff --git a/twilight-model/src/guild/premium_tier.rs b/twilight-model/src/guild/premium_tier.rs index 2508a61e9e4..fca35a855b2 100644 --- a/twilight-model/src/guild/premium_tier.rs +++ b/twilight-model/src/guild/premium_tier.rs @@ -1,35 +1,16 @@ use serde::{Deserialize, Serialize}; -use std::fmt::{Debug, Formatter, Result as FmtResult}; #[derive(Clone, Copy, Deserialize, Eq, Hash, PartialEq, Serialize)] pub struct PremiumTier(u8); impl PremiumTier { pub const NONE: Self = Self::new(0); + pub const TIER_1: Self = Self::new(1); - pub const TIER_2: Self = Self::new(2); - pub const TIER_3: Self = Self::new(3); - /// Create a new premium tier from a dynamic value. - /// - /// The provided value isn't validated. Known valid values are associated - /// constants such as [`TIER_!`][`Self::TIER_1`]. - pub const fn new(premium_tier: u8) -> Self { - Self(premium_tier) - } + pub const TIER_2: Self = Self::new(2); - /// Retrieve the value of the premium tier. - /// - /// # Examples - /// - /// ``` - /// use twilight_model::guild::PremiumTier; - /// - /// assert_eq!(2, PremiumTier::TIER_2.get()); - /// ``` - pub const fn get(&self) -> u8 { - self.0 - } + pub const TIER_3: Self = Self::new(3); /// Name of the associated constant. /// @@ -45,36 +26,13 @@ impl PremiumTier { } } -impl Debug for PremiumTier { - fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult { - if let Some(name) = self.name() { - f.debug_struct("PremiumTier") - .field("name", &name) - .field("value", &self.0) - .finish() - } else { - f.debug_tuple("PremiumTier").field(&self.0).finish() - } - } -} - impl Default for PremiumTier { fn default() -> Self { Self::NONE } } -impl From for PremiumTier { - fn from(value: u8) -> Self { - Self(value) - } -} - -impl From for u8 { - fn from(value: PremiumTier) -> Self { - value.get() - } -} +impl_typed!(PremiumTier, u8); #[cfg(test)] mod tests { diff --git a/twilight-model/src/guild/scheduled_event/mod.rs b/twilight-model/src/guild/scheduled_event/mod.rs index 94cf22c4a72..19cbbe41846 100644 --- a/twilight-model/src/guild/scheduled_event/mod.rs +++ b/twilight-model/src/guild/scheduled_event/mod.rs @@ -16,7 +16,6 @@ use crate::{ util::{ImageHash, Timestamp}, }; use serde::{Deserialize, Serialize}; -use std::fmt::{Debug, Formatter, Result as FmtResult}; /// Representation of a scheduled event. /// @@ -103,27 +102,6 @@ impl EntityType { /// Event takes place outside of Discord. pub const EXTERNAL: Self = Self::new(3); - /// Create a new scheduled event entity type from a dynamic value. - /// - /// The provided value isn't validated. Known valid values are associated - /// constants such as [`STAGE_INSTANCE`][`Self::STAGE_INSTANCE`]. - pub const fn new(entity_type: u8) -> Self { - Self(entity_type) - } - - /// Retrieve the value of the scheduled event entity type. - /// - /// # Examples - /// - /// ``` - /// use twilight_model::guild::scheduled_event::EntityType; - /// - /// assert_eq!(2, EntityType::VOICE.get()); - /// ``` - pub const fn get(&self) -> u8 { - self.0 - } - /// Name of the associated constant. /// /// Returns `None` if the value doesn't have a defined constant. @@ -137,30 +115,7 @@ impl EntityType { } } -impl Debug for EntityType { - fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult { - if let Some(name) = self.name() { - f.debug_struct("EntityType") - .field("name", &name) - .field("value", &self.0) - .finish() - } else { - f.debug_tuple("EntityType").field(&self.0).finish() - } - } -} - -impl From for EntityType { - fn from(value: u8) -> Self { - Self(value) - } -} - -impl From for u8 { - fn from(value: EntityType) -> Self { - value.get() - } -} +impl_typed!(EntityType, u8); /// Privacy level of an event. #[derive(Clone, Copy, Deserialize, Eq, Hash, PartialEq, Serialize)] @@ -170,27 +125,6 @@ impl PrivacyLevel { /// Event is only accessible to guild members. pub const GUILD_ONLY: Self = Self::new(2); - /// Create a new privacy level from a dynamic value. - /// - /// The provided value isn't validated. Known valid values are associated - /// constants such as [`GUILD_ONLY`][`Self::GUILD_ONLY`]. - pub const fn new(privacy_level: u8) -> Self { - Self(privacy_level) - } - - /// Retrieve the value of the privacy level. - /// - /// # Examples - /// - /// ``` - /// use twilight_model::guild::scheduled_event::PrivacyLevel; - /// - /// assert_eq!(2, PrivacyLevel::GUILD_ONLY.get()); - /// ``` - pub const fn get(&self) -> u8 { - self.0 - } - /// Name of the associated constant. /// /// Returns `None` if the value doesn't have a defined constant. @@ -202,30 +136,7 @@ impl PrivacyLevel { } } -impl Debug for PrivacyLevel { - fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult { - if let Some(name) = self.name() { - f.debug_struct("PrivacyLevel") - .field("name", &name) - .field("value", &self.0) - .finish() - } else { - f.debug_tuple("PrivacyLevel").field(&self.0).finish() - } - } -} - -impl From for PrivacyLevel { - fn from(value: u8) -> Self { - Self(value) - } -} - -impl From for u8 { - fn from(value: PrivacyLevel) -> Self { - value.get() - } -} +impl_typed!(PrivacyLevel, u8); /// Status of an event. #[derive(Clone, Copy, Deserialize, Eq, Hash, PartialEq, Serialize)] @@ -248,27 +159,6 @@ impl Status { /// Event is cancelled. pub const CANCELLED: Self = Self::new(4); - /// Create a new scheduled event status from a dynamic value. - /// - /// The provided value isn't validated. Known valid values are associated - /// constants such as [`SCHEDULED`][`Self::SCHEDULED`]. - pub const fn new(scheduled_event_status: u8) -> Self { - Self(scheduled_event_status) - } - - /// Retrieve the value of the scheduled event status. - /// - /// # Examples - /// - /// ``` - /// use twilight_model::guild::scheduled_event::Status; - /// - /// assert_eq!(2, Status::ACTIVE.get()); - /// ``` - pub const fn get(&self) -> u8 { - self.0 - } - /// Name of the associated constant. /// /// Returns `None` if the value doesn't have a defined constant. @@ -283,30 +173,7 @@ impl Status { } } -impl Debug for Status { - fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult { - if let Some(name) = self.name() { - f.debug_struct("Status") - .field("name", &name) - .field("value", &self.0) - .finish() - } else { - f.debug_tuple("Status").field(&self.0).finish() - } - } -} - -impl From for Status { - fn from(value: u8) -> Self { - Self(value) - } -} - -impl From for u8 { - fn from(value: Status) -> Self { - value.get() - } -} +impl_typed!(Status, u8); #[cfg(test)] mod tests { diff --git a/twilight-model/src/guild/verification_level.rs b/twilight-model/src/guild/verification_level.rs index 2feae93b128..b17da4622e3 100644 --- a/twilight-model/src/guild/verification_level.rs +++ b/twilight-model/src/guild/verification_level.rs @@ -1,36 +1,18 @@ use serde::{Deserialize, Serialize}; -use std::fmt::{Debug, Formatter, Result as FmtResult}; #[derive(Clone, Copy, Deserialize, Eq, Hash, Ord, PartialEq, PartialOrd, Serialize)] pub struct VerificationLevel(u8); impl VerificationLevel { pub const NONE: Self = Self::new(0); + 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); - /// Create a new verification level from a dynamic value. - /// - /// The provided value isn't validated. Known valid values are associated - /// constants such as [`MEDIUM`][`Self::MEDIUM`]. - pub const fn new(verification_level: u8) -> Self { - Self(verification_level) - } + pub const HIGH: Self = Self::new(3); - /// Retrieve the value of the verification level. - /// - /// # Examples - /// - /// ``` - /// use twilight_model::guild::VerificationLevel; - /// - /// assert_eq!(1, VerificationLevel::LOW.get()); - /// ``` - pub const fn get(&self) -> u8 { - self.0 - } + pub const VERY_HIGH: Self = Self::new(4); /// Name of the associated constant. /// @@ -47,30 +29,7 @@ impl VerificationLevel { } } -impl Debug for VerificationLevel { - fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult { - if let Some(name) = self.name() { - f.debug_struct("VerificationLevel") - .field("name", &name) - .field("value", &self.0) - .finish() - } else { - f.debug_tuple("VerificationLevel").field(&self.0).finish() - } - } -} - -impl From for VerificationLevel { - fn from(value: u8) -> Self { - Self(value) - } -} - -impl From for u8 { - fn from(value: VerificationLevel) -> Self { - value.get() - } -} +impl_typed!(VerificationLevel, u8); #[cfg(test)] mod tests { diff --git a/twilight-model/src/http/interaction.rs b/twilight-model/src/http/interaction.rs index 7268edc4d06..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 std::fmt::{Debug, Formatter, Result as FmtResult}; /// Interaction response sent to Discord. /// @@ -107,27 +106,6 @@ impl InteractionResponseType { /// Respond to an interaction with a popup modal. pub const MODAL: Self = Self::new(9); - /// Create a new interaction response type from a dynamic value. - /// - /// The provided value isn't validated. Known valid values are associated - /// constants such as [`UPDATE_MESSAGE`][`Self::UPDATE_MESSAGE`]. - pub const fn new(interaction_response_type: u8) -> Self { - Self(interaction_response_type) - } - - /// Retrieve the value of the interaction response type. - /// - /// # Examples - /// - /// ``` - /// use twilight_model::http::interaction::InteractionResponseType; - /// - /// assert_eq!(9, InteractionResponseType::MODAL.get()); - /// ``` - pub const fn get(&self) -> u8 { - self.0 - } - /// Name of the associated constant. /// /// Returns `None` if the value doesn't have a defined constant. @@ -147,32 +125,7 @@ impl InteractionResponseType { } } -impl Debug for InteractionResponseType { - fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult { - if let Some(name) = self.name() { - f.debug_struct("InteractionResponseType") - .field("name", &name) - .field("value", &self.0) - .finish() - } else { - f.debug_tuple("InteractionResponseType") - .field(&self.0) - .finish() - } - } -} - -impl From for InteractionResponseType { - fn from(value: u8) -> Self { - Self(value) - } -} - -impl From for u8 { - fn from(value: InteractionResponseType) -> Self { - value.get() - } -} +impl_typed!(InteractionResponseType, u8); #[cfg(test)] mod tests { diff --git a/twilight-model/src/http/permission_overwrite.rs b/twilight-model/src/http/permission_overwrite.rs index 62d3bea0ae7..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 std::fmt::{Debug, Formatter, Result as FmtResult}; /// Permission overwrite data for a role or member. #[derive(Clone, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)] @@ -30,27 +29,6 @@ impl PermissionOverwriteType { /// Permission overwrite targets an individual role. pub const ROLE: Self = Self::new(0); - /// Create a new permission overwrite from a dynamic value. - /// - /// The provided value isn't validated. Known valid values are associated - /// constants such as [`MEMBER`][`Self::MEMBER`]. - pub const fn new(permission_overwrite: u8) -> Self { - Self(permission_overwrite) - } - - /// Retrieve the value of the permission overwrite. - /// - /// # Examples - /// - /// ``` - /// use twilight_model::http::permission_overwrite::PermissionOverwriteType; - /// - /// assert_eq!(0, PermissionOverwriteType::ROLE.get()); - /// ``` - pub const fn get(&self) -> u8 { - self.0 - } - /// Name of the associated constant. /// /// Returns `None` if the value doesn't have a defined constant. @@ -63,32 +41,7 @@ impl PermissionOverwriteType { } } -impl Debug for PermissionOverwriteType { - fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult { - if let Some(name) = self.name() { - f.debug_struct("PermissionOverwriteType") - .field("name", &name) - .field("value", &self.0) - .finish() - } else { - f.debug_tuple("PermissionOverwriteType") - .field(&self.0) - .finish() - } - } -} - -impl From for PermissionOverwriteType { - fn from(value: u8) -> Self { - Self(value) - } -} - -impl From for u8 { - fn from(value: PermissionOverwriteType) -> Self { - value.get() - } -} +impl_typed!(PermissionOverwriteType, u8); #[cfg(test)] mod tests { 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/scope.rs b/twilight-model/src/oauth/scope.rs index 7eabb76f1b6..a2b312aeca7 100644 --- a/twilight-model/src/oauth/scope.rs +++ b/twilight-model/src/oauth/scope.rs @@ -6,11 +6,6 @@ use crate::util::known_string::KnownString; use serde::{Deserialize, Serialize}; -use std::{ - fmt::{Debug, Formatter, Result as FmtResult}, - ops::Deref, - str::FromStr, -}; /// OAuth 2 scope. /// @@ -160,22 +155,6 @@ impl Scope { /// authorization code grants. pub const WEBHOOK_INCOMING: Self = Self::from_bytes(b"webhook.incoming"); - /// Create a scope from a dynamic value. - /// - /// The provided scope must be 64 bytes or smaller. - pub fn new(scope: &str) -> Option { - KnownString::from_str(scope).map(Self) - } - - /// Get the value of the scope. - /// - /// # Panics - /// - /// Panics if the scope isn't valid UTF-8. - pub fn get(&self) -> &str { - self.0.get() - } - /// Name of the associated constant. /// /// Returns `None` if the value doesn't have a defined constant. @@ -214,54 +193,9 @@ impl Scope { _ => return None, }) } - - /// Create a scope from a set of bytes. - const fn from_bytes(input: &[u8]) -> Self { - Self(KnownString::from_bytes(input)) - } -} - -impl AsRef for Scope { - fn as_ref(&self) -> &str { - self.get() - } -} - -impl Debug for Scope { - fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult { - f.write_str(self.name().unwrap_or_else(|| self.get())) - } } -impl Deref for Scope { - type Target = str; - - fn deref(&self) -> &Self::Target { - self.get() - } -} - -impl FromStr for Scope { - type Err = (); - - fn from_str(s: &str) -> Result { - Self::try_from(s) - } -} - -impl ToString for Scope { - fn to_string(&self) -> String { - KnownString::to_string(&self.0) - } -} - -impl TryFrom<&str> for Scope { - type Error = (); - - fn try_from(value: &str) -> Result { - Self::new(value).ok_or(()) - } -} +impl_typed!(Scope, String); #[cfg(test)] mod tests { diff --git a/twilight-model/src/oauth/team/membership_state.rs b/twilight-model/src/oauth/team/membership_state.rs index dab9e5e7a56..93e085b1621 100644 --- a/twilight-model/src/oauth/team/membership_state.rs +++ b/twilight-model/src/oauth/team/membership_state.rs @@ -1,33 +1,12 @@ use serde::{Deserialize, Serialize}; -use std::fmt::{Debug, Formatter, Result as FmtResult}; #[derive(Clone, Copy, Deserialize, Eq, Hash, PartialEq, Serialize)] pub struct TeamMembershipState(u8); impl TeamMembershipState { pub const INVITED: Self = Self::new(1); - pub const ACCEPTED: Self = Self::new(2); - /// Create a new membership state from a dynamic value. - /// - /// The provided value isn't validated. Known valid values are associated - /// constants such as [`ACCEPTED`][`Self::ACCEPTED`]. - pub const fn new(membership_state: u8) -> Self { - Self(membership_state) - } - - /// Retrieve the value of the membership state. - /// - /// # Examples - /// - /// ``` - /// use twilight_model::oauth::team::TeamMembershipState; - /// - /// assert_eq!(1, TeamMembershipState::INVITED.get()); - /// ``` - pub const fn get(&self) -> u8 { - self.0 - } + pub const ACCEPTED: Self = Self::new(2); /// Name of the associated constant. /// @@ -41,30 +20,7 @@ impl TeamMembershipState { } } -impl Debug for TeamMembershipState { - fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult { - if let Some(name) = self.name() { - f.debug_struct("TeamMembershipState") - .field("name", &name) - .field("value", &self.0) - .finish() - } else { - f.debug_tuple("TeamMembershipState").field(&self.0).finish() - } - } -} - -impl From for TeamMembershipState { - fn from(value: u8) -> Self { - Self(value) - } -} - -impl From for u8 { - fn from(value: TeamMembershipState) -> Self { - value.get() - } -} +impl_typed!(TeamMembershipState, u8); #[cfg(test)] mod tests { diff --git a/twilight-model/src/user/connection_visibility.rs b/twilight-model/src/user/connection_visibility.rs index 0004abf249d..fa33d1f1eef 100644 --- a/twilight-model/src/user/connection_visibility.rs +++ b/twilight-model/src/user/connection_visibility.rs @@ -1,5 +1,4 @@ use serde::{Deserialize, Serialize}; -use std::fmt::{Debug, Formatter, Result as FmtResult}; #[derive(Clone, Copy, Deserialize, Eq, Hash, PartialEq, Serialize)] pub struct ConnectionVisibility(u8); @@ -11,27 +10,6 @@ impl ConnectionVisibility { /// Connection is visible to everyone. pub const EVERYONE: Self = Self::new(1); - /// Create a new connection visibility from a dynamic value. - /// - /// The provided value isn't validated. Known valid values are associated - /// constants such as [`EVERYONE`][`Self::EVERYONE`]. - pub const fn new(connection_visibility: u8) -> Self { - Self(connection_visibility) - } - - /// Retrieve the value of the connection visibility. - /// - /// # Examples - /// - /// ``` - /// use twilight_model::user::ConnectionVisibility; - /// - /// assert_eq!(1, ConnectionVisibility::EVERYONE.get()); - /// ``` - pub const fn get(&self) -> u8 { - self.0 - } - /// Name of the associated constant. /// /// Returns `None` if the value doesn't have a defined constant. @@ -44,32 +22,7 @@ impl ConnectionVisibility { } } -impl Debug for ConnectionVisibility { - fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult { - if let Some(name) = self.name() { - f.debug_struct("ConnectionVisibility") - .field("name", &name) - .field("value", &self.0) - .finish() - } else { - f.debug_tuple("ConnectionVisibility") - .field(&self.0) - .finish() - } - } -} - -impl From for ConnectionVisibility { - fn from(value: u8) -> Self { - Self(value) - } -} - -impl From for u8 { - fn from(value: ConnectionVisibility) -> Self { - value.get() - } -} +impl_typed!(ConnectionVisibility, u8); #[cfg(test)] mod tests { diff --git a/twilight-model/src/user/premium_type.rs b/twilight-model/src/user/premium_type.rs index 9ef873fb5bb..64f00117484 100644 --- a/twilight-model/src/user/premium_type.rs +++ b/twilight-model/src/user/premium_type.rs @@ -1,5 +1,4 @@ use serde::{Deserialize, Serialize}; -use std::fmt::{Debug, Formatter, Result as FmtResult}; /// Type of premium tier for a [`User`]. /// @@ -20,27 +19,6 @@ impl PremiumType { /// User has Nitro Basic. pub const NITRO_BASIC: Self = Self::new(3); - /// Create a new premium type from a dynamic value. - /// - /// The provided value isn't validated. Known valid values are associated - /// constants such as [`NITRO`][`Self::NITRO`]. - pub const fn new(premium_type: u8) -> Self { - Self(premium_type) - } - - /// Retrieve the value of the premium type. - /// - /// # Examples - /// - /// ``` - /// use twilight_model::user::PremiumType; - /// - /// assert_eq!(2, PremiumType::NITRO.get()); - /// ``` - pub const fn get(&self) -> u8 { - self.0 - } - /// Name of the associated constant. /// /// Returns `None` if the value doesn't have a defined constant. @@ -55,30 +33,7 @@ impl PremiumType { } } -impl Debug for PremiumType { - fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult { - if let Some(name) = self.name() { - f.debug_struct("PremiumType") - .field("name", &name) - .field("value", &self.0) - .finish() - } else { - f.debug_tuple("PremiumType").field(&self.0).finish() - } - } -} - -impl From for PremiumType { - fn from(value: u8) -> Self { - Self(value) - } -} - -impl From for u8 { - fn from(value: PremiumType) -> Self { - value.get() - } -} +impl_typed!(PremiumType, u8); #[cfg(test)] mod tests { diff --git a/twilight-model/src/util/mod.rs b/twilight-model/src/util/mod.rs index 4290f835a46..a052bf2f253 100644 --- a/twilight-model/src/util/mod.rs +++ b/twilight-model/src/util/mod.rs @@ -11,3 +11,119 @@ pub use self::{datetime::Timestamp, image_hash::ImageHash}; 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/voice/close_code.rs b/twilight-model/src/voice/close_code.rs index f9c454647b3..efb72953ba8 100644 --- a/twilight-model/src/voice/close_code.rs +++ b/twilight-model/src/voice/close_code.rs @@ -1,5 +1,4 @@ use serde::{Deserialize, Serialize}; -use std::fmt::{Debug, Formatter, Result as FmtResult}; /// Voice gateway close event codes. #[derive(Clone, Copy, Deserialize, Eq, Hash, PartialEq, Serialize)] @@ -42,27 +41,6 @@ impl CloseCode { /// The encryption could not be recognized. pub const UNKNOWN_ENCRYPTION_MODE: Self = Self::new(4016); - /// Create a new close code from a dynamic value. - /// - /// The provided value isn't validated. Known valid values are associated - /// constants such as [`DISCONNECTED`][`Self::DISCONNECTED`]. - pub const fn new(close_code: u16) -> Self { - Self(close_code) - } - - /// Retrieve the value of the close code. - /// - /// # Examples - /// - /// ``` - /// use twilight_model::voice::CloseCode; - /// - /// assert_eq!(4002, CloseCode::DECODE_ERROR.get()); - /// ``` - pub const fn get(&self) -> u16 { - self.0 - } - /// Name of the associated constant. /// /// Returns `None` if the value doesn't have a defined constant. @@ -85,30 +63,7 @@ impl CloseCode { } } -impl Debug for CloseCode { - fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult { - if let Some(name) = self.name() { - f.debug_struct("CloseCode") - .field("name", &name) - .field("value", &self.0) - .finish() - } else { - f.debug_tuple("CloseCode").field(&self.0).finish() - } - } -} - -impl From for CloseCode { - fn from(value: u16) -> Self { - Self(value) - } -} - -impl From for u16 { - fn from(value: CloseCode) -> Self { - value.get() - } -} +impl_typed!(CloseCode, u16); #[cfg(test)] mod tests { diff --git a/twilight-model/src/voice/opcode.rs b/twilight-model/src/voice/opcode.rs index 18cc3baade8..d61bfca6629 100644 --- a/twilight-model/src/voice/opcode.rs +++ b/twilight-model/src/voice/opcode.rs @@ -1,5 +1,4 @@ use serde::{Deserialize, Serialize}; -use std::fmt::{Debug, Formatter, Result as FmtResult}; /// Voice gateway opcodes. #[derive(Clone, Copy, Deserialize, Eq, Hash, PartialEq, Serialize)] @@ -39,27 +38,6 @@ impl OpCode { /// Received to indicate someone was disconnected. pub const CLIENT_DISCONNECT: Self = Self::new(13); - /// Create a new opcode from a dynamic value. - /// - /// The provided value isn't validated. Known valid values are associated - /// constants such as [`IDENTIFY`][`Self::IDENTIFY`]. - pub const fn new(opcode: u8) -> Self { - Self(opcode) - } - - /// Retrieve the value of the opcode. - /// - /// # Examples - /// - /// ``` - /// use twilight_model::voice::OpCode; - /// - /// assert_eq!(5, OpCode::SPEAKING.get()); - /// ``` - pub const fn get(&self) -> u8 { - self.0 - } - /// Name of the associated constant. /// /// Returns `None` if the value doesn't have a defined constant. @@ -81,30 +59,7 @@ impl OpCode { } } -impl Debug for OpCode { - fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult { - if let Some(name) = self.name() { - f.debug_struct("OpCode") - .field("name", &name) - .field("value", &self.0) - .finish() - } else { - f.debug_tuple("OpCode").field(&self.0).finish() - } - } -} - -impl From for OpCode { - fn from(value: u8) -> Self { - Self(value) - } -} - -impl From for u8 { - fn from(value: OpCode) -> Self { - value.get() - } -} +impl_typed!(OpCode, u8); #[cfg(test)] mod tests {