mirror of
https://github.com/teloxide/teloxide.git
synced 2025-03-23 07:09:34 +01:00
Merge pull request #862 from TheAwiteb/hide-attr
`#[command(hide)]` to hide a command from the help message
This commit is contained in:
commit
34c079cb78
7 changed files with 62 additions and 13 deletions
|
@ -10,6 +10,14 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||||
|
|
||||||
- Fix `split` parser for tuple variants with len < 2 ([issue #834](https://github.com/teloxide/teloxide/issues/834))
|
- Fix `split` parser for tuple variants with len < 2 ([issue #834](https://github.com/teloxide/teloxide/issues/834))
|
||||||
|
|
||||||
|
### Added
|
||||||
|
|
||||||
|
- Now you can use `#[command(hide)]` to hide a command from the help message ([PR #862](https://github.com/teloxide/teloxide/pull/862))
|
||||||
|
|
||||||
|
### Deprecated
|
||||||
|
|
||||||
|
- `off` in `#[command(description = "off")]` is deprecated in favour of `#[command(hide)]`
|
||||||
|
|
||||||
## 0.7.1 - 2023-01-17
|
## 0.7.1 - 2023-01-17
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
|
|
|
@ -4,7 +4,7 @@ use crate::{
|
||||||
};
|
};
|
||||||
|
|
||||||
use proc_macro2::TokenStream;
|
use proc_macro2::TokenStream;
|
||||||
use quote::quote;
|
use quote::{quote, quote_spanned};
|
||||||
use syn::DeriveInput;
|
use syn::DeriveInput;
|
||||||
|
|
||||||
pub(crate) fn bot_commands_impl(input: DeriveInput) -> Result<TokenStream> {
|
pub(crate) fn bot_commands_impl(input: DeriveInput) -> Result<TokenStream> {
|
||||||
|
@ -45,7 +45,7 @@ pub(crate) fn bot_commands_impl(input: DeriveInput) -> Result<TokenStream> {
|
||||||
fn impl_commands(infos: &[Command]) -> proc_macro2::TokenStream {
|
fn impl_commands(infos: &[Command]) -> proc_macro2::TokenStream {
|
||||||
let commands = infos.iter().filter(|command| command.description_is_enabled()).map(|command| {
|
let commands = infos.iter().filter(|command| command.description_is_enabled()).map(|command| {
|
||||||
let c = command.get_prefixed_command();
|
let c = command.get_prefixed_command();
|
||||||
let d = command.description.as_deref().unwrap_or_default();
|
let d = command.description().unwrap_or_default();
|
||||||
quote! { BotCommand::new(#c,#d) }
|
quote! { BotCommand::new(#c,#d) }
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -61,11 +61,21 @@ fn impl_descriptions(infos: &[Command], global: &CommandEnum) -> proc_macro2::To
|
||||||
let command_descriptions = infos
|
let command_descriptions = infos
|
||||||
.iter()
|
.iter()
|
||||||
.filter(|command| command.description_is_enabled())
|
.filter(|command| command.description_is_enabled())
|
||||||
.map(|Command { prefix, name, description, ..}| {
|
.map(|command @ Command { prefix, name, ..}| {
|
||||||
let description = description.clone().unwrap_or_default();
|
let description = command.description().unwrap_or_default();
|
||||||
quote! { CommandDescription { prefix: #prefix, command: #name, description: #description } }
|
quote! { CommandDescription { prefix: #prefix, command: #name, description: #description } }
|
||||||
});
|
});
|
||||||
|
|
||||||
|
let warnings = infos.iter().filter_map(|command| command.deprecated_description_off_span()).map(|span| {
|
||||||
|
quote_spanned! { span =>
|
||||||
|
const _: () = {
|
||||||
|
#[deprecated(note="\n`description = \"off\"` is deprecated, use `hide` instead")]
|
||||||
|
struct Deprecated;
|
||||||
|
_ = Deprecated;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
let global_description = match global.description.as_deref() {
|
let global_description = match global.description.as_deref() {
|
||||||
Some(gd) => quote! { .global_description(#gd) },
|
Some(gd) => quote! { .global_description(#gd) },
|
||||||
None => quote! {},
|
None => quote! {},
|
||||||
|
@ -76,6 +86,8 @@ fn impl_descriptions(infos: &[Command], global: &CommandEnum) -> proc_macro2::To
|
||||||
use teloxide::utils::command::{CommandDescriptions, CommandDescription};
|
use teloxide::utils::command::{CommandDescriptions, CommandDescription};
|
||||||
use std::borrow::Cow;
|
use std::borrow::Cow;
|
||||||
|
|
||||||
|
#(#warnings)*
|
||||||
|
|
||||||
CommandDescriptions::new(&[
|
CommandDescriptions::new(&[
|
||||||
#(#command_descriptions),*
|
#(#command_descriptions),*
|
||||||
])
|
])
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
use proc_macro2::Span;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
command_attr::CommandAttrs, command_enum::CommandEnum, error::compile_error_at,
|
command_attr::CommandAttrs, command_enum::CommandEnum, error::compile_error_at,
|
||||||
fields_parse::ParserType, Result,
|
fields_parse::ParserType, Result,
|
||||||
|
@ -7,11 +9,13 @@ pub(crate) struct Command {
|
||||||
/// Prefix of this command, for example "/".
|
/// Prefix of this command, for example "/".
|
||||||
pub prefix: String,
|
pub prefix: String,
|
||||||
/// Description for the command.
|
/// Description for the command.
|
||||||
pub description: Option<String>,
|
pub description: Option<(String, Span)>,
|
||||||
/// Name of the command, with all renames already applied.
|
/// Name of the command, with all renames already applied.
|
||||||
pub name: String,
|
pub name: String,
|
||||||
/// Parser for arguments of this command.
|
/// Parser for arguments of this command.
|
||||||
pub parser: ParserType,
|
pub parser: ParserType,
|
||||||
|
/// Whether the command is hidden from the help message.
|
||||||
|
pub hidden: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Command {
|
impl Command {
|
||||||
|
@ -29,6 +33,7 @@ impl Command {
|
||||||
parser,
|
parser,
|
||||||
// FIXME: error on/do not ignore separator
|
// FIXME: error on/do not ignore separator
|
||||||
separator: _,
|
separator: _,
|
||||||
|
hide,
|
||||||
} = attrs;
|
} = attrs;
|
||||||
|
|
||||||
let name = match (rename, rename_rule) {
|
let name = match (rename, rename_rule) {
|
||||||
|
@ -44,10 +49,10 @@ impl Command {
|
||||||
};
|
};
|
||||||
|
|
||||||
let prefix = prefix.map(|(p, _)| p).unwrap_or_else(|| global_options.prefix.clone());
|
let prefix = prefix.map(|(p, _)| p).unwrap_or_else(|| global_options.prefix.clone());
|
||||||
let description = description.map(|(d, _)| d);
|
|
||||||
let parser = parser.map(|(p, _)| p).unwrap_or_else(|| global_options.parser_type.clone());
|
let parser = parser.map(|(p, _)| p).unwrap_or_else(|| global_options.parser_type.clone());
|
||||||
|
let hidden = hide.is_some();
|
||||||
|
|
||||||
Ok(Self { prefix, description, parser, name })
|
Ok(Self { prefix, description, parser, name, hidden })
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_prefixed_command(&self) -> String {
|
pub fn get_prefixed_command(&self) -> String {
|
||||||
|
@ -55,7 +60,16 @@ impl Command {
|
||||||
format!("{prefix}{name}")
|
format!("{prefix}{name}")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn description(&self) -> Option<&str> {
|
||||||
|
self.description.as_ref().map(|(d, _span)| &**d)
|
||||||
|
}
|
||||||
|
|
||||||
pub(crate) fn description_is_enabled(&self) -> bool {
|
pub(crate) fn description_is_enabled(&self) -> bool {
|
||||||
self.description != Some("off".to_owned())
|
// FIXME: remove the first, `== "off"`, check eventually
|
||||||
|
self.description() != Some("off") && !self.hidden
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn deprecated_description_off_span(&self) -> Option<Span> {
|
||||||
|
self.description.as_ref().filter(|(d, _)| d == "off").map(|&(_, span)| span)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,6 +17,7 @@ pub(crate) struct CommandAttrs {
|
||||||
pub rename: Option<(String, Span)>,
|
pub rename: Option<(String, Span)>,
|
||||||
pub parser: Option<(ParserType, Span)>,
|
pub parser: Option<(ParserType, Span)>,
|
||||||
pub separator: Option<(String, Span)>,
|
pub separator: Option<(String, Span)>,
|
||||||
|
pub hide: Option<((), Span)>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A single k/v attribute for `BotCommands` derive macro.
|
/// A single k/v attribute for `BotCommands` derive macro.
|
||||||
|
@ -41,6 +42,7 @@ enum CommandAttrKind {
|
||||||
Rename(String),
|
Rename(String),
|
||||||
ParseWith(ParserType),
|
ParseWith(ParserType),
|
||||||
Separator(String),
|
Separator(String),
|
||||||
|
Hide,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl CommandAttrs {
|
impl CommandAttrs {
|
||||||
|
@ -58,6 +60,7 @@ impl CommandAttrs {
|
||||||
rename: None,
|
rename: None,
|
||||||
parser: None,
|
parser: None,
|
||||||
separator: None,
|
separator: None,
|
||||||
|
hide: None,
|
||||||
},
|
},
|
||||||
|mut this, attr| {
|
|mut this, attr| {
|
||||||
fn insert<T>(opt: &mut Option<(T, Span)>, x: T, sp: Span) -> Result<()> {
|
fn insert<T>(opt: &mut Option<(T, Span)>, x: T, sp: Span) -> Result<()> {
|
||||||
|
@ -77,6 +80,7 @@ impl CommandAttrs {
|
||||||
Rename(r) => insert(&mut this.rename, r, attr.sp),
|
Rename(r) => insert(&mut this.rename, r, attr.sp),
|
||||||
ParseWith(p) => insert(&mut this.parser, p, attr.sp),
|
ParseWith(p) => insert(&mut this.parser, p, attr.sp),
|
||||||
Separator(s) => insert(&mut this.separator, s, attr.sp),
|
Separator(s) => insert(&mut this.separator, s, attr.sp),
|
||||||
|
Hide => insert(&mut this.hide, (), attr.sp),
|
||||||
}?;
|
}?;
|
||||||
|
|
||||||
Ok(this)
|
Ok(this)
|
||||||
|
@ -100,10 +104,11 @@ impl CommandAttr {
|
||||||
"rename" => Rename(value.expect_string()?),
|
"rename" => Rename(value.expect_string()?),
|
||||||
"parse_with" => ParseWith(ParserType::parse(value)?),
|
"parse_with" => ParseWith(ParserType::parse(value)?),
|
||||||
"separator" => Separator(value.expect_string()?),
|
"separator" => Separator(value.expect_string()?),
|
||||||
|
"hide" => Hide,
|
||||||
_ => {
|
_ => {
|
||||||
return Err(compile_error_at(
|
return Err(compile_error_at(
|
||||||
"unexpected attribute name (expected one of `prefix`, `description`, \
|
"unexpected attribute name (expected one of `prefix`, `description`, \
|
||||||
`rename`, `parse_with` and `separator`",
|
`rename`, `parse_with`, `separator` and `hide`",
|
||||||
key.span(),
|
key.span(),
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,13 +13,19 @@ pub(crate) struct CommandEnum {
|
||||||
impl CommandEnum {
|
impl CommandEnum {
|
||||||
pub fn from_attributes(attributes: &[syn::Attribute]) -> Result<Self> {
|
pub fn from_attributes(attributes: &[syn::Attribute]) -> Result<Self> {
|
||||||
let attrs = CommandAttrs::from_attributes(attributes)?;
|
let attrs = CommandAttrs::from_attributes(attributes)?;
|
||||||
let CommandAttrs { prefix, description, rename_rule, rename, parser, separator } = attrs;
|
let CommandAttrs { prefix, description, rename_rule, rename, parser, separator, hide } =
|
||||||
|
attrs;
|
||||||
|
|
||||||
if let Some((_rename, sp)) = rename {
|
if let Some((_rename, sp)) = rename {
|
||||||
return Err(compile_error_at(
|
return Err(compile_error_at(
|
||||||
"`rename` attribute can only be applied to enums *variants*",
|
"`rename` attribute can only be applied to enums *variants*",
|
||||||
sp,
|
sp,
|
||||||
));
|
));
|
||||||
|
} else if let Some((_hide, sp)) = hide {
|
||||||
|
return Err(compile_error_at(
|
||||||
|
"`hide` attribute can only be applied to enums *variants*",
|
||||||
|
sp,
|
||||||
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut parser = parser.map(|(p, _)| p).unwrap_or(ParserType::Default);
|
let mut parser = parser.map(|(p, _)| p).unwrap_or(ParserType::Default);
|
||||||
|
|
|
@ -168,14 +168,16 @@ pub use teloxide_macros::BotCommands;
|
||||||
/// `rename_rule`).
|
/// `rename_rule`).
|
||||||
///
|
///
|
||||||
/// 3. `#[command(description = "description")]`
|
/// 3. `#[command(description = "description")]`
|
||||||
/// Give your command a description. Write `"off"` for `"description"` to hide a
|
/// Give your command a description. It will be shown in the help message.
|
||||||
/// command.
|
|
||||||
///
|
///
|
||||||
/// 4. `#[command(parse_with = "parser")]`
|
/// 4. `#[command(parse_with = "parser")]`
|
||||||
/// Parse arguments of one command with a given parser. `parser` must be a
|
/// Parse arguments of one command with a given parser. `parser` must be a
|
||||||
/// function of the signature `fn(String) -> Result<Tuple, ParseError>`, where
|
/// function of the signature `fn(String) -> Result<Tuple, ParseError>`, where
|
||||||
/// `Tuple` corresponds to the variant's arguments.
|
/// `Tuple` corresponds to the variant's arguments.
|
||||||
///
|
///
|
||||||
|
/// 5. `#[command(hide)]`
|
||||||
|
/// Hide a command from the help message. It will still be parsed.
|
||||||
|
///
|
||||||
/// ## Example
|
/// ## Example
|
||||||
/// ```
|
/// ```
|
||||||
/// # #[cfg(feature = "macros")] {
|
/// # #[cfg(feature = "macros")] {
|
||||||
|
|
|
@ -232,8 +232,10 @@ fn descriptions_off() {
|
||||||
#[derive(BotCommands, Debug, PartialEq)]
|
#[derive(BotCommands, Debug, PartialEq)]
|
||||||
#[command(rename_rule = "lowercase")]
|
#[command(rename_rule = "lowercase")]
|
||||||
enum DefaultCommands {
|
enum DefaultCommands {
|
||||||
#[command(description = "off")]
|
#[command(hide)]
|
||||||
Start,
|
Start,
|
||||||
|
#[command(hide)]
|
||||||
|
Username,
|
||||||
Help,
|
Help,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Add table
Reference in a new issue