mirror of
https://github.com/teloxide/teloxide.git
synced 2024-12-22 22:46:39 +01:00
refactor imports and stuff
This commit is contained in:
parent
5d39e3c06d
commit
5477535834
6 changed files with 164 additions and 162 deletions
|
@ -1,10 +1,10 @@
|
||||||
|
use crate::Result;
|
||||||
|
|
||||||
use syn::{
|
use syn::{
|
||||||
parse::{Parse, ParseBuffer, ParseStream},
|
parse::{Parse, ParseBuffer, ParseStream},
|
||||||
Attribute, LitStr, Token,
|
Attribute, LitStr, Token,
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::Result;
|
|
||||||
|
|
||||||
pub(crate) enum CommandAttrName {
|
pub(crate) enum CommandAttrName {
|
||||||
Prefix,
|
Prefix,
|
||||||
Description,
|
Description,
|
||||||
|
|
148
src/bot_commands.rs
Normal file
148
src/bot_commands.rs
Normal file
|
@ -0,0 +1,148 @@
|
||||||
|
use crate::{
|
||||||
|
attr::CommandAttrs, command::Command, command_enum::CommandEnum,
|
||||||
|
compile_error, fields_parse::impl_parse_args, unzip::Unzip, Result,
|
||||||
|
};
|
||||||
|
|
||||||
|
use proc_macro2::TokenStream;
|
||||||
|
use quote::quote;
|
||||||
|
use syn::DeriveInput;
|
||||||
|
|
||||||
|
pub(crate) fn bot_commands_impl(input: DeriveInput) -> Result<TokenStream> {
|
||||||
|
let data_enum = get_enum_data(&input)?;
|
||||||
|
let enum_attrs = CommandAttrs::from_attributes(&input.attrs)?;
|
||||||
|
let command_enum = CommandEnum::try_from(enum_attrs)?;
|
||||||
|
|
||||||
|
let Unzip(var_init, var_info) = data_enum
|
||||||
|
.variants
|
||||||
|
.iter()
|
||||||
|
.map(|variant| {
|
||||||
|
let attrs = CommandAttrs::from_attributes(&variant.attrs)?;
|
||||||
|
let command = Command::try_from(attrs, &variant.ident.to_string())?;
|
||||||
|
|
||||||
|
let variant_name = &variant.ident;
|
||||||
|
let self_variant = quote! { Self::#variant_name };
|
||||||
|
|
||||||
|
let parser =
|
||||||
|
command.parser.as_ref().unwrap_or(&command_enum.parser_type);
|
||||||
|
let parse = impl_parse_args(&variant.fields, self_variant, parser);
|
||||||
|
|
||||||
|
Ok((parse, command))
|
||||||
|
})
|
||||||
|
.collect::<Result<Unzip<Vec<_>, Vec<_>>>>()?;
|
||||||
|
|
||||||
|
let type_name = &input.ident;
|
||||||
|
let fn_descriptions = impl_descriptions(&var_info, &command_enum);
|
||||||
|
let fn_parse = impl_parse(&var_info, &command_enum, &var_init);
|
||||||
|
let fn_commands = impl_commands(&var_info, &command_enum);
|
||||||
|
|
||||||
|
let trait_impl = quote! {
|
||||||
|
impl BotCommands for #type_name {
|
||||||
|
#fn_descriptions
|
||||||
|
#fn_parse
|
||||||
|
#fn_commands
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok(TokenStream::from(trait_impl))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn impl_commands(
|
||||||
|
infos: &[Command],
|
||||||
|
global: &CommandEnum,
|
||||||
|
) -> proc_macro2::TokenStream {
|
||||||
|
let commands = infos
|
||||||
|
.iter()
|
||||||
|
.filter(|command| command.description_is_enabled())
|
||||||
|
.map(|command| {
|
||||||
|
let c = command.get_matched_value(global);
|
||||||
|
let d = command.description.as_deref().unwrap_or_default();
|
||||||
|
quote! { BotCommand::new(#c,#d) }
|
||||||
|
});
|
||||||
|
|
||||||
|
quote! {
|
||||||
|
fn bot_commands() -> Vec<teloxide::types::BotCommand> {
|
||||||
|
use teloxide::types::BotCommand;
|
||||||
|
vec![#(#commands),*]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn impl_descriptions(
|
||||||
|
infos: &[Command],
|
||||||
|
global: &CommandEnum,
|
||||||
|
) -> proc_macro2::TokenStream {
|
||||||
|
let command_descriptions = infos
|
||||||
|
.iter()
|
||||||
|
.filter(|command| command.description_is_enabled())
|
||||||
|
.map(|c| {
|
||||||
|
let (prefix, command) = c.get_matched_value2(global);
|
||||||
|
let description = c.description.clone().unwrap_or_default();
|
||||||
|
quote! { CommandDescription { prefix: #prefix, command: #command, description: #description } }
|
||||||
|
});
|
||||||
|
|
||||||
|
let global_description = match global.description.as_deref() {
|
||||||
|
Some(gd) => quote! { .global_description(#gd) },
|
||||||
|
None => quote! {},
|
||||||
|
};
|
||||||
|
|
||||||
|
quote! {
|
||||||
|
fn descriptions() -> teloxide::utils::command::CommandDescriptions<'static> {
|
||||||
|
use teloxide::utils::command::{CommandDescriptions, CommandDescription};
|
||||||
|
use std::borrow::Cow;
|
||||||
|
|
||||||
|
CommandDescriptions::new(&[
|
||||||
|
#(#command_descriptions),*
|
||||||
|
])
|
||||||
|
#global_description
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn impl_parse(
|
||||||
|
infos: &[Command],
|
||||||
|
global: &CommandEnum,
|
||||||
|
variants_initialization: &[proc_macro2::TokenStream],
|
||||||
|
) -> proc_macro2::TokenStream {
|
||||||
|
let matching_values = infos.iter().map(|c| c.get_matched_value(global));
|
||||||
|
|
||||||
|
quote! {
|
||||||
|
fn parse<N>(s: &str, bot_name: N) -> Result<Self, teloxide::utils::command::ParseError>
|
||||||
|
where
|
||||||
|
N: Into<String>
|
||||||
|
{
|
||||||
|
// FIXME: we should probably just call a helper function from `teloxide`, instead of parsing command syntax ourselves
|
||||||
|
use std::str::FromStr;
|
||||||
|
use teloxide::utils::command::ParseError;
|
||||||
|
|
||||||
|
// 2 is used to only split once (=> in two parts),
|
||||||
|
// we only need to split the command and the rest of arguments.
|
||||||
|
let mut words = s.splitn(2, ' ');
|
||||||
|
|
||||||
|
// Unwrap: split iterators always have at least one item
|
||||||
|
let mut full_command = words.next().unwrap().split('@');
|
||||||
|
let command = full_command.next().unwrap();
|
||||||
|
|
||||||
|
let bot_username = full_command.next();
|
||||||
|
match bot_username {
|
||||||
|
None => {}
|
||||||
|
Some(username) if username.eq_ignore_ascii_case(&bot_name.into()) => {}
|
||||||
|
Some(n) => return Err(ParseError::WrongBotName(n.to_owned())),
|
||||||
|
}
|
||||||
|
|
||||||
|
let args = words.next().unwrap_or("").to_owned();
|
||||||
|
match command {
|
||||||
|
#(
|
||||||
|
#matching_values => Ok(#variants_initialization),
|
||||||
|
)*
|
||||||
|
_ => Err(ParseError::UnknownCommand(command.to_owned())),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_enum_data(input: &DeriveInput) -> Result<&syn::DataEnum> {
|
||||||
|
match &input.data {
|
||||||
|
syn::Data::Enum(data) => Ok(data),
|
||||||
|
_ => Err(compile_error("`BotCommands` is only allowed for enums")),
|
||||||
|
}
|
||||||
|
}
|
|
@ -13,9 +13,9 @@ where
|
||||||
Error(TokenStream::from(quote! { compile_error! { #data } }))
|
Error(TokenStream::from(quote! { compile_error! { #data } }))
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<Error> for proc_macro::TokenStream {
|
impl From<Error> for proc_macro2::TokenStream {
|
||||||
fn from(Error(e): Error) -> Self {
|
fn from(Error(e): Error) -> Self {
|
||||||
e.into()
|
e
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,3 @@
|
||||||
extern crate quote;
|
|
||||||
|
|
||||||
use quote::quote;
|
use quote::quote;
|
||||||
use syn::{Fields, FieldsNamed, FieldsUnnamed, Type};
|
use syn::{Fields, FieldsNamed, FieldsUnnamed, Type};
|
||||||
|
|
||||||
|
|
164
src/lib.rs
164
src/lib.rs
|
@ -1,6 +1,9 @@
|
||||||
// TODO: refactor this shit.
|
// TODO: refactor this shit.
|
||||||
|
|
||||||
|
extern crate proc_macro;
|
||||||
|
|
||||||
mod attr;
|
mod attr;
|
||||||
|
mod bot_commands;
|
||||||
mod command;
|
mod command;
|
||||||
mod command_enum;
|
mod command_enum;
|
||||||
mod error;
|
mod error;
|
||||||
|
@ -8,162 +11,15 @@ mod fields_parse;
|
||||||
mod rename_rules;
|
mod rename_rules;
|
||||||
mod unzip;
|
mod unzip;
|
||||||
|
|
||||||
extern crate proc_macro;
|
pub(crate) use error::{compile_error, Result};
|
||||||
extern crate quote;
|
use syn::{parse_macro_input, DeriveInput};
|
||||||
extern crate syn;
|
|
||||||
use crate::{
|
|
||||||
attr::CommandAttrs, command::Command, command_enum::CommandEnum,
|
|
||||||
fields_parse::impl_parse_args, unzip::Unzip,
|
|
||||||
};
|
|
||||||
use proc_macro::TokenStream;
|
|
||||||
use quote::quote;
|
|
||||||
use syn::DeriveInput;
|
|
||||||
|
|
||||||
pub(crate) use error::{compile_error, Error, Result};
|
use crate::bot_commands::bot_commands_impl;
|
||||||
|
use proc_macro::TokenStream;
|
||||||
|
|
||||||
#[proc_macro_derive(BotCommands, attributes(command))]
|
#[proc_macro_derive(BotCommands, attributes(command))]
|
||||||
pub fn bot_commands_derive(tokens: TokenStream) -> TokenStream {
|
pub fn bot_commands_derive(tokens: TokenStream) -> TokenStream {
|
||||||
bot_commands_impl(tokens).unwrap_or_else(Error::into)
|
let input = parse_macro_input!(tokens as DeriveInput);
|
||||||
}
|
|
||||||
|
bot_commands_impl(input).unwrap_or_else(<_>::into).into()
|
||||||
fn bot_commands_impl(tokens: TokenStream) -> Result<TokenStream, Error> {
|
|
||||||
let input = syn::parse_macro_input::parse::<DeriveInput>(tokens)?;
|
|
||||||
|
|
||||||
let data_enum = get_enum_data(&input)?;
|
|
||||||
let enum_attrs = CommandAttrs::from_attributes(&input.attrs)?;
|
|
||||||
let command_enum = CommandEnum::try_from(enum_attrs)?;
|
|
||||||
|
|
||||||
let Unzip(var_init, var_info) = data_enum
|
|
||||||
.variants
|
|
||||||
.iter()
|
|
||||||
.map(|variant| {
|
|
||||||
let attrs = CommandAttrs::from_attributes(&variant.attrs)?;
|
|
||||||
let command = Command::try_from(attrs, &variant.ident.to_string())?;
|
|
||||||
|
|
||||||
let variant_name = &variant.ident;
|
|
||||||
let self_variant = quote! { Self::#variant_name };
|
|
||||||
|
|
||||||
let parser =
|
|
||||||
command.parser.as_ref().unwrap_or(&command_enum.parser_type);
|
|
||||||
let parse = impl_parse_args(&variant.fields, self_variant, parser);
|
|
||||||
|
|
||||||
Ok((parse, command))
|
|
||||||
})
|
|
||||||
.collect::<Result<Unzip<Vec<_>, Vec<_>>, Error>>()?;
|
|
||||||
|
|
||||||
let type_name = &input.ident;
|
|
||||||
let fn_descriptions = impl_descriptions(&var_info, &command_enum);
|
|
||||||
let fn_parse = impl_parse(&var_info, &command_enum, &var_init);
|
|
||||||
let fn_commands = impl_commands(&var_info, &command_enum);
|
|
||||||
|
|
||||||
let trait_impl = quote! {
|
|
||||||
impl BotCommands for #type_name {
|
|
||||||
#fn_descriptions
|
|
||||||
#fn_parse
|
|
||||||
#fn_commands
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
Ok(TokenStream::from(trait_impl))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn impl_commands(
|
|
||||||
infos: &[Command],
|
|
||||||
global: &CommandEnum,
|
|
||||||
) -> proc_macro2::TokenStream {
|
|
||||||
let commands = infos
|
|
||||||
.iter()
|
|
||||||
.filter(|command| command.description_is_enabled())
|
|
||||||
.map(|command| {
|
|
||||||
let c = command.get_matched_value(global);
|
|
||||||
let d = command.description.as_deref().unwrap_or_default();
|
|
||||||
quote! { BotCommand::new(#c,#d) }
|
|
||||||
});
|
|
||||||
|
|
||||||
quote! {
|
|
||||||
fn bot_commands() -> Vec<teloxide::types::BotCommand> {
|
|
||||||
use teloxide::types::BotCommand;
|
|
||||||
vec![#(#commands),*]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn impl_descriptions(
|
|
||||||
infos: &[Command],
|
|
||||||
global: &CommandEnum,
|
|
||||||
) -> proc_macro2::TokenStream {
|
|
||||||
let command_descriptions = infos
|
|
||||||
.iter()
|
|
||||||
.filter(|command| command.description_is_enabled())
|
|
||||||
.map(|c| {
|
|
||||||
let (prefix, command) = c.get_matched_value2(global);
|
|
||||||
let description = c.description.clone().unwrap_or_default();
|
|
||||||
quote! { CommandDescription { prefix: #prefix, command: #command, description: #description } }
|
|
||||||
});
|
|
||||||
|
|
||||||
let global_description = match global.description.as_deref() {
|
|
||||||
Some(gd) => quote! { .global_description(#gd) },
|
|
||||||
None => quote! {},
|
|
||||||
};
|
|
||||||
|
|
||||||
quote! {
|
|
||||||
fn descriptions() -> teloxide::utils::command::CommandDescriptions<'static> {
|
|
||||||
use teloxide::utils::command::{CommandDescriptions, CommandDescription};
|
|
||||||
use std::borrow::Cow;
|
|
||||||
|
|
||||||
CommandDescriptions::new(&[
|
|
||||||
#(#command_descriptions),*
|
|
||||||
])
|
|
||||||
#global_description
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn impl_parse(
|
|
||||||
infos: &[Command],
|
|
||||||
global: &CommandEnum,
|
|
||||||
variants_initialization: &[proc_macro2::TokenStream],
|
|
||||||
) -> proc_macro2::TokenStream {
|
|
||||||
let matching_values = infos.iter().map(|c| c.get_matched_value(global));
|
|
||||||
|
|
||||||
quote! {
|
|
||||||
fn parse<N>(s: &str, bot_name: N) -> Result<Self, teloxide::utils::command::ParseError>
|
|
||||||
where
|
|
||||||
N: Into<String>
|
|
||||||
{
|
|
||||||
// FIXME: we should probably just call a helper function from `teloxide`, instead of parsing command syntax ourselves
|
|
||||||
use std::str::FromStr;
|
|
||||||
use teloxide::utils::command::ParseError;
|
|
||||||
|
|
||||||
// 2 is used to only split once (=> in two parts),
|
|
||||||
// we only need to split the command and the rest of arguments.
|
|
||||||
let mut words = s.splitn(2, ' ');
|
|
||||||
|
|
||||||
// Unwrap: split iterators always have at least one item
|
|
||||||
let mut full_command = words.next().unwrap().split('@');
|
|
||||||
let command = full_command.next().unwrap();
|
|
||||||
|
|
||||||
let bot_username = full_command.next();
|
|
||||||
match bot_username {
|
|
||||||
None => {}
|
|
||||||
Some(username) if username.eq_ignore_ascii_case(&bot_name.into()) => {}
|
|
||||||
Some(n) => return Err(ParseError::WrongBotName(n.to_owned())),
|
|
||||||
}
|
|
||||||
|
|
||||||
let args = words.next().unwrap_or("").to_owned();
|
|
||||||
match command {
|
|
||||||
#(
|
|
||||||
#matching_values => Ok(#variants_initialization),
|
|
||||||
)*
|
|
||||||
_ => Err(ParseError::UnknownCommand(command.to_owned())),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_enum_data(input: &DeriveInput) -> Result<&syn::DataEnum> {
|
|
||||||
match &input.data {
|
|
||||||
syn::Data::Enum(data) => Ok(data),
|
|
||||||
_ => Err(compile_error("`BotCommands` is only allowed for enums")),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,12 +1,12 @@
|
||||||
// Some concepts are from Serde.
|
// Some concepts are from Serde.
|
||||||
|
|
||||||
|
use crate::error::{compile_error, Result};
|
||||||
|
|
||||||
use heck::{
|
use heck::{
|
||||||
ToKebabCase, ToLowerCamelCase, ToPascalCase, ToShoutyKebabCase,
|
ToKebabCase, ToLowerCamelCase, ToPascalCase, ToShoutyKebabCase,
|
||||||
ToShoutySnakeCase, ToSnakeCase,
|
ToShoutySnakeCase, ToSnakeCase,
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::error::{compile_error, Result};
|
|
||||||
|
|
||||||
#[derive(Copy, Clone, Debug)]
|
#[derive(Copy, Clone, Debug)]
|
||||||
pub(crate) enum RenameRule {
|
pub(crate) enum RenameRule {
|
||||||
/// -> `lowercase`
|
/// -> `lowercase`
|
||||||
|
|
Loading…
Reference in a new issue