mirror of
https://github.com/teloxide/teloxide.git
synced 2024-12-23 06:51:01 +01:00
now rename in variant overlap rename in enum
This commit is contained in:
parent
d490ed9bc0
commit
0654bbc82d
6 changed files with 149 additions and 66 deletions
|
@ -4,6 +4,7 @@ pub use teloxide_macros::BotCommand;
|
|||
/// Example:
|
||||
/// ```
|
||||
/// use teloxide::utils::{parse_command_into_enum, BotCommand};
|
||||
/// #[command(rename = "lowercase")]
|
||||
/// #[derive(BotCommand, PartialEq, Debug)]
|
||||
/// enum TelegramAdminCommand {
|
||||
/// Ban,
|
||||
|
@ -25,6 +26,7 @@ pub trait BotCommand: Sized {
|
|||
/// Example:
|
||||
/// ```
|
||||
/// use teloxide::utils::{parse_command_into_enum, BotCommand};
|
||||
/// #[command(rename = "lowercase")]
|
||||
/// #[derive(BotCommand, PartialEq, Debug)]
|
||||
/// enum TelegramAdminCommand {
|
||||
/// Ban,
|
||||
|
@ -108,6 +110,7 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn parse_command_with_args() {
|
||||
#[command(rename = "lowercase")]
|
||||
#[derive(BotCommand, Debug, PartialEq)]
|
||||
enum DefaultCommands {
|
||||
Start,
|
||||
|
@ -122,6 +125,7 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn attribute_prefix() {
|
||||
#[command(rename = "lowercase")]
|
||||
#[derive(BotCommand, Debug, PartialEq)]
|
||||
enum DefaultCommands {
|
||||
#[command(prefix = "!")]
|
||||
|
@ -137,6 +141,7 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn many_attributes() {
|
||||
#[command(rename = "lowercase")]
|
||||
#[derive(BotCommand, Debug, PartialEq)]
|
||||
enum DefaultCommands {
|
||||
#[command(prefix = "!", description = "desc")]
|
||||
|
@ -150,7 +155,7 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn global_attributes() {
|
||||
#[command(prefix = "!")]
|
||||
#[command(prefix = "!", rename = "lowercase")]
|
||||
#[derive(BotCommand, Debug, PartialEq)]
|
||||
enum DefaultCommands {
|
||||
#[command(prefix = "/")]
|
||||
|
|
|
@ -4,7 +4,8 @@ use syn::{Attribute, LitStr, Token};
|
|||
|
||||
pub enum BotCommandAttribute {
|
||||
Prefix,
|
||||
Description
|
||||
Description,
|
||||
RenameRule
|
||||
}
|
||||
|
||||
impl Parse for BotCommandAttribute {
|
||||
|
@ -13,6 +14,7 @@ impl Parse for BotCommandAttribute {
|
|||
match name_arg.to_string().as_str() {
|
||||
"prefix" => Ok(BotCommandAttribute::Prefix),
|
||||
"description" => Ok(BotCommandAttribute::Description),
|
||||
"rename" => Ok(BotCommandAttribute::RenameRule),
|
||||
_ => Err(syn::Error::new(name_arg.span(), "unexpected argument"))
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,38 +1,59 @@
|
|||
use crate::attr::{Attr, BotCommandAttribute};
|
||||
use std::convert::TryFrom;
|
||||
use crate::rename_rules::rename_by_rule;
|
||||
|
||||
pub struct Command {
|
||||
pub prefix: Option<String>,
|
||||
pub description: Option<String>,
|
||||
pub name: String,
|
||||
pub renamed: bool,
|
||||
}
|
||||
|
||||
impl Command {
|
||||
fn from_attrs(prefix: Option<&Attr>, description: Option<&Attr>) -> Self {
|
||||
let prefix = prefix.map(|attr| attr.value());
|
||||
let description = description.map(|attr| attr.value());
|
||||
pub fn try_from(attrs: &[Attr], name: &str) -> Result<Self, String> {
|
||||
let attrs = parse_attrs(attrs)?;
|
||||
let mut new_name = name.to_string();
|
||||
let mut renamed = false;
|
||||
|
||||
Self {
|
||||
let prefix = attrs.prefix;
|
||||
let description = attrs.description;
|
||||
let rename = attrs.rename;
|
||||
if let Some(rename_rule) = rename {
|
||||
new_name = rename_by_rule(name, &rename_rule);
|
||||
renamed = true;
|
||||
}
|
||||
Ok(Self {
|
||||
prefix,
|
||||
description
|
||||
}
|
||||
description,
|
||||
name: new_name,
|
||||
renamed,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<&[Attr]> for Command {
|
||||
type Error = String;
|
||||
|
||||
fn try_from(attrs: &[Attr]) -> Result<Self, Self::Error> {
|
||||
let mut prefix = None;
|
||||
let mut description = None;
|
||||
|
||||
for attr in attrs {
|
||||
match attr.name() {
|
||||
BotCommandAttribute::Prefix => prefix = Some(attr),
|
||||
BotCommandAttribute::Description => description = Some(attr),
|
||||
_ => return Err(format!("unexpected attribute")),
|
||||
}
|
||||
}
|
||||
|
||||
Ok(Self::from_attrs(prefix, description))
|
||||
}
|
||||
struct CommandAttrs {
|
||||
prefix: Option<String>,
|
||||
description: Option<String>,
|
||||
rename: Option<String>
|
||||
}
|
||||
|
||||
fn parse_attrs(attrs: &[Attr]) -> Result<CommandAttrs, String> {
|
||||
let mut prefix = None;
|
||||
let mut description = None;
|
||||
let mut rename_rule = None;
|
||||
|
||||
for attr in attrs {
|
||||
match attr.name() {
|
||||
BotCommandAttribute::Prefix => prefix = Some(attr.value()),
|
||||
BotCommandAttribute::Description => description = Some(attr.value()),
|
||||
BotCommandAttribute::RenameRule => rename_rule = Some(attr.value()),
|
||||
_ => return Err(format!("unexpected attribute")),
|
||||
}
|
||||
}
|
||||
|
||||
Ok(CommandAttrs {
|
||||
prefix,
|
||||
description,
|
||||
rename: rename_rule
|
||||
})
|
||||
}
|
|
@ -1,38 +1,57 @@
|
|||
use crate::attr::{Attr, BotCommandAttribute};
|
||||
use std::convert::TryFrom;
|
||||
use crate::rename_rules::rename_by_rule;
|
||||
|
||||
pub struct CommandEnum {
|
||||
pub prefix: Option<String>,
|
||||
pub description: Option<String>,
|
||||
pub rename_rule: Option<String>,
|
||||
}
|
||||
|
||||
impl CommandEnum {
|
||||
fn from_attrs(prefix: Option<&Attr>, description: Option<&Attr>) -> Self {
|
||||
let prefix = prefix.map(|attr| attr.value());
|
||||
let description = description.map(|attr| attr.value());
|
||||
pub fn try_from(attrs: &[Attr]) -> Result<Self, String> {
|
||||
let attrs = parse_attrs(attrs)?;
|
||||
|
||||
Self {
|
||||
prefix,
|
||||
description
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<&[Attr]> for CommandEnum {
|
||||
type Error = String;
|
||||
|
||||
fn try_from(attrs: &[Attr]) -> Result<Self, Self::Error> {
|
||||
let mut prefix = None;
|
||||
let mut description = None;
|
||||
|
||||
for attr in attrs {
|
||||
match attr.name() {
|
||||
BotCommandAttribute::Prefix => prefix = Some(attr),
|
||||
BotCommandAttribute::Description => description = Some(attr),
|
||||
_ => return Err(format!("unexpected attribute")),
|
||||
let prefix = attrs.prefix;
|
||||
let description = attrs.description;
|
||||
let rename = attrs.rename;
|
||||
if let Some(rename_rule) = &rename {
|
||||
match rename_rule.as_str() {
|
||||
"lowercase" => {},
|
||||
_ => return Err(format!("unallowed value")),
|
||||
}
|
||||
}
|
||||
|
||||
Ok(Self::from_attrs(prefix, description))
|
||||
Ok(Self {
|
||||
prefix,
|
||||
description,
|
||||
rename_rule: rename
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
struct CommandAttrs {
|
||||
prefix: Option<String>,
|
||||
description: Option<String>,
|
||||
rename: Option<String>
|
||||
}
|
||||
|
||||
fn parse_attrs(attrs: &[Attr]) -> Result<CommandAttrs, String> {
|
||||
let mut prefix = None;
|
||||
let mut description = None;
|
||||
let mut rename_rule = None;
|
||||
|
||||
for attr in attrs {
|
||||
match attr.name() {
|
||||
BotCommandAttribute::Prefix => prefix = Some(attr.value()),
|
||||
BotCommandAttribute::Description => description = Some(attr.value()),
|
||||
BotCommandAttribute::RenameRule => rename_rule = Some(attr.value()),
|
||||
_ => return Err(format!("unexpected attribute")),
|
||||
}
|
||||
}
|
||||
|
||||
Ok(CommandAttrs {
|
||||
prefix,
|
||||
description,
|
||||
rename: rename_rule
|
||||
})
|
||||
}
|
|
@ -1,6 +1,7 @@
|
|||
mod attr;
|
||||
mod command;
|
||||
mod enum_attributes;
|
||||
mod rename_rules;
|
||||
|
||||
extern crate proc_macro;
|
||||
extern crate syn;
|
||||
|
@ -12,27 +13,24 @@ use crate::command::Command;
|
|||
use std::convert::TryFrom;
|
||||
use crate::attr::{Attr, VecAttrs};
|
||||
use crate::enum_attributes::CommandEnum;
|
||||
use crate::rename_rules::rename_by_rule;
|
||||
|
||||
macro_rules! get_or_return {
|
||||
($some:tt) => {
|
||||
match $some {
|
||||
Ok(elem) => elem,
|
||||
Err(e) => return e
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
#[proc_macro_derive(BotCommand, attributes(command))]
|
||||
pub fn derive_telegram_command_enum(tokens: TokenStream) -> TokenStream {
|
||||
let input = parse_macro_input!(tokens as DeriveInput);
|
||||
|
||||
let data_enum = match &input.data {
|
||||
syn::Data::Enum(data) => data,
|
||||
_ => return compile_error("TelegramBotCommand allowed only for enums")
|
||||
};
|
||||
let data_enum: &syn::DataEnum = get_or_return!(get_enum_data(&input));
|
||||
|
||||
let mut enum_attrs = Vec::new();
|
||||
for attr in &input.attrs {
|
||||
match attr.parse_args::<VecAttrs>() {
|
||||
Ok(mut attrs_) => {
|
||||
enum_attrs.append(attrs_.data.as_mut());
|
||||
},
|
||||
Err(e) => {
|
||||
return compile_error(e.to_compile_error());
|
||||
},
|
||||
}
|
||||
}
|
||||
let mut enum_attrs: Vec<Attr> = get_or_return!(parse_attributes(&input.attrs));
|
||||
|
||||
let command_enum = match CommandEnum::try_from(enum_attrs.as_slice()) {
|
||||
Ok(command_enum) => command_enum,
|
||||
|
@ -54,14 +52,24 @@ pub fn derive_telegram_command_enum(tokens: TokenStream) -> TokenStream {
|
|||
},
|
||||
}
|
||||
}
|
||||
match Command::try_from(attrs.as_slice()) {
|
||||
match Command::try_from(attrs.as_slice(), &variant.ident.to_string()) {
|
||||
Ok(command) => variant_infos.push(command),
|
||||
Err(e) => return compile_error(e),
|
||||
}
|
||||
}
|
||||
|
||||
let variant_ident = variants.iter().map(|variant| &variant.ident);
|
||||
let variant_name = variants.iter().map(|variant| variant.ident.to_string().to_lowercase());
|
||||
let variant_name = variant_infos.iter().map(|info| {
|
||||
if info.renamed {
|
||||
info.name.clone()
|
||||
}
|
||||
else if let Some(rename_rule) = &command_enum.rename_rule {
|
||||
rename_by_rule(&info.name, rename_rule)
|
||||
}
|
||||
else {
|
||||
info.name.clone()
|
||||
}
|
||||
});
|
||||
let variant_prefixes = variant_infos.iter().map(|info| {
|
||||
if let Some(prefix) = &info.prefix {
|
||||
prefix
|
||||
|
@ -100,6 +108,28 @@ pub fn derive_telegram_command_enum(tokens: TokenStream) -> TokenStream {
|
|||
tokens
|
||||
}
|
||||
|
||||
fn get_enum_data(input: &DeriveInput) -> Result<&syn::DataEnum, TokenStream> {
|
||||
match &input.data {
|
||||
syn::Data::Enum(data) => Ok(data),
|
||||
_ => Err(compile_error("TelegramBotCommand allowed only for enums"))
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_attributes(input: &Vec<syn::Attribute>) -> Result<Vec<Attr>, TokenStream> {
|
||||
let mut enum_attrs = Vec::new();
|
||||
for attr in &input.attrs {
|
||||
match attr.parse_args::<VecAttrs>() {
|
||||
Ok(mut attrs_) => {
|
||||
enum_attrs.append(attrs_.data.as_mut());
|
||||
},
|
||||
Err(e) => {
|
||||
return Err(compile_error(e.to_compile_error()));
|
||||
},
|
||||
}
|
||||
}
|
||||
Ok(enum_attrs)
|
||||
}
|
||||
|
||||
fn compile_error<T>(data: T) -> TokenStream
|
||||
where
|
||||
T: ToTokens
|
||||
|
|
6
teloxide-macros/src/rename_rules.rs
Normal file
6
teloxide-macros/src/rename_rules.rs
Normal file
|
@ -0,0 +1,6 @@
|
|||
pub fn rename_by_rule(input: &str, rule: &str) -> String {
|
||||
match rule {
|
||||
"lowercase" => input.to_string().to_lowercase(),
|
||||
_ => rule.to_string(),
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue