mirror of
https://github.com/teloxide/teloxide.git
synced 2025-01-03 17:52:12 +01:00
Clean up attribute handling a bit
This commit is contained in:
parent
ddacee966e
commit
66ad49ab7c
4 changed files with 58 additions and 62 deletions
72
src/attr.rs
72
src/attr.rs
|
@ -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 })
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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()),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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;
|
||||||
|
|
18
src/lib.rs
18
src/lib.rs
|
@ -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()));
|
||||||
|
|
Loading…
Reference in a new issue