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::{
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<Self, syn::Error> {
impl Parse for CommandAttrName {
fn parse(input: ParseStream) -> syn::Result<Self> {
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<Self, syn::Error> {
let name = input.parse::<BotCommandAttribute>()?;
impl Parse for CommandAttr {
fn parse(input: ParseStream) -> syn::Result<Self> {
let name = input.parse::<CommandAttrName>()?;
// FIXME: this should support value-less attrs, as well as
// non-string-literal values
input.parse::<Token![=]>()?;
let value = input.parse::<LitStr>()?.value();
@ -40,29 +49,20 @@ impl Parse for Attr {
}
}
impl Attr {
pub fn name(&self) -> &BotCommandAttribute {
&self.name
}
pub(crate) struct CommandAttrs(Punctuated<CommandAttr, Token![,]>);
pub fn value(&self) -> String {
self.value.clone()
impl Parse for CommandAttrs {
fn parse(input: ParseStream) -> syn::Result<Self> {
input.parse_terminated(CommandAttr::parse).map(Self)
}
}
pub(crate) struct VecAttrs {
pub data: Vec<Attr>,
}
impl IntoIterator for CommandAttrs {
type Item = CommandAttr;
impl Parse for VecAttrs {
fn parse(input: ParseStream) -> Result<Self, syn::Error> {
let mut data = vec![];
while !input.is_empty() {
data.push(input.parse()?);
if !input.is_empty() {
input.parse::<Token![,]>()?;
}
}
Ok(Self { data })
type IntoIter = syn::punctuated::IntoIter<CommandAttr>;
fn into_iter(self) -> Self::IntoIter {
self.0.into_iter()
}
}

View file

@ -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<Self> {
pub fn try_from(attrs: &[CommandAttr], name: &str) -> Result<Self> {
let attrs = parse_attrs(attrs)?;
let CommandAttrs {
prefix,
@ -65,26 +65,22 @@ pub(crate) struct CommandAttrs {
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 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()),
}
}

View file

@ -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<Self> {
pub fn try_from(attrs: &[CommandAttr]) -> Result<Self> {
let attrs = parse_attrs(attrs)?;
let prefix = attrs.prefix;

View file

@ -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<TokenStream, Error> {
let input = syn::parse_macro_input::parse::<DeriveInput>(tokens)?;
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 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() {
let mut attrs = Vec::new();
for attr in &variant.attrs {
let mut attrs_ = attr
.parse_args::<VecAttrs>()
let attrs_ = attr
.parse_args::<CommandAttrs>()
.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<Vec<Attr>> {
fn parse_attributes(input: &[syn::Attribute]) -> Result<Vec<CommandAttr>> {
let mut enum_attrs = Vec::new();
for attr in input.iter() {
match attr.parse_args::<VecAttrs>() {
Ok(mut attrs_) => {
enum_attrs.append(attrs_.data.as_mut());
match attr.parse_args::<CommandAttrs>() {
Ok(attrs) => {
enum_attrs.extend(attrs);
}
Err(e) => {
return Err(compile_error(e.to_compile_error()));