mirror of
https://github.com/teloxide/teloxide.git
synced 2024-12-22 14:35:36 +01:00
Refactor attributes AGAIN
This commit is contained in:
parent
26eba3eb14
commit
ff08854ca9
5 changed files with 80 additions and 71 deletions
|
@ -1,6 +1,6 @@
|
|||
use crate::{
|
||||
command::Command, command_attr::CommandAttrs, command_enum::CommandEnum,
|
||||
compile_error, fields_parse::impl_parse_args, unzip::Unzip, Result,
|
||||
command::Command, command_enum::CommandEnum, compile_error,
|
||||
fields_parse::impl_parse_args, unzip::Unzip, Result,
|
||||
};
|
||||
|
||||
use proc_macro2::TokenStream;
|
||||
|
@ -9,22 +9,23 @@ use syn::DeriveInput;
|
|||
|
||||
pub(crate) fn bot_commands_impl(input: DeriveInput) -> Result<TokenStream> {
|
||||
let data_enum = get_enum_data(&input)?;
|
||||
let enum_attrs = CommandAttrs::from_attributes(&input.attrs)?;
|
||||
let command_enum = CommandEnum::try_from(enum_attrs)?;
|
||||
let command_enum = CommandEnum::from_attributes(&input.attrs)?;
|
||||
|
||||
let Unzip(var_init, var_info) = data_enum
|
||||
.variants
|
||||
.iter()
|
||||
.map(|variant| {
|
||||
let attrs = CommandAttrs::from_attributes(&variant.attrs)?;
|
||||
let command = Command::try_from(attrs, &variant.ident.to_string())?;
|
||||
let command = Command::new(
|
||||
&variant.ident.to_string(),
|
||||
&variant.attrs,
|
||||
&command_enum,
|
||||
)?;
|
||||
|
||||
let variant_name = &variant.ident;
|
||||
let self_variant = quote! { Self::#variant_name };
|
||||
|
||||
let parser =
|
||||
command.parser.as_ref().unwrap_or(&command_enum.parser_type);
|
||||
let parse = impl_parse_args(&variant.fields, self_variant, parser);
|
||||
let parse =
|
||||
impl_parse_args(&variant.fields, self_variant, &command.parser);
|
||||
|
||||
Ok((parse, command))
|
||||
})
|
||||
|
@ -32,8 +33,8 @@ 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, &command_enum, &var_init);
|
||||
let fn_commands = impl_commands(&var_info, &command_enum);
|
||||
let fn_parse = impl_parse(&var_info, &var_init);
|
||||
let fn_commands = impl_commands(&var_info);
|
||||
|
||||
let trait_impl = quote! {
|
||||
impl teloxide::utils::command::BotCommands for #type_name {
|
||||
|
@ -46,15 +47,12 @@ pub(crate) fn bot_commands_impl(input: DeriveInput) -> Result<TokenStream> {
|
|||
Ok(trait_impl)
|
||||
}
|
||||
|
||||
fn impl_commands(
|
||||
infos: &[Command],
|
||||
global: &CommandEnum,
|
||||
) -> proc_macro2::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_matched_value(global);
|
||||
let c = command.get_prefixed_command();
|
||||
let d = command.description.as_deref().unwrap_or_default();
|
||||
quote! { BotCommand::new(#c,#d) }
|
||||
});
|
||||
|
@ -74,10 +72,9 @@ fn impl_descriptions(
|
|||
let command_descriptions = infos
|
||||
.iter()
|
||||
.filter(|command| command.description_is_enabled())
|
||||
.map(|c| {
|
||||
let (prefix, command) = c.get_matched_value2(global);
|
||||
let description = c.description.clone().unwrap_or_default();
|
||||
quote! { CommandDescription { prefix: #prefix, command: #command, description: #description } }
|
||||
.map(|Command { prefix, name, description, ..}| {
|
||||
let description = description.clone().unwrap_or_default();
|
||||
quote! { CommandDescription { prefix: #prefix, command: #name, description: #description } }
|
||||
});
|
||||
|
||||
let global_description = match global.description.as_deref() {
|
||||
|
@ -100,10 +97,9 @@ fn impl_descriptions(
|
|||
|
||||
fn impl_parse(
|
||||
infos: &[Command],
|
||||
global: &CommandEnum,
|
||||
variants_initialization: &[proc_macro2::TokenStream],
|
||||
) -> proc_macro2::TokenStream {
|
||||
let matching_values = infos.iter().map(|c| c.get_matched_value(global));
|
||||
let matching_values = infos.iter().map(|c| c.get_prefixed_command());
|
||||
|
||||
quote! {
|
||||
fn parse<N>(s: &str, bot_name: N) -> Result<Self, teloxide::utils::command::ParseError>
|
||||
|
|
|
@ -1,55 +1,54 @@
|
|||
use crate::{
|
||||
command_attr::CommandAttrs, command_enum::CommandEnum,
|
||||
fields_parse::ParserType, rename_rules::RenameRule, Result,
|
||||
fields_parse::ParserType, Result,
|
||||
};
|
||||
|
||||
pub(crate) struct Command {
|
||||
pub prefix: Option<String>,
|
||||
/// Prefix of this command, for example "/".
|
||||
pub prefix: String,
|
||||
/// Description for the command.
|
||||
pub description: Option<String>,
|
||||
pub parser: Option<ParserType>,
|
||||
/// Name of the command, with all renames already applied.
|
||||
pub name: String,
|
||||
/// Parser for arguments of this command.
|
||||
pub parser: ParserType,
|
||||
}
|
||||
|
||||
impl Command {
|
||||
pub fn try_from(attrs: CommandAttrs, name: &str) -> Result<Self> {
|
||||
pub fn new(
|
||||
name: &str,
|
||||
attributes: &[syn::Attribute],
|
||||
global_options: &CommandEnum,
|
||||
) -> Result<Self> {
|
||||
let attrs = CommandAttrs::from_attributes(attributes)?;
|
||||
let CommandAttrs {
|
||||
prefix,
|
||||
description,
|
||||
rename_rule,
|
||||
parser,
|
||||
// FIXME: error on/do not ignore separator
|
||||
separator: _,
|
||||
} = attrs;
|
||||
|
||||
let name = rename_rule.unwrap_or(RenameRule::Identity).apply(name);
|
||||
let name = rename_rule
|
||||
.map(|(rr, _)| rr)
|
||||
.unwrap_or(global_options.rename_rule)
|
||||
.apply(name);
|
||||
|
||||
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());
|
||||
|
||||
Ok(Self { prefix, description, parser, name })
|
||||
}
|
||||
|
||||
pub fn get_matched_value(&self, global_parameters: &CommandEnum) -> String {
|
||||
let prefix = if let Some(prefix) = &self.prefix {
|
||||
prefix
|
||||
} else if let Some(prefix) = &global_parameters.prefix {
|
||||
prefix
|
||||
} else {
|
||||
"/"
|
||||
};
|
||||
|
||||
String::from(prefix) + &global_parameters.rename_rule.apply(&self.name)
|
||||
}
|
||||
|
||||
pub fn get_matched_value2(
|
||||
&self,
|
||||
global_parameters: &CommandEnum,
|
||||
) -> (String, String) {
|
||||
let prefix = if let Some(prefix) = &self.prefix {
|
||||
prefix
|
||||
} else if let Some(prefix) = &global_parameters.prefix {
|
||||
prefix
|
||||
} else {
|
||||
"/"
|
||||
};
|
||||
|
||||
(String::from(prefix), global_parameters.rename_rule.apply(&self.name))
|
||||
pub fn get_prefixed_command(&self) -> String {
|
||||
let Self { prefix, name, .. } = self;
|
||||
format!("{prefix}{name}")
|
||||
}
|
||||
|
||||
pub(crate) fn description_is_enabled(&self) -> bool {
|
||||
|
|
|
@ -9,22 +9,31 @@ use crate::{
|
|||
use proc_macro2::Span;
|
||||
use syn::Attribute;
|
||||
|
||||
/// Attributes for `BotCommands` derive macro.
|
||||
/// All attributes that can be used for `derive(BotCommands)`
|
||||
pub(crate) struct CommandAttrs {
|
||||
pub prefix: Option<String>,
|
||||
pub description: Option<String>,
|
||||
pub rename_rule: Option<RenameRule>,
|
||||
pub parser: Option<ParserType>,
|
||||
pub separator: Option<String>,
|
||||
pub prefix: Option<(String, Span)>,
|
||||
pub description: Option<(String, Span)>,
|
||||
pub rename_rule: Option<(RenameRule, Span)>,
|
||||
pub parser: Option<(ParserType, Span)>,
|
||||
pub separator: Option<(String, Span)>,
|
||||
}
|
||||
|
||||
/// An attribute for `BotCommands` derive macro.
|
||||
pub(crate) struct CommandAttr {
|
||||
/// A single k/v attribute for `BotCommands` derive macro.
|
||||
///
|
||||
/// For example:
|
||||
/// ```text
|
||||
/// #[command(prefix = "!", rename_rule = "snake_case")]
|
||||
/// /^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^---- CommandAttr { kind: RenameRule(SnakeCase) }
|
||||
/// |
|
||||
/// CommandAttr { kind: Prefix("!") }
|
||||
/// ```
|
||||
struct CommandAttr {
|
||||
kind: CommandAttrKind,
|
||||
sp: Span,
|
||||
}
|
||||
|
||||
pub(crate) enum CommandAttrKind {
|
||||
/// Kind of [`CommandAttr`].
|
||||
enum CommandAttrKind {
|
||||
Prefix(String),
|
||||
Description(String),
|
||||
RenameRule(RenameRule),
|
||||
|
@ -49,13 +58,13 @@ impl CommandAttrs {
|
|||
},
|
||||
|mut this, attr| {
|
||||
fn insert<T>(
|
||||
opt: &mut Option<T>,
|
||||
opt: &mut Option<(T, Span)>,
|
||||
x: T,
|
||||
sp: Span,
|
||||
) -> Result<()> {
|
||||
match opt {
|
||||
slot @ None => {
|
||||
*slot = Some(x);
|
||||
*slot = Some((x, sp));
|
||||
Ok(())
|
||||
}
|
||||
Some(_) => {
|
||||
|
|
|
@ -5,14 +5,15 @@ use crate::{
|
|||
|
||||
#[derive(Debug)]
|
||||
pub(crate) struct CommandEnum {
|
||||
pub prefix: Option<String>,
|
||||
pub prefix: String,
|
||||
pub description: Option<String>,
|
||||
pub rename_rule: RenameRule,
|
||||
pub parser_type: ParserType,
|
||||
}
|
||||
|
||||
impl CommandEnum {
|
||||
pub fn try_from(attrs: CommandAttrs) -> Result<Self> {
|
||||
pub fn from_attributes(attributes: &[syn::Attribute]) -> Result<Self> {
|
||||
let attrs = CommandAttrs::from_attributes(attributes)?;
|
||||
let CommandAttrs {
|
||||
prefix,
|
||||
description,
|
||||
|
@ -20,18 +21,22 @@ impl CommandEnum {
|
|||
parser,
|
||||
separator,
|
||||
} = attrs;
|
||||
let mut parser = parser.unwrap_or(ParserType::Default);
|
||||
|
||||
let mut parser = parser.map(|(p, _)| p).unwrap_or(ParserType::Default);
|
||||
|
||||
// FIXME: Error on unused separator
|
||||
if let (ParserType::Split { separator }, Some(s)) =
|
||||
if let (ParserType::Split { separator }, Some((s, _))) =
|
||||
(&mut parser, &separator)
|
||||
{
|
||||
*separator = Some(s.clone())
|
||||
}
|
||||
|
||||
Ok(Self {
|
||||
prefix,
|
||||
description,
|
||||
rename_rule: rename_rule.unwrap_or(RenameRule::Identity),
|
||||
prefix: prefix.map(|(p, _)| p).unwrap_or_else(|| "/".to_owned()),
|
||||
description: description.map(|(d, _)| d),
|
||||
rename_rule: rename_rule
|
||||
.map(|(rr, _)| rr)
|
||||
.unwrap_or(RenameRule::Identity),
|
||||
parser_type: parser,
|
||||
})
|
||||
}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
use quote::quote;
|
||||
use syn::{Fields, FieldsNamed, FieldsUnnamed, Type};
|
||||
|
||||
#[derive(Debug)]
|
||||
#[derive(Debug, Clone)]
|
||||
pub(crate) enum ParserType {
|
||||
Default,
|
||||
Split { separator: Option<String> },
|
||||
|
|
Loading…
Reference in a new issue