Skip to content

Commit 53f2e5a

Browse files
committed
Add poise::StrArg for FromStr arguments
1 parent 2ac1e69 commit 53f2e5a

File tree

4 files changed

+69
-7
lines changed

4 files changed

+69
-7
lines changed

macros/src/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,7 @@ for example for command-specific help (i.e. `~help command_name`). Escape newlin
8888
SlashContext, which contain a variety of context data each. Context provides some utility methods to
8989
access data present in both PrefixContext and SlashContext, like `author()` or `created_at()`.
9090
91-
All following parameters are inputs to the command. You can use all types that implement `poise::PopArgument`, `serenity::ArgumentConvert` or `std::str::FromStr`.
91+
All following parameters are inputs to the command. You can use all types that implement `PopArgument` (for prefix) or `SlashArgument` (for slash).
9292
You can also wrap types in `Option` or `Vec` to make them optional or variadic. In addition, there
9393
are multiple attributes you can use on parameters:
9494

src/argument.rs

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
use std::str::FromStr;
2+
3+
use crate::{
4+
serenity_prelude as serenity, PopArgument, PopArgumentResult, SlashArgError, SlashArgument,
5+
};
6+
7+
/// A wrapper for `T` to implement [`SlashArgument`] and [`PopArgument`] via [`FromStr`].
8+
///
9+
/// This is useful if you need to take an argument via a string, but immediately convert it via [`FromStr`].
10+
pub struct StrArg<T>(pub T);
11+
12+
#[async_trait::async_trait]
13+
impl<T> SlashArgument for StrArg<T>
14+
where
15+
T: FromStr,
16+
T::Err: std::error::Error + Send + Sync + 'static,
17+
{
18+
fn create(builder: serenity::CreateCommandOption) -> serenity::CreateCommandOption {
19+
builder.kind(serenity::CommandOptionType::String)
20+
}
21+
22+
async fn extract(
23+
_: &serenity::Context,
24+
_: &serenity::CommandInteraction,
25+
value: &serenity::ResolvedValue<'_>,
26+
) -> Result<Self, SlashArgError> {
27+
let serenity::ResolvedValue::String(value) = value else {
28+
return Err(SlashArgError::new_command_structure_mismatch(
29+
"expected a String",
30+
));
31+
};
32+
33+
match T::from_str(value) {
34+
Ok(value) => Ok(Self(value)),
35+
Err(err) => Err(SlashArgError::Parse {
36+
error: err.into(),
37+
input: String::from(*value),
38+
}),
39+
}
40+
}
41+
}
42+
43+
#[async_trait::async_trait]
44+
impl<'a, T> PopArgument<'a> for StrArg<T>
45+
where
46+
T: FromStr,
47+
T::Err: std::error::Error + Send + Sync + 'static,
48+
{
49+
async fn pop_from(
50+
args: &'a str,
51+
attachment_index: usize,
52+
ctx: &serenity::Context,
53+
msg: &serenity::Message,
54+
) -> PopArgumentResult<'a, Self> {
55+
let (args, attach_idx, value) = String::pop_from(args, attachment_index, ctx, msg).await?;
56+
match T::from_str(&value) {
57+
Ok(value) => Ok((args, attach_idx, Self(value))),
58+
Err(err) => Err((Box::new(err), Some(value))),
59+
}
60+
}
61+
}

src/lib.rs

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -173,7 +173,7 @@ For another example of subcommands, see `examples/feature_showcase/subcommands.r
173173
Also see the [`command`] macro docs
174174
175175
```rust
176-
use poise::serenity_prelude as serenity;
176+
use poise::{StrArg, serenity_prelude as serenity};
177177
type Data = ();
178178
type Error = Box<dyn std::error::Error + Send + Sync>;
179179
type Context<'a> = poise::Context<'a, Data, Error>;
@@ -190,9 +190,9 @@ type Context<'a> = poise::Context<'a, Data, Error>;
190190
)]
191191
async fn my_huge_ass_command(
192192
ctx: Context<'_>,
193-
#[description = "Consectetur"] ip_addr: std::net::IpAddr, // implements FromStr
194-
#[description = "Amet"] user: serenity::Member, // implements ArgumentConvert
195-
#[description = "Sit"] code_block: poise::CodeBlock, // implements PopArgument
193+
#[description = "Amet"] user: serenity::Member,
194+
#[description = "Consectetur"] #[rename = "ip_addr"] StrArg(ip_addr): StrArg<std::net::IpAddr>,
195+
#[description = "Sit"] code_block: poise::CodeBlock,
196196
#[description = "Dolor"] #[flag] my_flag: bool,
197197
#[description = "Ipsum"] #[lazy] always_none: Option<String>,
198198
#[description = "Lorem"] #[rest] rest: String,
@@ -381,6 +381,7 @@ underlying this framework, so that's what I chose.
381381
Also, poise is a stat in Dark Souls
382382
*/
383383

384+
mod argument;
384385
pub mod builtins;
385386
pub mod choice_parameter;
386387
pub mod cooldown;
@@ -400,7 +401,7 @@ pub mod macros {
400401

401402
#[doc(no_inline)]
402403
pub use {
403-
choice_parameter::*, cooldown::*, dispatch::*, framework::*, macros::*, modal::*,
404+
argument::*, choice_parameter::*, cooldown::*, dispatch::*, framework::*, macros::*, modal::*,
404405
prefix_argument::*, reply::*, slash_argument::*, structs::*, track_edits::*,
405406
};
406407

src/prefix_argument/argument_trait.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -127,7 +127,7 @@ impl_popargument_via_argumentconvert!(
127127
f32, f64,
128128
u8, u16, u32, u64,
129129
i8, i16, i32, i64,
130-
serenity::UserId, serenity::User,
130+
serenity::UserId, serenity::User, serenity::Member,
131131
serenity::MessageId, serenity::Message,
132132
serenity::ChannelId, serenity::Channel, serenity::GuildChannel,
133133
serenity::RoleId, serenity::Role

0 commit comments

Comments
 (0)