mirror of
https://github.com/teloxide/teloxide.git
synced 2024-12-22 14:35:36 +01:00
Implemend command_separator attr to split command and args
Signed-off-by: Roman Proskuryakov <r.proskuryakoff@gmail.com>
This commit is contained in:
parent
c7feb38995
commit
835f31be80
6 changed files with 82 additions and 4 deletions
|
@ -28,7 +28,7 @@ pub(crate) fn bot_commands_impl(input: DeriveInput) -> Result<TokenStream> {
|
|||
|
||||
let type_name = &input.ident;
|
||||
let fn_descriptions = impl_descriptions(&var_info, &command_enum);
|
||||
let fn_parse = impl_parse(&var_info, &var_init);
|
||||
let fn_parse = impl_parse(&var_info, &var_init, &command_enum.command_separator);
|
||||
let fn_commands = impl_commands(&var_info);
|
||||
|
||||
let trait_impl = quote! {
|
||||
|
@ -99,6 +99,7 @@ fn impl_descriptions(infos: &[Command], global: &CommandEnum) -> proc_macro2::To
|
|||
fn impl_parse(
|
||||
infos: &[Command],
|
||||
variants_initialization: &[proc_macro2::TokenStream],
|
||||
command_separator: &str,
|
||||
) -> proc_macro2::TokenStream {
|
||||
let matching_values = infos.iter().map(|c| c.get_prefixed_command());
|
||||
|
||||
|
@ -110,7 +111,7 @@ fn impl_parse(
|
|||
|
||||
// 2 is used to only split once (=> in two parts),
|
||||
// we only need to split the command and the rest of arguments.
|
||||
let mut words = s.splitn(2, ' ');
|
||||
let mut words = s.splitn(2, #command_separator);
|
||||
|
||||
// Unwrap: split iterators always have at least one item
|
||||
let mut full_command = words.next().unwrap().split('@');
|
||||
|
|
|
@ -34,6 +34,8 @@ impl Command {
|
|||
parser,
|
||||
// FIXME: error on/do not ignore separator
|
||||
separator: _,
|
||||
// FIXME: error on/do not ignore command separator
|
||||
command_separator: _,
|
||||
hide,
|
||||
} = attrs;
|
||||
|
||||
|
|
|
@ -22,6 +22,7 @@ pub(crate) struct CommandAttrs {
|
|||
pub rename: Option<(String, Span)>,
|
||||
pub parser: Option<(ParserType, Span)>,
|
||||
pub separator: Option<(String, Span)>,
|
||||
pub command_separator: Option<(String, Span)>,
|
||||
pub hide: Option<((), Span)>,
|
||||
}
|
||||
|
||||
|
@ -48,6 +49,7 @@ enum CommandAttrKind {
|
|||
Rename(String),
|
||||
ParseWith(ParserType),
|
||||
Separator(String),
|
||||
CommandSeparator(String),
|
||||
Hide,
|
||||
}
|
||||
|
||||
|
@ -66,6 +68,7 @@ impl CommandAttrs {
|
|||
rename: None,
|
||||
parser: None,
|
||||
separator: None,
|
||||
command_separator: None,
|
||||
hide: None,
|
||||
},
|
||||
|mut this, attr| {
|
||||
|
@ -110,6 +113,7 @@ impl CommandAttrs {
|
|||
Rename(r) => insert(&mut this.rename, r, attr.sp),
|
||||
ParseWith(p) => insert(&mut this.parser, p, attr.sp),
|
||||
Separator(s) => insert(&mut this.separator, s, attr.sp),
|
||||
CommandSeparator(s) => insert(&mut this.command_separator, s, attr.sp),
|
||||
Hide => insert(&mut this.hide, (), attr.sp),
|
||||
}?;
|
||||
|
||||
|
@ -165,6 +169,7 @@ impl CommandAttr {
|
|||
"rename" => Rename(value.expect_string()?),
|
||||
"parse_with" => ParseWith(ParserType::parse(value)?),
|
||||
"separator" => Separator(value.expect_string()?),
|
||||
"command_separator" => CommandSeparator(value.expect_string()?),
|
||||
"hide" => value.expect_none("hide").map(|_| Hide)?,
|
||||
_ => {
|
||||
return Err(compile_error_at(
|
||||
|
|
|
@ -7,6 +7,7 @@ pub(crate) struct CommandEnum {
|
|||
pub prefix: String,
|
||||
/// The bool is true if the description contains a doc comment
|
||||
pub description: Option<(String, bool)>,
|
||||
pub command_separator: String,
|
||||
pub rename_rule: RenameRule,
|
||||
pub parser_type: ParserType,
|
||||
}
|
||||
|
@ -14,8 +15,16 @@ pub(crate) struct CommandEnum {
|
|||
impl CommandEnum {
|
||||
pub fn from_attributes(attributes: &[syn::Attribute]) -> Result<Self> {
|
||||
let attrs = CommandAttrs::from_attributes(attributes)?;
|
||||
let CommandAttrs { prefix, description, rename_rule, rename, parser, separator, hide } =
|
||||
attrs;
|
||||
let CommandAttrs {
|
||||
prefix,
|
||||
description,
|
||||
rename_rule,
|
||||
rename,
|
||||
parser,
|
||||
separator,
|
||||
command_separator,
|
||||
hide,
|
||||
} = attrs;
|
||||
|
||||
if let Some((_rename, sp)) = rename {
|
||||
return Err(compile_error_at(
|
||||
|
@ -39,6 +48,9 @@ impl CommandEnum {
|
|||
Ok(Self {
|
||||
prefix: prefix.map(|(p, _)| p).unwrap_or_else(|| "/".to_owned()),
|
||||
description: description.map(|(d, is_doc, _)| (d, is_doc)),
|
||||
command_separator: command_separator
|
||||
.map(|(s, _)| s)
|
||||
.unwrap_or_else(|| String::from(" ")),
|
||||
rename_rule: rename_rule.map(|(rr, _)| rr).unwrap_or(RenameRule::Identity),
|
||||
parser_type: parser,
|
||||
})
|
||||
|
|
|
@ -155,6 +155,30 @@ pub use teloxide_macros::BotCommands;
|
|||
/// # }
|
||||
/// ```
|
||||
///
|
||||
/// 6. `#[command(command_separator = "sep")]`
|
||||
/// Specify separator between command and args. Default is a space character.
|
||||
///
|
||||
/// ## Example
|
||||
/// ```
|
||||
/// # #[cfg(feature = "macros")] {
|
||||
/// use teloxide::utils::command::BotCommands;
|
||||
///
|
||||
/// #[derive(BotCommands, PartialEq, Debug)]
|
||||
/// #[command(
|
||||
/// rename_rule = "lowercase",
|
||||
/// parse_with = "split",
|
||||
/// separator = "_",
|
||||
/// command_separator = "_"
|
||||
/// )]
|
||||
/// enum Command {
|
||||
/// Nums(u8, u16, i32),
|
||||
/// }
|
||||
///
|
||||
/// let command = Command::parse("/nums_1_32_5", "").unwrap();
|
||||
/// assert_eq!(command, Command::Nums(1, 32, 5));
|
||||
/// # }
|
||||
/// ```
|
||||
///
|
||||
/// # Variant attributes
|
||||
/// All variant attributes override the corresponding `enum` attributes.
|
||||
///
|
||||
|
|
|
@ -166,6 +166,40 @@ fn parse_with_split4() {
|
|||
assert_eq!(DefaultCommands::Start(), DefaultCommands::parse("/start", "").unwrap(),);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(feature = "macros")]
|
||||
fn parse_with_command_separator1() {
|
||||
#[derive(BotCommands, Debug, PartialEq)]
|
||||
#[command(rename_rule = "lowercase")]
|
||||
#[command(parse_with = "split", separator = "|", command_separator = "_")]
|
||||
enum DefaultCommands {
|
||||
Start(u8, String),
|
||||
Help,
|
||||
}
|
||||
|
||||
assert_eq!(
|
||||
DefaultCommands::Start(10, "hello".to_string()),
|
||||
DefaultCommands::parse("/start_10|hello", "").unwrap()
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(feature = "macros")]
|
||||
fn parse_with_command_separator2() {
|
||||
#[derive(BotCommands, Debug, PartialEq)]
|
||||
#[command(rename_rule = "lowercase")]
|
||||
#[command(parse_with = "split", separator = "_", command_separator = "_")]
|
||||
enum DefaultCommands {
|
||||
Start(u8, String),
|
||||
Help,
|
||||
}
|
||||
|
||||
assert_eq!(
|
||||
DefaultCommands::Start(10, "hello".to_string()),
|
||||
DefaultCommands::parse("/start_10_hello", "").unwrap()
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(feature = "macros")]
|
||||
fn parse_custom_parser() {
|
||||
|
|
Loading…
Reference in a new issue