Clean up attribute handling a bit

This commit is contained in:
Maybe Waffle 2022-08-22 18:18:44 +04:00
parent ddacee966e
commit 66ad49ab7c
4 changed files with 58 additions and 62 deletions

View file

@ -1,38 +1,47 @@
use syn::{ use syn::{
parse::{Parse, ParseStream}, parse::{Parse, ParseStream},
punctuated::Punctuated,
LitStr, Token, LitStr, Token,
}; };
pub(crate) enum BotCommandAttribute { pub(crate) enum CommandAttrName {
Prefix, Prefix,
Description, Description,
RenameRule, Rename,
CustomParser, ParseWith,
Separator, Separator,
} }
impl Parse for BotCommandAttribute { impl Parse for CommandAttrName {
fn parse(input: ParseStream) -> Result<Self, syn::Error> { fn parse(input: ParseStream) -> syn::Result<Self> {
let name_arg: syn::Ident = input.parse()?; let name_arg: syn::Ident = input.parse()?;
match name_arg.to_string().as_str() { match name_arg.to_string().as_str() {
"prefix" => Ok(BotCommandAttribute::Prefix), "prefix" => Ok(CommandAttrName::Prefix),
"description" => Ok(BotCommandAttribute::Description), "description" => Ok(CommandAttrName::Description),
"rename" => Ok(BotCommandAttribute::RenameRule), "rename" => Ok(CommandAttrName::Rename),
"parse_with" => Ok(BotCommandAttribute::CustomParser), "parse_with" => Ok(CommandAttrName::ParseWith),
"separator" => Ok(BotCommandAttribute::Separator), "separator" => Ok(CommandAttrName::Separator),
_ => Err(syn::Error::new(name_arg.span(), "unexpected argument")), _ => Err(syn::Error::new(
name_arg.span(),
"unexpected attribute name (expected one of `prefix`, \
`description`, `rename`, `parse_with`, `separator`",
)),
} }
} }
} }
pub(crate) struct Attr { pub(crate) struct CommandAttr {
name: BotCommandAttribute, pub name: CommandAttrName,
value: String, pub value: String,
} }
impl Parse for Attr { impl Parse for CommandAttr {
fn parse(input: ParseStream) -> Result<Self, syn::Error> { fn parse(input: ParseStream) -> syn::Result<Self> {
let name = input.parse::<BotCommandAttribute>()?; let name = input.parse::<CommandAttrName>()?;
// FIXME: this should support value-less attrs, as well as
// non-string-literal values
input.parse::<Token![=]>()?; input.parse::<Token![=]>()?;
let value = input.parse::<LitStr>()?.value(); let value = input.parse::<LitStr>()?.value();
@ -40,29 +49,20 @@ impl Parse for Attr {
} }
} }
impl Attr { pub(crate) struct CommandAttrs(Punctuated<CommandAttr, Token![,]>);
pub fn name(&self) -> &BotCommandAttribute {
&self.name
}
pub fn value(&self) -> String { impl Parse for CommandAttrs {
self.value.clone() fn parse(input: ParseStream) -> syn::Result<Self> {
input.parse_terminated(CommandAttr::parse).map(Self)
} }
} }
pub(crate) struct VecAttrs { impl IntoIterator for CommandAttrs {
pub data: Vec<Attr>, type Item = CommandAttr;
}
impl Parse for VecAttrs { type IntoIter = syn::punctuated::IntoIter<CommandAttr>;
fn parse(input: ParseStream) -> Result<Self, syn::Error> {
let mut data = vec![]; fn into_iter(self) -> Self::IntoIter {
while !input.is_empty() { self.0.into_iter()
data.push(input.parse()?);
if !input.is_empty() {
input.parse::<Token![,]>()?;
}
}
Ok(Self { data })
} }
} }

View file

@ -1,5 +1,5 @@
use crate::{ use crate::{
attr::{Attr, BotCommandAttribute}, attr::{CommandAttr, CommandAttrName},
command_enum::CommandEnum, command_enum::CommandEnum,
fields_parse::ParserType, fields_parse::ParserType,
rename_rules::RenameRule, rename_rules::RenameRule,
@ -14,7 +14,7 @@ pub(crate) struct Command {
} }
impl Command { impl Command {
pub fn try_from(attrs: &[Attr], name: &str) -> Result<Self> { pub fn try_from(attrs: &[CommandAttr], name: &str) -> Result<Self> {
let attrs = parse_attrs(attrs)?; let attrs = parse_attrs(attrs)?;
let CommandAttrs { let CommandAttrs {
prefix, prefix,
@ -65,26 +65,22 @@ pub(crate) struct CommandAttrs {
pub separator: Option<String>, pub separator: Option<String>,
} }
pub(crate) fn parse_attrs(attrs: &[Attr]) -> Result<CommandAttrs> { pub(crate) fn parse_attrs(attrs: &[CommandAttr]) -> Result<CommandAttrs> {
let mut prefix = None; let mut prefix = None;
let mut description = None; let mut description = None;
let mut rename_rule = RenameRule::Identity; let mut rename_rule = RenameRule::Identity;
let mut parser = None; let mut parser = None;
let mut separator = None; let mut separator = None;
for attr in attrs { for CommandAttr { name, value } in attrs {
match attr.name() { match name {
BotCommandAttribute::Prefix => prefix = Some(attr.value()), CommandAttrName::Prefix => prefix = Some(value.clone()),
BotCommandAttribute::Description => { CommandAttrName::Description => description = Some(value.clone()),
description = Some(attr.value()) CommandAttrName::Rename => rename_rule = RenameRule::parse(value)?,
CommandAttrName::ParseWith => {
parser = Some(ParserType::parse(value))
} }
BotCommandAttribute::RenameRule => { CommandAttrName::Separator => separator = Some(value.clone()),
rename_rule = RenameRule::parse(&attr.value())?
}
BotCommandAttribute::CustomParser => {
parser = Some(ParserType::parse(&attr.value()))
}
BotCommandAttribute::Separator => separator = Some(attr.value()),
} }
} }

View file

@ -1,5 +1,5 @@
use crate::{ use crate::{
attr::Attr, command::parse_attrs, fields_parse::ParserType, attr::CommandAttr, command::parse_attrs, fields_parse::ParserType,
rename_rules::RenameRule, Result, rename_rules::RenameRule, Result,
}; };
@ -12,7 +12,7 @@ pub(crate) struct CommandEnum {
} }
impl CommandEnum { impl CommandEnum {
pub fn try_from(attrs: &[Attr]) -> Result<Self> { pub fn try_from(attrs: &[CommandAttr]) -> Result<Self> {
let attrs = parse_attrs(attrs)?; let attrs = parse_attrs(attrs)?;
let prefix = attrs.prefix; let prefix = attrs.prefix;

View file

@ -11,7 +11,7 @@ extern crate proc_macro;
extern crate quote; extern crate quote;
extern crate syn; extern crate syn;
use crate::{ use crate::{
attr::{Attr, VecAttrs}, attr::{CommandAttr, CommandAttrs},
command::Command, command::Command,
command_enum::CommandEnum, command_enum::CommandEnum,
fields_parse::{impl_parse_args_named, impl_parse_args_unnamed}, fields_parse::{impl_parse_args_named, impl_parse_args_unnamed},
@ -31,7 +31,7 @@ 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: &syn::DataEnum = get_enum_data(&input)?;
let enum_attrs: Vec<Attr> = parse_attributes(&input.attrs)?; let enum_attrs: Vec<CommandAttr> = parse_attributes(&input.attrs)?;
let command_enum = CommandEnum::try_from(enum_attrs.as_slice())?; let command_enum = CommandEnum::try_from(enum_attrs.as_slice())?;
let variants: Vec<&syn::Variant> = data_enum.variants.iter().collect(); let variants: Vec<&syn::Variant> = data_enum.variants.iter().collect();
@ -40,10 +40,10 @@ fn bot_commands_impl(tokens: TokenStream) -> Result<TokenStream, Error> {
for variant in variants.iter() { for variant in variants.iter() {
let mut attrs = Vec::new(); let mut attrs = Vec::new();
for attr in &variant.attrs { for attr in &variant.attrs {
let mut attrs_ = attr let attrs_ = attr
.parse_args::<VecAttrs>() .parse_args::<CommandAttrs>()
.map_err(|e| compile_error(e.to_compile_error()))?; .map_err(|e| compile_error(e.to_compile_error()))?;
attrs.append(attrs_.data.as_mut()); attrs.extend(attrs_);
} }
let command = let command =
Command::try_from(attrs.as_slice(), &variant.ident.to_string())?; 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<Vec<Attr>> { fn parse_attributes(input: &[syn::Attribute]) -> Result<Vec<CommandAttr>> {
let mut enum_attrs = Vec::new(); let mut enum_attrs = Vec::new();
for attr in input.iter() { for attr in input.iter() {
match attr.parse_args::<VecAttrs>() { match attr.parse_args::<CommandAttrs>() {
Ok(mut attrs_) => { Ok(attrs) => {
enum_attrs.append(attrs_.data.as_mut()); enum_attrs.extend(attrs);
} }
Err(e) => { Err(e) => {
return Err(compile_error(e.to_compile_error())); return Err(compile_error(e.to_compile_error()));