#![feature(proc_macro, proc_macro_lib)]
#![recursion_limit="150"]
#[macro_use]
extern crate log;
extern crate proc_macro;
#[macro_use]
extern crate quote;
extern crate syn;
use proc_macro::TokenStream;
use std::collections::BTreeMap;
#[proc_macro_derive(TelegramFunction)]
pub fn derive_TelegramFunction(input: TokenStream) -> TokenStream {
let ast = syn::parse_macro_input(&input.to_string()).unwrap();
let expanded = expand(ast);
expanded.to_string().parse().unwrap()
}
fn expand(mut ast: syn::MacroInput) -> quote::Tokens {
let config = config_from(&ast.attrs, &["function", "answer"]);
let function = config.get("function").unwrap();
let function = syn::Lit::Str((*function).clone(), syn::StrStyle::Cooked);
let bot_function = syn::Ident::from(config.get("bot_function").unwrap().as_str());
let answer = syn::Ident::from(config.get("answer").unwrap().as_str());
let fields: Vec<_> = match ast.body {
syn::Body::Struct(syn::VariantData::Struct(ref fields)) => {
fields.iter().map(|f| (f.ident.as_ref().unwrap(), &f.ty)).collect()
},
syn::Body::Struct(syn::VariantData::Unit) => {
vec![]
},
_ => panic!("#[derive(getters)] can only be used with braced structs"),
};
let name = &ast.ident;
let (impl_generics, ty_generics, where_clause) = ast.generics
.split_for_impl();
let is_option_ident = |ref f: &(&syn::Ident, &syn::Ty)| -> bool {
match *f.1 {
syn::Ty::Path(_, ref path) => {
match path.segments.first().unwrap().ident.as_ref() {
"Option" => true,
_ => false
}
},
_ => false
}
};
let field_compulsory: Vec<_> = fields.iter().filter(|f| !is_option_ident(&f))
.map(|f| syn::Ident::from(format!("_{}", f.0.as_ref()))).collect();
let field_optional: Vec<_> = fields.iter().filter(|f| is_option_ident(&f)).map(|f| f.0).collect();
let field_optional2 = field_optional.clone();
let field_compulsory2: Vec<_> = fields.iter().map(|f| f.0).collect();
let field_compulsory3 = field_compulsory.clone();
let values: Vec<_> = fields.iter().map(|f| {
match *f.1 {
syn::Ty::Path(_, ref path) => {
match path.segments.first().unwrap().ident.as_ref() {
"Option" => return syn::Ident::from("None"),
_ => return syn::Ident::from(format!("_{}", f.0.as_ref()))
}
},
_ => return syn::Ident::from("None")
}
return "".into();
}).collect();
let ty_compulsory: Vec<_> = fields.iter().map(|f| f.1).collect();
let ty_compulsory2 = ty_compulsory.clone();
let ty_optional: Vec<_> = fields.iter().filter(|f| is_option_ident(&f)).map(|f| {
if let syn::Ty::Path(_, ref path) = *f.1 {
if let syn::PathParameters::AngleBracketed(ref param) = path.segments.first().unwrap().parameters {
if let &syn::Ty::Path(_, ref path) = param.types.first().unwrap() {
return (*path).clone();
}
}
}
panic!("REACHED");
}).collect();
let trait_name = syn::Ident::from(format!("Function{}", name.as_ref()));
let wrapper_name = syn::Ident::from(format!("Wrapper{}", name.as_ref()));
quote! {
pub struct #wrapper_name {
bot: Rc<Bot>,
inner: #name
}
impl #wrapper_name {
pub fn send<'a>(self) -> impl Future<Item=(RcBot, objects::#answer), Error=Error> + 'a {
let msg = serde_json::to_string(&self.inner).unwrap();
Bot::fetch_json(&self.bot.key.clone(), self.bot.handle.clone(), #function, &msg).map(move |x| (RcBot { inner: self.bot.clone() }, serde_json::from_str::<objects::#answer>(&x).unwrap()))
}
pub fn send_with<'b,T: io::Read + 'b>(self, file_name: &str, file: T) -> impl Future<Item=(RcBot, objects::#answer), Error=Error> + 'b{
let msg = serde_json::to_value(&self.inner);
Bot::fetch_formdata(&self.bot.key.clone(), self.bot.handle.clone(), #function, msg, file, file_name).map(move |x| (RcBot { inner: self.bot.clone() }, serde_json::from_str::<objects::#answer>(&x).unwrap()))
}
#(
pub fn #field_optional<S>(mut self, val: S) -> Self where S: Into<#ty_optional> {
self.inner.#field_optional2 = Some(val.into());
self
}
)*
}
pub trait #trait_name {
fn #bot_function(&self, #( #field_compulsory: #ty_compulsory, )*) -> #wrapper_name;
}
impl #trait_name for RcBot{
fn #bot_function(&self, #( #field_compulsory3: #ty_compulsory2, )*) -> #wrapper_name {
#wrapper_name { inner: #name { #( #field_compulsory2: #values, )* }, bot: self.inner.clone() }
}
}
}
}
fn extract_attrs(attrs: &mut Vec<syn::Attribute>,
name: &str)
-> Vec<syn::Attribute> {
let extracted =
attrs.iter().filter(|a| a.name() == name).cloned().collect();
attrs.retain(|a| a.name() != name);
extracted
}
fn config_from(attrs: &[syn::Attribute],
keys: &[&str])
-> BTreeMap<String, String> {
let mut result = BTreeMap::new();
for attr in attrs {
if let syn::MetaItem::NameValue(ref name, ref value) = attr.value {
let name = format!("{}", name);
let value = match value.clone() {
syn::Lit::Str(value, _) => value,
_ => panic!("bla")
};
result.insert(name, value);
}
}
result
}