Migrate teloxide-macros to the separate repository

This commit is contained in:
Temirkhan Myrzamadi 2020-02-24 14:50:21 +06:00
parent 4aeddbd7b4
commit 825c2c7499
7 changed files with 0 additions and 378 deletions

View file

@ -1,16 +0,0 @@
[package]
name = "teloxide-macros"
version = "0.1.2"
description = "The teloxide's macros for internal usage"
authors = ["p0lunin <dmytro.polunin@gmail.com>"]
license = "MIT"
edition = "2018"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
quote = "1.0.2"
syn = "1.0.13"
[lib]
proc-macro = true

View file

@ -1,22 +0,0 @@
MIT License
Copyright (c) 2019-2020 teloxide
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View file

@ -1,64 +0,0 @@
use syn::{
parse::{Parse, ParseStream},
LitStr, Token,
};
pub enum BotCommandAttribute {
Prefix,
Description,
RenameRule,
}
impl Parse for BotCommandAttribute {
fn parse(input: ParseStream) -> Result<Self, syn::Error> {
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),
_ => Err(syn::Error::new(name_arg.span(), "unexpected argument")),
}
}
}
pub struct Attr {
name: BotCommandAttribute,
value: String,
}
impl Parse for Attr {
fn parse(input: ParseStream) -> Result<Self, syn::Error> {
let name = input.parse::<BotCommandAttribute>()?;
input.parse::<Token![=]>()?;
let value = input.parse::<LitStr>()?.value();
Ok(Self { name, value })
}
}
impl Attr {
pub fn name(&self) -> &BotCommandAttribute {
&self.name
}
pub fn value(&self) -> String {
self.value.clone()
}
}
pub struct VecAttrs {
pub data: Vec<Attr>,
}
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 })
}
}

View file

@ -1,54 +0,0 @@
use crate::{
attr::{Attr, BotCommandAttribute},
rename_rules::rename_by_rule,
};
pub struct Command {
pub prefix: Option<String>,
pub description: Option<String>,
pub name: String,
pub renamed: bool,
}
impl Command {
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;
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, name: new_name, renamed })
}
}
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()),
#[allow(unreachable_patterns)]
_ => return Err("unexpected attribute".to_owned()),
}
}
Ok(CommandAttrs { prefix, description, rename: rename_rule })
}

View file

@ -1,50 +0,0 @@
use crate::attr::{Attr, BotCommandAttribute};
pub struct CommandEnum {
pub prefix: Option<String>,
pub description: Option<String>,
pub rename_rule: Option<String>,
}
impl CommandEnum {
pub fn try_from(attrs: &[Attr]) -> Result<Self, String> {
let attrs = parse_attrs(attrs)?;
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("disallowed value".to_owned()),
}
}
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()),
#[allow(unreachable_patterns)]
_ => return Err("unexpected attribute".to_owned()),
}
}
Ok(CommandAttrs { prefix, description, rename: rename_rule })
}

View file

@ -1,166 +0,0 @@
mod attr;
mod command;
mod enum_attributes;
mod rename_rules;
extern crate proc_macro;
extern crate syn;
use crate::{
attr::{Attr, VecAttrs},
command::Command,
enum_attributes::CommandEnum,
rename_rules::rename_by_rule,
};
use proc_macro::TokenStream;
use quote::{quote, ToTokens};
use syn::{parse_macro_input, DeriveInput};
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: &syn::DataEnum = get_or_return!(get_enum_data(&input));
let 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,
Err(e) => return compile_error(e),
};
let variants: Vec<&syn::Variant> =
data_enum.variants.iter().map(|attr| attr).collect();
let mut variant_infos = vec![];
for variant in variants.iter() {
let mut attrs = Vec::new();
for attr in &variant.attrs {
match attr.parse_args::<VecAttrs>() {
Ok(mut attrs_) => {
attrs.append(attrs_.data.as_mut());
}
Err(e) => {
return compile_error(e.to_compile_error());
}
}
}
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 = 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
} else if let Some(prefix) = &command_enum.prefix {
prefix
} else {
"/"
}
});
let variant_str1 = variant_prefixes
.zip(variant_name)
.map(|(prefix, command)| prefix.to_string() + command.as_str());
let variant_str2 = variant_str1.clone();
let variant_description = variant_infos.iter().map(|info| {
info.description
.as_deref()
.map(|e| format!(" - {}", e))
.unwrap_or(String::new())
});
let ident = &input.ident;
let global_description = if let Some(s) = &command_enum.description {
quote! { #s, "\n", }
} else {
quote! {}
};
let expanded = quote! {
impl BotCommand for #ident {
fn try_from(value: &str) -> Option<Self> {
match value {
#(
#variant_str1 => Some(Self::#variant_ident),
)*
_ => None
}
}
fn descriptions() -> String {
std::concat!(#global_description #(#variant_str2, #variant_description, '\n'),*).to_string()
}
fn parse<N>(s: &str, bot_name: N) -> Option<(Self, Vec<&str>)>
where
N: Into<String>
{
let mut words = s.split_whitespace();
let mut splited = words.next()?.split('@');
let command_raw = splited.next()?;
let bot = splited.next();
let bot_name = bot_name.into();
match bot {
Some(name) if name == bot_name => {}
None => {}
_ => return None,
}
let command = Self::try_from(command_raw)?;
Some((command, words.collect()))
}
}
};
//for debug
//println!("{}", &expanded.to_string());
TokenStream::from(expanded)
}
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: &[syn::Attribute],
) -> Result<Vec<Attr>, TokenStream> {
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());
}
Err(e) => {
return Err(compile_error(e.to_compile_error()));
}
}
}
Ok(enum_attrs)
}
fn compile_error<T>(data: T) -> TokenStream
where
T: ToTokens,
{
TokenStream::from(quote! { compile_error!(#data) })
}

View file

@ -1,6 +0,0 @@
pub fn rename_by_rule(input: &str, rule: &str) -> String {
match rule {
"lowercase" => input.to_string().to_lowercase(),
_ => rule.to_string(),
}
}