From f03264e694c01984969688f54364321d0d6c6e2d Mon Sep 17 00:00:00 2001 From: Maybe Waffle Date: Tue, 23 Aug 2022 20:37:23 +0400 Subject: [PATCH] Iterator ftw --- src/command.rs | 4 +++ src/fields_parse.rs | 24 ++++++++++--- src/lib.rs | 88 +++++++++++++++++++-------------------------- src/unzip.rs | 20 +++++++++++ 4 files changed, 80 insertions(+), 56 deletions(-) create mode 100644 src/unzip.rs diff --git a/src/command.rs b/src/command.rs index 0c6dd064..b06038f1 100644 --- a/src/command.rs +++ b/src/command.rs @@ -55,6 +55,10 @@ impl Command { (String::from(prefix), global_parameters.rename_rule.apply(&self.name)) } + + pub(crate) fn description_is_enabled(&self) -> bool { + self.description != Some("off".to_owned()) + } } pub(crate) struct CommandAttrs { diff --git a/src/fields_parse.rs b/src/fields_parse.rs index d9d52337..19b518a6 100644 --- a/src/fields_parse.rs +++ b/src/fields_parse.rs @@ -1,7 +1,7 @@ extern crate quote; -use quote::{quote, ToTokens}; -use syn::{FieldsNamed, FieldsUnnamed, Type}; +use quote::quote; +use syn::{Fields, FieldsNamed, FieldsUnnamed, Type}; #[derive(Debug)] pub(crate) enum ParserType { @@ -20,9 +20,25 @@ impl ParserType { } } +pub(crate) fn impl_parse_args( + fields: &Fields, + self_variant: proc_macro2::TokenStream, + parser: &ParserType, +) -> proc_macro2::TokenStream { + match fields { + Fields::Unit => self_variant, + Fields::Unnamed(fields) => { + impl_parse_args_unnamed(fields, self_variant, parser) + } + Fields::Named(named) => { + impl_parse_args_named(named, self_variant, parser) + } + } +} + pub(crate) fn impl_parse_args_unnamed( data: &FieldsUnnamed, - variant: impl ToTokens, + variant: proc_macro2::TokenStream, parser_type: &ParserType, ) -> proc_macro2::TokenStream { let get_arguments = create_parser( @@ -46,7 +62,7 @@ pub(crate) fn impl_parse_args_unnamed( pub(crate) fn impl_parse_args_named( data: &FieldsNamed, - variant: impl ToTokens, + variant: proc_macro2::TokenStream, parser_type: &ParserType, ) -> proc_macro2::TokenStream { let get_arguments = create_parser( diff --git a/src/lib.rs b/src/lib.rs index 9a870d0d..eba47a57 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -6,19 +6,18 @@ mod command_enum; mod error; mod fields_parse; mod rename_rules; +mod unzip; extern crate proc_macro; extern crate quote; extern crate syn; use crate::{ - attr::CommandAttrs, - command::Command, - command_enum::CommandEnum, - fields_parse::{impl_parse_args_named, impl_parse_args_unnamed}, + attr::CommandAttrs, command::Command, command_enum::CommandEnum, + fields_parse::impl_parse_args, unzip::Unzip, }; use proc_macro::TokenStream; use quote::quote; -use syn::{DeriveInput, Fields}; +use syn::DeriveInput; pub(crate) use error::{compile_error, Error, Result}; @@ -30,53 +29,35 @@ pub fn bot_commands_derive(tokens: TokenStream) -> TokenStream { fn bot_commands_impl(tokens: TokenStream) -> Result { let input = syn::parse_macro_input::parse::(tokens)?; - let data_enum: &syn::DataEnum = get_enum_data(&input)?; + let data_enum = get_enum_data(&input)?; let enum_attrs = CommandAttrs::from_attributes(&input.attrs)?; let command_enum = CommandEnum::try_from(enum_attrs)?; - let variant_infos = data_enum + 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())?; - Ok(command) + 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); + + Ok((parse, command)) }) - .collect::, Error>>()?; + .collect::, Vec<_>>, Error>>()?; - let mut vec_impl_create = vec![]; - - for (variant, info) in data_enum.variants.iter().zip(variant_infos.iter()) { - let var = &variant.ident; - let variant_ = quote! { Self::#var }; - match &variant.fields { - Fields::Unnamed(fields) => { - let parser = - info.parser.as_ref().unwrap_or(&command_enum.parser_type); - vec_impl_create - .push(impl_parse_args_unnamed(fields, variant_, parser)); - } - Fields::Unit => { - vec_impl_create.push(variant_); - } - Fields::Named(named) => { - let parser = - info.parser.as_ref().unwrap_or(&command_enum.parser_type); - vec_impl_create - .push(impl_parse_args_named(named, variant_, parser)); - } - } - } - - let ident = &input.ident; - - let fn_descriptions = impl_descriptions(&variant_infos, &command_enum); - let fn_parse = impl_parse(&variant_infos, &command_enum, &vec_impl_create); - let fn_commands = impl_commands(&variant_infos, &command_enum); + 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 trait_impl = quote! { - impl BotCommands for #ident { + impl BotCommands for #type_name { #fn_descriptions #fn_parse #fn_commands @@ -90,19 +71,19 @@ fn impl_commands( infos: &[Command], global: &CommandEnum, ) -> proc_macro2::TokenStream { - let commands_to_list = infos.iter().filter_map(|command| { - if command.description == Some("off".into()) { - None - } else { + let commands = infos + .iter() + .filter(|command| command.description_is_enabled()) + .map(|command| { let c = command.get_matched_value(global); let d = command.description.as_deref().unwrap_or_default(); - Some(quote! { BotCommand::new(#c,#d) }) - } - }); + quote! { BotCommand::new(#c,#d) } + }); + quote! { fn bot_commands() -> Vec { use teloxide::types::BotCommand; - vec![#(#commands_to_list),*] + vec![#(#commands),*] } } } @@ -111,11 +92,14 @@ fn impl_descriptions( infos: &[Command], global: &CommandEnum, ) -> proc_macro2::TokenStream { - let command_descriptions = infos.iter().filter_map(|c| { - let (prefix, command) = c.get_matched_value2(global); - let description = c.description.clone().unwrap_or_default(); - (description != "off").then(|| quote! { CommandDescription { prefix: #prefix, command: #command, description: #description } }) - }); + 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 } } + }); let global_description = match global.description.as_deref() { Some(gd) => quote! { .global_description(#gd) }, diff --git a/src/unzip.rs b/src/unzip.rs new file mode 100644 index 00000000..372ad2e2 --- /dev/null +++ b/src/unzip.rs @@ -0,0 +1,20 @@ +use std::iter::FromIterator; + +pub(crate) struct Unzip(pub A, pub B); + +impl FromIterator<(T, U)> for Unzip +where + A: Default + Extend, + B: Default + Extend, +{ + fn from_iter>(iter: I) -> Self { + let (mut a, mut b): (A, B) = Default::default(); + + for (t, u) in iter { + a.extend([t]); + b.extend([u]); + } + + Unzip(a, b) + } +}