Skip to content

Commit

Permalink
feat: add ResolvedMentionable (#26)
Browse files Browse the repository at this point in the history
* feat: add ResolvedMentionable

* remove unknown mentionable

* update changelog
  • Loading branch information
baptiste0928 authored Jun 23, 2023
1 parent 80a41e2 commit 6c196cb
Show file tree
Hide file tree
Showing 5 changed files with 82 additions and 11 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## [Unreleased]
### Added
- Subcommands enums now support `Box`ed variants to avoid large enums.
- `ResolvedMentionable` type can be used to resolve a mentionable to either
a user or a role.

### Fixed
- Strings are now trimmed in macro attributes to match Discord's behavior.
Expand Down
52 changes: 52 additions & 0 deletions twilight-interactions/src/command/command_model.rs
Original file line number Diff line number Diff line change
Expand Up @@ -351,6 +351,28 @@ pub struct ResolvedUser {
pub member: Option<InteractionMember>,
}

/// A resolved mentionable.
///
/// This struct implements [`CommandOption`] and can be used to obtain the
/// resolved data from a mentionable ID, that can be either a user or a role.
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum ResolvedMentionable {
/// User mention.
User(ResolvedUser),
/// Role mention.
Role(Role),
}

impl ResolvedMentionable {
/// Get the ID of the mentionable.
pub fn id(&self) -> Id<GenericMarker> {
match self {
ResolvedMentionable::User(user) => user.resolved.id.cast(),
ResolvedMentionable::Role(role) => role.id.cast(),
}
}
}

/// An autocomplete command field.
///
/// This type represent a value parsed from an autocomplete field. See "Autocomplete interactions"
Expand Down Expand Up @@ -622,6 +644,36 @@ impl CommandOption for ResolvedUser {
}
}

impl CommandOption for ResolvedMentionable {
fn from_option(
value: CommandOptionValue,
_data: CommandOptionData,
resolved: Option<&CommandInteractionDataResolved>,
) -> Result<Self, ParseOptionErrorType> {
let id = match value {
CommandOptionValue::Mentionable(value) => value,
other => return Err(ParseOptionErrorType::InvalidType(other.kind())),
};

let user_id = id.cast();
if let Ok(user) = lookup!(resolved.users, user_id) {
let resolved_user = ResolvedUser {
resolved: user,
member: lookup!(resolved.members, user_id).ok(),
};

return Ok(Self::User(resolved_user));
}

let role_id = id.cast();
if let Ok(role) = lookup!(resolved.roles, role_id) {
return Ok(Self::Role(role));
}

Err(ParseOptionErrorType::LookupFailed(id.into()))
}
}

impl CommandOption for InteractionChannel {
fn from_option(
value: CommandOptionValue,
Expand Down
8 changes: 7 additions & 1 deletion twilight-interactions/src/command/create_command.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ use twilight_model::{
user::User,
};

use super::{internal::CreateOptionData, ResolvedUser};
use super::{internal::CreateOptionData, ResolvedMentionable, ResolvedUser};

/// Create a slash command from a type.
///
Expand Down Expand Up @@ -294,6 +294,12 @@ impl CreateOption for ResolvedUser {
}
}

impl CreateOption for ResolvedMentionable {
fn create_option(data: CreateOptionData) -> CommandOption {
data.into_option(CommandOptionType::Mentionable)
}
}

impl CreateOption for InteractionChannel {
fn create_option(data: CreateOptionData) -> CommandOption {
data.into_option(CommandOptionType::Channel)
Expand Down
5 changes: 3 additions & 2 deletions twilight-interactions/src/command/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@
//! | `USER` | [`ResolvedUser`], [`User`], [`Id<UserMarker>`] |
//! | `CHANNEL` | [`InteractionChannel`], [`Id<ChannelMarker>`] |
//! | `ROLE` | [`Role`], [`Id<RoleMarker>`] |
//! | `MENTIONABLE` | [`Id<GenericMarker>`] |
//! | `MENTIONABLE` | [`ResolvedMentionable`], [`Id<GenericMarker>`] |
//! | `ATTACHMENT` | [`Attachment`], [`Id<AttachmentMarker>`] |
//!
//! [`from_interaction`]: CommandModel::from_interaction
Expand All @@ -89,7 +89,8 @@ mod create_command;
pub mod internal;

pub use command_model::{
AutocompleteValue, CommandInputData, CommandModel, CommandOption, ResolvedUser,
AutocompleteValue, CommandInputData, CommandModel, CommandOption, ResolvedMentionable,
ResolvedUser,
};
pub use create_command::{ApplicationCommandData, CreateCommand, CreateOption};
#[cfg(feature = "derive")]
Expand Down
26 changes: 18 additions & 8 deletions twilight-interactions/tests/command_model.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
use std::{borrow::Cow, collections::HashMap};

use twilight_interactions::command::{CommandInputData, CommandModel, CommandOption, ResolvedUser};
use twilight_interactions::command::{
CommandInputData, CommandModel, CommandOption, ResolvedMentionable, ResolvedUser,
};
use twilight_model::{
application::interaction::application_command::{
CommandDataOption, CommandInteractionDataResolved, CommandOptionValue, InteractionMember,
Expand All @@ -22,6 +24,7 @@ where
number: Option<i64>,
generic: T,
cow: Cow<'a, str>,
mentionable: ResolvedMentionable,
}

#[derive(CommandModel, Debug, PartialEq, Eq)]
Expand Down Expand Up @@ -51,6 +54,10 @@ fn test_command_model() {
name: "cow".into(),
value: CommandOptionValue::String("cow".into()),
},
CommandDataOption {
name: "mentionable".into(),
value: CommandOptionValue::Mentionable(user_id.cast()),
},
];

let member = InteractionMember {
Expand Down Expand Up @@ -83,11 +90,16 @@ fn test_command_model() {
banner: None,
};

let resolved_user = ResolvedUser {
resolved: user.clone(),
member: Some(member.clone()),
};

let resolved = CommandInteractionDataResolved {
channels: HashMap::new(),
members: HashMap::from([(user_id, member.clone())]),
members: HashMap::from([(user_id, member)]),
roles: HashMap::new(),
users: HashMap::from([(user_id, user.clone())]),
users: HashMap::from([(user_id, user)]),
messages: HashMap::new(),
attachments: HashMap::new(),
};
Expand All @@ -101,14 +113,12 @@ fn test_command_model() {

assert_eq!(
DemoCommand {
user: ResolvedUser {
resolved: user,
member: Some(member)
},
user: resolved_user.clone(),
text: "hello world".into(),
number: Some(42),
generic: 0_i64,
cow: Cow::Borrowed("cow")
cow: Cow::Borrowed("cow"),
mentionable: ResolvedMentionable::User(resolved_user)
},
result
);
Expand Down

0 comments on commit 6c196cb

Please sign in to comment.