diff --git a/crates/teloxide-macros/src/bot_commands.rs b/crates/teloxide-macros/src/bot_commands.rs index 581a3611..960184f6 100644 --- a/crates/teloxide-macros/src/bot_commands.rs +++ b/crates/teloxide-macros/src/bot_commands.rs @@ -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 { @@ -45,7 +45,7 @@ pub(crate) fn bot_commands_impl(input: DeriveInput) -> Result { 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),* ]) diff --git a/crates/teloxide-macros/src/command.rs b/crates/teloxide-macros/src/command.rs index cd0bebc3..d26c85f7 100644 --- a/crates/teloxide-macros/src/command.rs +++ b/crates/teloxide-macros/src/command.rs @@ -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,7 +9,7 @@ pub(crate) struct Command { /// Prefix of this command, for example "/". pub prefix: String, /// Description for the command. - pub description: Option, + pub description: Option<(String, Span)>, /// Name of the command, with all renames already applied. pub name: String, /// Parser for arguments of this command. @@ -47,7 +49,6 @@ 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(); @@ -59,8 +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 { // FIXME: remove the first, `== "off"`, check eventually - self.description != Some("off".to_owned()) && !self.hidden + self.description() != Some("off") && !self.hidden + } + + pub(crate) fn deprecated_description_off_span(&self) -> Option { + self.description.as_ref().filter(|(d, _)| d == "off").map(|&(_, span)| span) } }