diff --git a/src/attr.rs b/src/attr.rs index 953c819c..765cff7c 100644 --- a/src/attr.rs +++ b/src/attr.rs @@ -1,9 +1,10 @@ use syn::{ - parse::{Parse, ParseStream}, - punctuated::Punctuated, - LitStr, Token, + parse::{Parse, ParseBuffer, ParseStream}, + Attribute, LitStr, Token, }; +use crate::Result; + pub(crate) enum CommandAttrName { Prefix, Description, @@ -49,20 +50,47 @@ impl Parse for CommandAttr { } } -pub(crate) struct CommandAttrs(Punctuated<CommandAttr, Token![,]>); +pub(crate) struct CommandAttrs(Vec<CommandAttr>); -impl Parse for CommandAttrs { - fn parse(input: ParseStream) -> syn::Result<Self> { - input.parse_terminated(CommandAttr::parse).map(Self) +impl CommandAttrs { + pub fn from_attributes(attributes: &[Attribute]) -> Result<Self> { + let mut attrs = Vec::new(); + + for attribute in attributes.iter().filter(is_command_attribute) { + let attrs_ = attribute.parse_args_with(|input: &ParseBuffer| { + input.parse_terminated::<_, Token![,]>(CommandAttr::parse) + })?; + + attrs.extend(attrs_); + } + + Ok(Self(attrs)) + } +} + +impl<'a> IntoIterator for &'a CommandAttrs { + type Item = &'a CommandAttr; + + type IntoIter = std::slice::Iter<'a, CommandAttr>; + + fn into_iter(self) -> Self::IntoIter { + self.0.iter() } } impl IntoIterator for CommandAttrs { type Item = CommandAttr; - type IntoIter = syn::punctuated::IntoIter<CommandAttr>; + type IntoIter = std::vec::IntoIter<CommandAttr>; fn into_iter(self) -> Self::IntoIter { self.0.into_iter() } } + +fn is_command_attribute(a: &&Attribute) -> bool { + match a.path.get_ident() { + Some(ident) => ident == "command", + _ => false, + } +} diff --git a/src/command.rs b/src/command.rs index b830c7c5..0c6dd064 100644 --- a/src/command.rs +++ b/src/command.rs @@ -1,5 +1,5 @@ use crate::{ - attr::{CommandAttr, CommandAttrName}, + attr::{self, CommandAttr, CommandAttrName}, command_enum::CommandEnum, fields_parse::ParserType, rename_rules::RenameRule, @@ -14,7 +14,7 @@ pub(crate) struct Command { } impl Command { - pub fn try_from(attrs: &[CommandAttr], name: &str) -> Result<Self> { + pub fn try_from(attrs: attr::CommandAttrs, name: &str) -> Result<Self> { let attrs = parse_attrs(attrs)?; let CommandAttrs { prefix, @@ -65,7 +65,7 @@ pub(crate) struct CommandAttrs { pub separator: Option<String>, } -pub(crate) fn parse_attrs(attrs: &[CommandAttr]) -> Result<CommandAttrs> { +pub(crate) fn parse_attrs(attrs: attr::CommandAttrs) -> Result<CommandAttrs> { let mut prefix = None; let mut description = None; let mut rename_rule = RenameRule::Identity; @@ -74,13 +74,13 @@ pub(crate) fn parse_attrs(attrs: &[CommandAttr]) -> Result<CommandAttrs> { for CommandAttr { name, value } in attrs { match name { - CommandAttrName::Prefix => prefix = Some(value.clone()), - CommandAttrName::Description => description = Some(value.clone()), - CommandAttrName::Rename => rename_rule = RenameRule::parse(value)?, + CommandAttrName::Prefix => prefix = Some(value), + CommandAttrName::Description => description = Some(value), + CommandAttrName::Rename => rename_rule = RenameRule::parse(&value)?, CommandAttrName::ParseWith => { - parser = Some(ParserType::parse(value)) + parser = Some(ParserType::parse(&value)) } - CommandAttrName::Separator => separator = Some(value.clone()), + CommandAttrName::Separator => separator = Some(value), } } diff --git a/src/command_enum.rs b/src/command_enum.rs index c3248d6a..14aa0381 100644 --- a/src/command_enum.rs +++ b/src/command_enum.rs @@ -1,5 +1,5 @@ use crate::{ - attr::CommandAttr, command::parse_attrs, fields_parse::ParserType, + attr, command::parse_attrs, fields_parse::ParserType, rename_rules::RenameRule, Result, }; @@ -12,7 +12,7 @@ pub(crate) struct CommandEnum { } impl CommandEnum { - pub fn try_from(attrs: &[CommandAttr]) -> Result<Self> { + pub fn try_from(attrs: attr::CommandAttrs) -> Result<Self> { let attrs = parse_attrs(attrs)?; let prefix = attrs.prefix; diff --git a/src/lib.rs b/src/lib.rs index e7b8ae12..9a870d0d 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -11,7 +11,7 @@ extern crate proc_macro; extern crate quote; extern crate syn; use crate::{ - attr::{CommandAttr, CommandAttrs}, + attr::CommandAttrs, command::Command, command_enum::CommandEnum, fields_parse::{impl_parse_args_named, impl_parse_args_unnamed}, @@ -31,28 +31,23 @@ fn bot_commands_impl(tokens: TokenStream) -> Result<TokenStream, Error> { let input = syn::parse_macro_input::parse::<DeriveInput>(tokens)?; let data_enum: &syn::DataEnum = get_enum_data(&input)?; - let enum_attrs: Vec<CommandAttr> = parse_attributes(&input.attrs)?; - let command_enum = CommandEnum::try_from(enum_attrs.as_slice())?; + let enum_attrs = CommandAttrs::from_attributes(&input.attrs)?; + let command_enum = CommandEnum::try_from(enum_attrs)?; - let variants: Vec<&syn::Variant> = data_enum.variants.iter().collect(); + let variant_infos = data_enum + .variants + .iter() + .map(|variant| { + let attrs = CommandAttrs::from_attributes(&variant.attrs)?; + let command = Command::try_from(attrs, &variant.ident.to_string())?; - let mut variant_infos = vec![]; - for variant in variants.iter() { - let mut attrs = Vec::new(); - for attr in &variant.attrs { - let attrs_ = attr - .parse_args::<CommandAttrs>() - .map_err(|e| compile_error(e.to_compile_error()))?; - attrs.extend(attrs_); - } - let command = - Command::try_from(attrs.as_slice(), &variant.ident.to_string())?; - - variant_infos.push(command); - } + Ok(command) + }) + .collect::<Result<Vec<_>, Error>>()?; let mut vec_impl_create = vec![]; - for (variant, info) in variants.iter().zip(variant_infos.iter()) { + + for (variant, info) in data_enum.variants.iter().zip(variant_infos.iter()) { let var = &variant.ident; let variant_ = quote! { Self::#var }; match &variant.fields { @@ -182,18 +177,3 @@ fn get_enum_data(input: &DeriveInput) -> Result<&syn::DataEnum> { _ => Err(compile_error("TelegramBotCommand allowed only for enums")), } } - -fn parse_attributes(input: &[syn::Attribute]) -> Result<Vec<CommandAttr>> { - let mut enum_attrs = Vec::new(); - for attr in input.iter() { - match attr.parse_args::<CommandAttrs>() { - Ok(attrs) => { - enum_attrs.extend(attrs); - } - Err(e) => { - return Err(compile_error(e.to_compile_error())); - } - } - } - Ok(enum_attrs) -}