Skip to content

Commit

Permalink
Add poise::StrArg for FromStr arguments
Browse files Browse the repository at this point in the history
  • Loading branch information
GnomedDev committed Nov 13, 2024
1 parent c8e2845 commit 4866c24
Show file tree
Hide file tree
Showing 4 changed files with 69 additions and 7 deletions.
2 changes: 1 addition & 1 deletion macros/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ for example for command-specific help (i.e. `~help command_name`). Escape newlin
SlashContext, which contain a variety of context data each. Context provides some utility methods to
access data present in both PrefixContext and SlashContext, like `author()` or `created_at()`.
All following parameters are inputs to the command. You can use all types that implement `poise::PopArgument`, `serenity::ArgumentConvert` or `std::str::FromStr`.
All following parameters are inputs to the command. You can use all types that implement `PopArgument` (for prefix) or `SlashArgument` (for slash).
You can also wrap types in `Option` or `Vec` to make them optional or variadic. In addition, there
are multiple attributes you can use on parameters:
Expand Down
61 changes: 61 additions & 0 deletions src/argument.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
use std::str::FromStr;

use crate::{
serenity_prelude as serenity, PopArgument, PopArgumentResult, SlashArgError, SlashArgument,
};

/// A wrapper for `T` to implement [`SlashArgument`] and [`PopArgument`] via [`FromStr`].
///
/// This is useful if you need to take an argument via a string, but immediately convert it via [`FromStr`].
pub struct StrArg<T>(pub T);

#[async_trait::async_trait]
impl<T> SlashArgument for StrArg<T>
where
T: FromStr,
T::Err: std::error::Error + Send + Sync + 'static,
{
fn create(builder: serenity::CreateCommandOption) -> serenity::CreateCommandOption {
builder.kind(serenity::CommandOptionType::String)
}

async fn extract(
_: &serenity::Context,
_: &serenity::CommandInteraction,
value: &serenity::ResolvedValue<'_>,
) -> Result<Self, SlashArgError> {
let serenity::ResolvedValue::String(value) = value else {
return Err(SlashArgError::new_command_structure_mismatch(
"expected a String",
));
};

match T::from_str(value) {
Ok(value) => Ok(Self(value)),
Err(err) => Err(SlashArgError::Parse {
error: err.into(),
input: String::from(*value),
}),
}
}
}

#[async_trait::async_trait]
impl<'a, T> PopArgument<'a> for StrArg<T>
where
T: FromStr,
T::Err: std::error::Error + Send + Sync + 'static,
{
async fn pop_from(
args: &'a str,
attachment_index: usize,
ctx: &serenity::Context,
msg: &serenity::Message,
) -> PopArgumentResult<'a, Self> {
let (args, attach_idx, value) = String::pop_from(args, attachment_index, ctx, msg).await?;
match T::from_str(&value) {
Ok(value) => Ok((args, attach_idx, Self(value))),
Err(err) => Err((Box::new(err), Some(value))),
}
}
}
11 changes: 6 additions & 5 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -173,7 +173,7 @@ For another example of subcommands, see `examples/feature_showcase/subcommands.r
Also see the [`command`] macro docs
```rust
use poise::serenity_prelude as serenity;
use poise::{StrArg, serenity_prelude as serenity};
type Data = ();
type Error = Box<dyn std::error::Error + Send + Sync>;
type Context<'a> = poise::Context<'a, Data, Error>;
Expand All @@ -190,9 +190,9 @@ type Context<'a> = poise::Context<'a, Data, Error>;
)]
async fn my_huge_ass_command(
ctx: Context<'_>,
#[description = "Consectetur"] ip_addr: std::net::IpAddr, // implements FromStr
#[description = "Amet"] user: serenity::Member, // implements ArgumentConvert
#[description = "Sit"] code_block: poise::CodeBlock, // implements PopArgument
#[description = "Amet"] user: serenity::Member,
#[description = "Consectetur"] #[rename = "ip_addr"] StrArg(ip_addr): StrArg<std::net::IpAddr>,
#[description = "Sit"] code_block: poise::CodeBlock,
#[description = "Dolor"] #[flag] my_flag: bool,
#[description = "Ipsum"] #[lazy] always_none: Option<String>,
#[description = "Lorem"] #[rest] rest: String,
Expand Down Expand Up @@ -381,6 +381,7 @@ underlying this framework, so that's what I chose.
Also, poise is a stat in Dark Souls
*/

mod argument;
pub mod builtins;
pub mod choice_parameter;
pub mod cooldown;
Expand All @@ -400,7 +401,7 @@ pub mod macros {

#[doc(no_inline)]
pub use {
choice_parameter::*, cooldown::*, dispatch::*, framework::*, macros::*, modal::*,
argument::*, choice_parameter::*, cooldown::*, dispatch::*, framework::*, macros::*, modal::*,
prefix_argument::*, reply::*, slash_argument::*, structs::*, track_edits::*,
};

Expand Down
2 changes: 1 addition & 1 deletion src/prefix_argument/argument_trait.rs
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,7 @@ impl_popargument_via_argumentconvert!(
f32, f64,
u8, u16, u32, u64,
i8, i16, i32, i64,
serenity::UserId, serenity::User,
serenity::UserId, serenity::User, serenity::Member,
serenity::MessageId, serenity::Message,
serenity::ChannelId, serenity::Channel, serenity::GuildChannel,
serenity::RoleId, serenity::Role
Expand Down

0 comments on commit 4866c24

Please sign in to comment.