Iterator ftw

This commit is contained in:
Maybe Waffle 2022-08-23 20:37:23 +04:00
parent cfe75ae844
commit f03264e694
4 changed files with 80 additions and 56 deletions

View file

@ -55,6 +55,10 @@ impl Command {
(String::from(prefix), global_parameters.rename_rule.apply(&self.name)) (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 { pub(crate) struct CommandAttrs {

View file

@ -1,7 +1,7 @@
extern crate quote; extern crate quote;
use quote::{quote, ToTokens}; use quote::quote;
use syn::{FieldsNamed, FieldsUnnamed, Type}; use syn::{Fields, FieldsNamed, FieldsUnnamed, Type};
#[derive(Debug)] #[derive(Debug)]
pub(crate) enum ParserType { 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( pub(crate) fn impl_parse_args_unnamed(
data: &FieldsUnnamed, data: &FieldsUnnamed,
variant: impl ToTokens, variant: proc_macro2::TokenStream,
parser_type: &ParserType, parser_type: &ParserType,
) -> proc_macro2::TokenStream { ) -> proc_macro2::TokenStream {
let get_arguments = create_parser( let get_arguments = create_parser(
@ -46,7 +62,7 @@ pub(crate) fn impl_parse_args_unnamed(
pub(crate) fn impl_parse_args_named( pub(crate) fn impl_parse_args_named(
data: &FieldsNamed, data: &FieldsNamed,
variant: impl ToTokens, variant: proc_macro2::TokenStream,
parser_type: &ParserType, parser_type: &ParserType,
) -> proc_macro2::TokenStream { ) -> proc_macro2::TokenStream {
let get_arguments = create_parser( let get_arguments = create_parser(

View file

@ -6,19 +6,18 @@ mod command_enum;
mod error; mod error;
mod fields_parse; mod fields_parse;
mod rename_rules; mod rename_rules;
mod unzip;
extern crate proc_macro; extern crate proc_macro;
extern crate quote; extern crate quote;
extern crate syn; extern crate syn;
use crate::{ use crate::{
attr::CommandAttrs, attr::CommandAttrs, command::Command, command_enum::CommandEnum,
command::Command, fields_parse::impl_parse_args, unzip::Unzip,
command_enum::CommandEnum,
fields_parse::{impl_parse_args_named, impl_parse_args_unnamed},
}; };
use proc_macro::TokenStream; use proc_macro::TokenStream;
use quote::quote; use quote::quote;
use syn::{DeriveInput, Fields}; use syn::DeriveInput;
pub(crate) use error::{compile_error, Error, Result}; 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<TokenStream, Error> { fn bot_commands_impl(tokens: TokenStream) -> Result<TokenStream, Error> {
let input = syn::parse_macro_input::parse::<DeriveInput>(tokens)?; let input = syn::parse_macro_input::parse::<DeriveInput>(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 enum_attrs = CommandAttrs::from_attributes(&input.attrs)?;
let command_enum = CommandEnum::try_from(enum_attrs)?; let command_enum = CommandEnum::try_from(enum_attrs)?;
let variant_infos = 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 attrs = CommandAttrs::from_attributes(&variant.attrs)?;
let command = Command::try_from(attrs, &variant.ident.to_string())?; 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::<Result<Vec<_>, Error>>()?; .collect::<Result<Unzip<Vec<_>, Vec<_>>, Error>>()?;
let mut vec_impl_create = vec![]; let type_name = &input.ident;
let fn_descriptions = impl_descriptions(&var_info, &command_enum);
for (variant, info) in data_enum.variants.iter().zip(variant_infos.iter()) { let fn_parse = impl_parse(&var_info, &command_enum, &var_init);
let var = &variant.ident; let fn_commands = impl_commands(&var_info, &command_enum);
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 trait_impl = quote! { let trait_impl = quote! {
impl BotCommands for #ident { impl BotCommands for #type_name {
#fn_descriptions #fn_descriptions
#fn_parse #fn_parse
#fn_commands #fn_commands
@ -90,19 +71,19 @@ fn impl_commands(
infos: &[Command], infos: &[Command],
global: &CommandEnum, global: &CommandEnum,
) -> proc_macro2::TokenStream { ) -> proc_macro2::TokenStream {
let commands_to_list = infos.iter().filter_map(|command| { let commands = infos
if command.description == Some("off".into()) { .iter()
None .filter(|command| command.description_is_enabled())
} else { .map(|command| {
let c = command.get_matched_value(global); let c = command.get_matched_value(global);
let d = command.description.as_deref().unwrap_or_default(); let d = command.description.as_deref().unwrap_or_default();
Some(quote! { BotCommand::new(#c,#d) }) quote! { BotCommand::new(#c,#d) }
} });
});
quote! { quote! {
fn bot_commands() -> Vec<teloxide::types::BotCommand> { fn bot_commands() -> Vec<teloxide::types::BotCommand> {
use teloxide::types::BotCommand; use teloxide::types::BotCommand;
vec![#(#commands_to_list),*] vec![#(#commands),*]
} }
} }
} }
@ -111,11 +92,14 @@ fn impl_descriptions(
infos: &[Command], infos: &[Command],
global: &CommandEnum, global: &CommandEnum,
) -> proc_macro2::TokenStream { ) -> proc_macro2::TokenStream {
let command_descriptions = infos.iter().filter_map(|c| { let command_descriptions = infos
let (prefix, command) = c.get_matched_value2(global); .iter()
let description = c.description.clone().unwrap_or_default(); .filter(|command| command.description_is_enabled())
(description != "off").then(|| quote! { CommandDescription { prefix: #prefix, command: #command, description: #description } }) .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() { let global_description = match global.description.as_deref() {
Some(gd) => quote! { .global_description(#gd) }, Some(gd) => quote! { .global_description(#gd) },

20
src/unzip.rs Normal file
View file

@ -0,0 +1,20 @@
use std::iter::FromIterator;
pub(crate) struct Unzip<A, B>(pub A, pub B);
impl<A, B, T, U> FromIterator<(T, U)> for Unzip<A, B>
where
A: Default + Extend<T>,
B: Default + Extend<U>,
{
fn from_iter<I: IntoIterator<Item = (T, U)>>(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)
}
}