telebot-derive 0.0.1

Getters and setters for the telebot library
Documentation
#![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"),
    };

    
    /*for field in &fields {
        println!("{:?}", field.1);
    }*/
    
    let name = &ast.ident;
    let (impl_generics, ty_generics, where_clause) = ast.generics
        .split_for_impl();
    //let getter: Vec<_> = fields.iter().map(|f| f.0).collect();
    
    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();

    //println!("{:?}", ty_optional.first());

    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
}