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::{
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>

View file

@ -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 {

View file

@ -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(_) => {

View file

@ -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,
})
}

View file

@ -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> },