Refactor attributes AGAIN

This commit is contained in:
Maybe Waffle 2022-10-02 18:03:10 +04:00
parent 26eba3eb14
commit ff08854ca9
5 changed files with 80 additions and 71 deletions

View file

@ -1,6 +1,6 @@
use crate::{ use crate::{
command::Command, command_attr::CommandAttrs, command_enum::CommandEnum, command::Command, command_enum::CommandEnum, compile_error,
compile_error, fields_parse::impl_parse_args, unzip::Unzip, Result, fields_parse::impl_parse_args, unzip::Unzip, Result,
}; };
use proc_macro2::TokenStream; use proc_macro2::TokenStream;
@ -9,22 +9,23 @@ use syn::DeriveInput;
pub(crate) fn bot_commands_impl(input: DeriveInput) -> Result<TokenStream> { pub(crate) fn bot_commands_impl(input: DeriveInput) -> Result<TokenStream> {
let data_enum = get_enum_data(&input)?; let data_enum = get_enum_data(&input)?;
let enum_attrs = CommandAttrs::from_attributes(&input.attrs)?; let command_enum = CommandEnum::from_attributes(&input.attrs)?;
let command_enum = CommandEnum::try_from(enum_attrs)?;
let Unzip(var_init, var_info) = data_enum let Unzip(var_init, var_info) = data_enum
.variants .variants
.iter() .iter()
.map(|variant| { .map(|variant| {
let attrs = CommandAttrs::from_attributes(&variant.attrs)?; let command = Command::new(
let command = Command::try_from(attrs, &variant.ident.to_string())?; &variant.ident.to_string(),
&variant.attrs,
&command_enum,
)?;
let variant_name = &variant.ident; let variant_name = &variant.ident;
let self_variant = quote! { Self::#variant_name }; let self_variant = quote! { Self::#variant_name };
let parser = let parse =
command.parser.as_ref().unwrap_or(&command_enum.parser_type); impl_parse_args(&variant.fields, self_variant, &command.parser);
let parse = impl_parse_args(&variant.fields, self_variant, parser);
Ok((parse, command)) Ok((parse, command))
}) })
@ -32,8 +33,8 @@ pub(crate) fn bot_commands_impl(input: DeriveInput) -> Result<TokenStream> {
let type_name = &input.ident; let type_name = &input.ident;
let fn_descriptions = impl_descriptions(&var_info, &command_enum); let fn_descriptions = impl_descriptions(&var_info, &command_enum);
let fn_parse = impl_parse(&var_info, &command_enum, &var_init); let fn_parse = impl_parse(&var_info, &var_init);
let fn_commands = impl_commands(&var_info, &command_enum); let fn_commands = impl_commands(&var_info);
let trait_impl = quote! { let trait_impl = quote! {
impl teloxide::utils::command::BotCommands for #type_name { 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) Ok(trait_impl)
} }
fn impl_commands( fn impl_commands(infos: &[Command]) -> proc_macro2::TokenStream {
infos: &[Command],
global: &CommandEnum,
) -> proc_macro2::TokenStream {
let commands = infos let commands = infos
.iter() .iter()
.filter(|command| command.description_is_enabled()) .filter(|command| command.description_is_enabled())
.map(|command| { .map(|command| {
let c = command.get_matched_value(global); let c = command.get_prefixed_command();
let d = command.description.as_deref().unwrap_or_default(); let d = command.description.as_deref().unwrap_or_default();
quote! { BotCommand::new(#c,#d) } quote! { BotCommand::new(#c,#d) }
}); });
@ -74,10 +72,9 @@ fn impl_descriptions(
let command_descriptions = infos let command_descriptions = infos
.iter() .iter()
.filter(|command| command.description_is_enabled()) .filter(|command| command.description_is_enabled())
.map(|c| { .map(|Command { prefix, name, description, ..}| {
let (prefix, command) = c.get_matched_value2(global); let description = description.clone().unwrap_or_default();
let description = c.description.clone().unwrap_or_default(); quote! { CommandDescription { prefix: #prefix, command: #name, description: #description } }
quote! { CommandDescription { prefix: #prefix, command: #command, description: #description } }
}); });
let global_description = match global.description.as_deref() { let global_description = match global.description.as_deref() {
@ -100,10 +97,9 @@ fn impl_descriptions(
fn impl_parse( fn impl_parse(
infos: &[Command], infos: &[Command],
global: &CommandEnum,
variants_initialization: &[proc_macro2::TokenStream], variants_initialization: &[proc_macro2::TokenStream],
) -> 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! { quote! {
fn parse<N>(s: &str, bot_name: N) -> Result<Self, teloxide::utils::command::ParseError> fn parse<N>(s: &str, bot_name: N) -> Result<Self, teloxide::utils::command::ParseError>

View file

@ -1,55 +1,54 @@
use crate::{ use crate::{
command_attr::CommandAttrs, command_enum::CommandEnum, command_attr::CommandAttrs, command_enum::CommandEnum,
fields_parse::ParserType, rename_rules::RenameRule, Result, fields_parse::ParserType, Result,
}; };
pub(crate) struct Command { 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 description: Option<String>,
pub parser: Option<ParserType>, /// Name of the command, with all renames already applied.
pub name: String, pub name: String,
/// Parser for arguments of this command.
pub parser: ParserType,
} }
impl Command { 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 { let CommandAttrs {
prefix, prefix,
description, description,
rename_rule, rename_rule,
parser, parser,
// FIXME: error on/do not ignore separator
separator: _, separator: _,
} = attrs; } = 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 }) Ok(Self { prefix, description, parser, name })
} }
pub fn get_matched_value(&self, global_parameters: &CommandEnum) -> String { pub fn get_prefixed_command(&self) -> String {
let prefix = if let Some(prefix) = &self.prefix { let Self { prefix, name, .. } = self;
prefix format!("{prefix}{name}")
} 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(crate) fn description_is_enabled(&self) -> bool { pub(crate) fn description_is_enabled(&self) -> bool {

View file

@ -9,22 +9,31 @@ use crate::{
use proc_macro2::Span; use proc_macro2::Span;
use syn::Attribute; use syn::Attribute;
/// Attributes for `BotCommands` derive macro. /// All attributes that can be used for `derive(BotCommands)`
pub(crate) struct CommandAttrs { pub(crate) struct CommandAttrs {
pub prefix: Option<String>, pub prefix: Option<(String, Span)>,
pub description: Option<String>, pub description: Option<(String, Span)>,
pub rename_rule: Option<RenameRule>, pub rename_rule: Option<(RenameRule, Span)>,
pub parser: Option<ParserType>, pub parser: Option<(ParserType, Span)>,
pub separator: Option<String>, pub separator: Option<(String, Span)>,
} }
/// An attribute for `BotCommands` derive macro. /// A single k/v attribute for `BotCommands` derive macro.
pub(crate) struct CommandAttr { ///
/// For example:
/// ```text
/// #[command(prefix = "!", rename_rule = "snake_case")]
/// /^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^---- CommandAttr { kind: RenameRule(SnakeCase) }
/// |
/// CommandAttr { kind: Prefix("!") }
/// ```
struct CommandAttr {
kind: CommandAttrKind, kind: CommandAttrKind,
sp: Span, sp: Span,
} }
pub(crate) enum CommandAttrKind { /// Kind of [`CommandAttr`].
enum CommandAttrKind {
Prefix(String), Prefix(String),
Description(String), Description(String),
RenameRule(RenameRule), RenameRule(RenameRule),
@ -49,13 +58,13 @@ impl CommandAttrs {
}, },
|mut this, attr| { |mut this, attr| {
fn insert<T>( fn insert<T>(
opt: &mut Option<T>, opt: &mut Option<(T, Span)>,
x: T, x: T,
sp: Span, sp: Span,
) -> Result<()> { ) -> Result<()> {
match opt { match opt {
slot @ None => { slot @ None => {
*slot = Some(x); *slot = Some((x, sp));
Ok(()) Ok(())
} }
Some(_) => { Some(_) => {

View file

@ -5,14 +5,15 @@ use crate::{
#[derive(Debug)] #[derive(Debug)]
pub(crate) struct CommandEnum { pub(crate) struct CommandEnum {
pub prefix: Option<String>, pub prefix: String,
pub description: Option<String>, pub description: Option<String>,
pub rename_rule: RenameRule, pub rename_rule: RenameRule,
pub parser_type: ParserType, pub parser_type: ParserType,
} }
impl CommandEnum { 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 { let CommandAttrs {
prefix, prefix,
description, description,
@ -20,18 +21,22 @@ impl CommandEnum {
parser, parser,
separator, separator,
} = attrs; } = 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 // FIXME: Error on unused separator
if let (ParserType::Split { separator }, Some(s)) = if let (ParserType::Split { separator }, Some((s, _))) =
(&mut parser, &separator) (&mut parser, &separator)
{ {
*separator = Some(s.clone()) *separator = Some(s.clone())
} }
Ok(Self { Ok(Self {
prefix, prefix: prefix.map(|(p, _)| p).unwrap_or_else(|| "/".to_owned()),
description, description: description.map(|(d, _)| d),
rename_rule: rename_rule.unwrap_or(RenameRule::Identity), rename_rule: rename_rule
.map(|(rr, _)| rr)
.unwrap_or(RenameRule::Identity),
parser_type: parser, parser_type: parser,
}) })
} }

View file

@ -1,7 +1,7 @@
use quote::quote; use quote::quote;
use syn::{Fields, FieldsNamed, FieldsUnnamed, Type}; use syn::{Fields, FieldsNamed, FieldsUnnamed, Type};
#[derive(Debug)] #[derive(Debug, Clone)]
pub(crate) enum ParserType { pub(crate) enum ParserType {
Default, Default,
Split { separator: Option<String> }, Split { separator: Option<String> },