From 66ad49ab7cfa93089b5432c8cdaf2525ab061e51 Mon Sep 17 00:00:00 2001 From: Maybe Waffle Date: Mon, 22 Aug 2022 18:18:44 +0400 Subject: [PATCH] Clean up attribute handling a bit --- src/attr.rs | 72 ++++++++++++++++++++++----------------------- src/command.rs | 26 +++++++--------- src/command_enum.rs | 4 +-- src/lib.rs | 18 ++++++------ 4 files changed, 58 insertions(+), 62 deletions(-) diff --git a/src/attr.rs b/src/attr.rs index c15cfb76..953c819c 100644 --- a/src/attr.rs +++ b/src/attr.rs @@ -1,38 +1,47 @@ use syn::{ parse::{Parse, ParseStream}, + punctuated::Punctuated, LitStr, Token, }; -pub(crate) enum BotCommandAttribute { +pub(crate) enum CommandAttrName { Prefix, Description, - RenameRule, - CustomParser, + Rename, + ParseWith, Separator, } -impl Parse for BotCommandAttribute { - fn parse(input: ParseStream) -> Result { +impl Parse for CommandAttrName { + fn parse(input: ParseStream) -> syn::Result { let name_arg: syn::Ident = input.parse()?; + match name_arg.to_string().as_str() { - "prefix" => Ok(BotCommandAttribute::Prefix), - "description" => Ok(BotCommandAttribute::Description), - "rename" => Ok(BotCommandAttribute::RenameRule), - "parse_with" => Ok(BotCommandAttribute::CustomParser), - "separator" => Ok(BotCommandAttribute::Separator), - _ => Err(syn::Error::new(name_arg.span(), "unexpected argument")), + "prefix" => Ok(CommandAttrName::Prefix), + "description" => Ok(CommandAttrName::Description), + "rename" => Ok(CommandAttrName::Rename), + "parse_with" => Ok(CommandAttrName::ParseWith), + "separator" => Ok(CommandAttrName::Separator), + _ => Err(syn::Error::new( + name_arg.span(), + "unexpected attribute name (expected one of `prefix`, \ + `description`, `rename`, `parse_with`, `separator`", + )), } } } -pub(crate) struct Attr { - name: BotCommandAttribute, - value: String, +pub(crate) struct CommandAttr { + pub name: CommandAttrName, + pub value: String, } -impl Parse for Attr { - fn parse(input: ParseStream) -> Result { - let name = input.parse::()?; +impl Parse for CommandAttr { + fn parse(input: ParseStream) -> syn::Result { + let name = input.parse::()?; + + // FIXME: this should support value-less attrs, as well as + // non-string-literal values input.parse::()?; let value = input.parse::()?.value(); @@ -40,29 +49,20 @@ impl Parse for Attr { } } -impl Attr { - pub fn name(&self) -> &BotCommandAttribute { - &self.name - } +pub(crate) struct CommandAttrs(Punctuated); - pub fn value(&self) -> String { - self.value.clone() +impl Parse for CommandAttrs { + fn parse(input: ParseStream) -> syn::Result { + input.parse_terminated(CommandAttr::parse).map(Self) } } -pub(crate) struct VecAttrs { - pub data: Vec, -} +impl IntoIterator for CommandAttrs { + type Item = CommandAttr; -impl Parse for VecAttrs { - fn parse(input: ParseStream) -> Result { - let mut data = vec![]; - while !input.is_empty() { - data.push(input.parse()?); - if !input.is_empty() { - input.parse::()?; - } - } - Ok(Self { data }) + type IntoIter = syn::punctuated::IntoIter; + + fn into_iter(self) -> Self::IntoIter { + self.0.into_iter() } } diff --git a/src/command.rs b/src/command.rs index 090c9c46..b830c7c5 100644 --- a/src/command.rs +++ b/src/command.rs @@ -1,5 +1,5 @@ use crate::{ - attr::{Attr, BotCommandAttribute}, + attr::{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: &[Attr], name: &str) -> Result { + pub fn try_from(attrs: &[CommandAttr], name: &str) -> Result { let attrs = parse_attrs(attrs)?; let CommandAttrs { prefix, @@ -65,26 +65,22 @@ pub(crate) struct CommandAttrs { pub separator: Option, } -pub(crate) fn parse_attrs(attrs: &[Attr]) -> Result { +pub(crate) fn parse_attrs(attrs: &[CommandAttr]) -> Result { let mut prefix = None; let mut description = None; let mut rename_rule = RenameRule::Identity; let mut parser = None; let mut separator = None; - for attr in attrs { - match attr.name() { - BotCommandAttribute::Prefix => prefix = Some(attr.value()), - BotCommandAttribute::Description => { - description = Some(attr.value()) + 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::ParseWith => { + parser = Some(ParserType::parse(value)) } - BotCommandAttribute::RenameRule => { - rename_rule = RenameRule::parse(&attr.value())? - } - BotCommandAttribute::CustomParser => { - parser = Some(ParserType::parse(&attr.value())) - } - BotCommandAttribute::Separator => separator = Some(attr.value()), + CommandAttrName::Separator => separator = Some(value.clone()), } } diff --git a/src/command_enum.rs b/src/command_enum.rs index 271a4ab5..c3248d6a 100644 --- a/src/command_enum.rs +++ b/src/command_enum.rs @@ -1,5 +1,5 @@ use crate::{ - attr::Attr, command::parse_attrs, fields_parse::ParserType, + attr::CommandAttr, 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: &[Attr]) -> Result { + pub fn try_from(attrs: &[CommandAttr]) -> Result { let attrs = parse_attrs(attrs)?; let prefix = attrs.prefix; diff --git a/src/lib.rs b/src/lib.rs index a4406f88..8fd93007 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::{Attr, VecAttrs}, + attr::{CommandAttr, CommandAttrs}, command::Command, command_enum::CommandEnum, fields_parse::{impl_parse_args_named, impl_parse_args_unnamed}, @@ -31,7 +31,7 @@ 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 enum_attrs: Vec = parse_attributes(&input.attrs)?; + let enum_attrs: Vec = parse_attributes(&input.attrs)?; let command_enum = CommandEnum::try_from(enum_attrs.as_slice())?; let variants: Vec<&syn::Variant> = data_enum.variants.iter().collect(); @@ -40,10 +40,10 @@ fn bot_commands_impl(tokens: TokenStream) -> Result { for variant in variants.iter() { let mut attrs = Vec::new(); for attr in &variant.attrs { - let mut attrs_ = attr - .parse_args::() + let attrs_ = attr + .parse_args::() .map_err(|e| compile_error(e.to_compile_error()))?; - attrs.append(attrs_.data.as_mut()); + attrs.extend(attrs_); } let command = Command::try_from(attrs.as_slice(), &variant.ident.to_string())?; @@ -183,12 +183,12 @@ fn get_enum_data(input: &DeriveInput) -> Result<&syn::DataEnum> { } } -fn parse_attributes(input: &[syn::Attribute]) -> Result> { +fn parse_attributes(input: &[syn::Attribute]) -> Result> { let mut enum_attrs = Vec::new(); for attr in input.iter() { - match attr.parse_args::() { - Ok(mut attrs_) => { - enum_attrs.append(attrs_.data.as_mut()); + match attr.parse_args::() { + Ok(attrs) => { + enum_attrs.extend(attrs); } Err(e) => { return Err(compile_error(e.to_compile_error()));