mirror of
https://github.com/teloxide/teloxide.git
synced 2024-12-22 14:35:36 +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))
|
||||
|
||||
### 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
|
||||
|
||||
### Fixed
|
||||
|
|
|
@ -4,7 +4,7 @@ use crate::{
|
|||
};
|
||||
|
||||
use proc_macro2::TokenStream;
|
||||
use quote::quote;
|
||||
use quote::{quote, quote_spanned};
|
||||
use syn::DeriveInput;
|
||||
|
||||
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 {
|
||||
let commands = infos.iter().filter(|command| command.description_is_enabled()).map(|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) }
|
||||
});
|
||||
|
||||
|
@ -61,11 +61,21 @@ fn impl_descriptions(infos: &[Command], global: &CommandEnum) -> proc_macro2::To
|
|||
let command_descriptions = infos
|
||||
.iter()
|
||||
.filter(|command| command.description_is_enabled())
|
||||
.map(|Command { prefix, name, description, ..}| {
|
||||
let description = description.clone().unwrap_or_default();
|
||||
.map(|command @ Command { prefix, name, ..}| {
|
||||
let description = command.description().unwrap_or_default();
|
||||
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() {
|
||||
Some(gd) => quote! { .global_description(#gd) },
|
||||
None => quote! {},
|
||||
|
@ -76,6 +86,8 @@ fn impl_descriptions(infos: &[Command], global: &CommandEnum) -> proc_macro2::To
|
|||
use teloxide::utils::command::{CommandDescriptions, CommandDescription};
|
||||
use std::borrow::Cow;
|
||||
|
||||
#(#warnings)*
|
||||
|
||||
CommandDescriptions::new(&[
|
||||
#(#command_descriptions),*
|
||||
])
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
use proc_macro2::Span;
|
||||
|
||||
use crate::{
|
||||
command_attr::CommandAttrs, command_enum::CommandEnum, error::compile_error_at,
|
||||
fields_parse::ParserType, Result,
|
||||
|
@ -7,11 +9,13 @@ pub(crate) struct Command {
|
|||
/// Prefix of this command, for example "/".
|
||||
pub prefix: String,
|
||||
/// Description for the command.
|
||||
pub description: Option<String>,
|
||||
pub description: Option<(String, Span)>,
|
||||
/// Name of the command, with all renames already applied.
|
||||
pub name: String,
|
||||
/// Parser for arguments of this command.
|
||||
pub parser: ParserType,
|
||||
/// Whether the command is hidden from the help message.
|
||||
pub hidden: bool,
|
||||
}
|
||||
|
||||
impl Command {
|
||||
|
@ -29,6 +33,7 @@ impl Command {
|
|||
parser,
|
||||
// FIXME: error on/do not ignore separator
|
||||
separator: _,
|
||||
hide,
|
||||
} = attrs;
|
||||
|
||||
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 description = description.map(|(d, _)| d);
|
||||
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 {
|
||||
|
@ -55,7 +60,16 @@ impl Command {
|
|||
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 {
|
||||
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 parser: Option<(ParserType, Span)>,
|
||||
pub separator: Option<(String, Span)>,
|
||||
pub hide: Option<((), Span)>,
|
||||
}
|
||||
|
||||
/// A single k/v attribute for `BotCommands` derive macro.
|
||||
|
@ -41,6 +42,7 @@ enum CommandAttrKind {
|
|||
Rename(String),
|
||||
ParseWith(ParserType),
|
||||
Separator(String),
|
||||
Hide,
|
||||
}
|
||||
|
||||
impl CommandAttrs {
|
||||
|
@ -58,6 +60,7 @@ impl CommandAttrs {
|
|||
rename: None,
|
||||
parser: None,
|
||||
separator: None,
|
||||
hide: None,
|
||||
},
|
||||
|mut this, attr| {
|
||||
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),
|
||||
ParseWith(p) => insert(&mut this.parser, p, attr.sp),
|
||||
Separator(s) => insert(&mut this.separator, s, attr.sp),
|
||||
Hide => insert(&mut this.hide, (), attr.sp),
|
||||
}?;
|
||||
|
||||
Ok(this)
|
||||
|
@ -100,10 +104,11 @@ impl CommandAttr {
|
|||
"rename" => Rename(value.expect_string()?),
|
||||
"parse_with" => ParseWith(ParserType::parse(value)?),
|
||||
"separator" => Separator(value.expect_string()?),
|
||||
"hide" => Hide,
|
||||
_ => {
|
||||
return Err(compile_error_at(
|
||||
"unexpected attribute name (expected one of `prefix`, `description`, \
|
||||
`rename`, `parse_with` and `separator`",
|
||||
`rename`, `parse_with`, `separator` and `hide`",
|
||||
key.span(),
|
||||
))
|
||||
}
|
||||
|
|
|
@ -13,13 +13,19 @@ 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 } = attrs;
|
||||
let CommandAttrs { prefix, description, rename_rule, rename, parser, separator, hide } =
|
||||
attrs;
|
||||
|
||||
if let Some((_rename, sp)) = rename {
|
||||
return Err(compile_error_at(
|
||||
"`rename` attribute can only be applied to enums *variants*",
|
||||
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);
|
||||
|
|
|
@ -168,14 +168,16 @@ pub use teloxide_macros::BotCommands;
|
|||
/// `rename_rule`).
|
||||
///
|
||||
/// 3. `#[command(description = "description")]`
|
||||
/// Give your command a description. Write `"off"` for `"description"` to hide a
|
||||
/// command.
|
||||
/// Give your command a description. It will be shown in the help message.
|
||||
///
|
||||
/// 4. `#[command(parse_with = "parser")]`
|
||||
/// Parse arguments of one command with a given parser. `parser` must be a
|
||||
/// function of the signature `fn(String) -> Result<Tuple, ParseError>`, where
|
||||
/// `Tuple` corresponds to the variant's arguments.
|
||||
///
|
||||
/// 5. `#[command(hide)]`
|
||||
/// Hide a command from the help message. It will still be parsed.
|
||||
///
|
||||
/// ## Example
|
||||
/// ```
|
||||
/// # #[cfg(feature = "macros")] {
|
||||
|
|
|
@ -232,8 +232,10 @@ fn descriptions_off() {
|
|||
#[derive(BotCommands, Debug, PartialEq)]
|
||||
#[command(rename_rule = "lowercase")]
|
||||
enum DefaultCommands {
|
||||
#[command(description = "off")]
|
||||
#[command(hide)]
|
||||
Start,
|
||||
#[command(hide)]
|
||||
Username,
|
||||
Help,
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue