1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
extern crate proc_macro;
use proc_macro::{TokenStream, TokenTree};
use quote::quote;
use syn::{parse_macro_input, DeriveInput};
use quote::ToTokens;
//use serde::{Serialize, Deserialize};

#[proc_macro_attribute]
pub fn qan_export(args: TokenStream, input: TokenStream) -> TokenStream {
    let mut boilerplate = format!(r#"
    #[no_mangle]
    {}
    "#, input);
    let parsed_fn: syn::ItemFn = parse_macro_input!(input);
    let mut should_wrap = false;
    let items: Vec<&syn::FnArg> = parsed_fn.sig.inputs.iter().collect();
    if items.len() > 0 {
        boilerplate += "\n#[derive(Serialize, Deserialize)]";
        boilerplate += format!("\npub struct _{}_wrapped%lifetime% {{", parsed_fn.sig.ident).as_ref();
    }
    for input in &items {
        match input {
            syn::FnArg::Typed(p) => {
                let pat_id = *p.pat.clone();
                let param_name = match pat_id {
                    syn::Pat::Ident(t) => t.ident,
                    _ => continue
                };
                let pat_ty = *p.ty.clone();
                let param_type = match pat_ty {
                    syn::Type::Path(t) => {
                        let param_type = &t.path.segments[t.path.segments.len()-1].ident;
                        should_wrap = match param_type.to_string().as_ref() {
                            "i32" => false,
                            "i64" => false,
                            "f32" => false,
                            "f64" => false,
                            "String" => true,
                            "Address" => false,
                            "DateTime" => false,
                            "Customer" => true,
                            _ => false
                        };
                        //if should_wrap { boilerplate += "\n#[serde(borrow)]"; } 
                        boilerplate += format!("\n{}: {}", param_name, param_type).as_ref();
                        if should_wrap {
                            boilerplate = boilerplate.replace("%lifetime%", "");
                            boilerplate += "";
                        }
                        boilerplate += ",";
                    },
                    _ => continue
                };
            },
            _ => {}
        }
    }
    boilerplate = boilerplate.replace("%lifetime%", "");
    if items.len() > 0 {
        boilerplate += "\n}\n";
    }
    if should_wrap {
        let mut ret_type = String::new();
        boilerplate += format!("#[no_mangle]\npub fn _{}(len: i32) -> Vec<u8>", 
                           parsed_fn.sig.ident).as_ref();
        match parsed_fn.sig.output {
            syn::ReturnType::Default => {},
            syn::ReturnType::Type(_, bt) => {
                match *bt {
                    // TODO what to do with other types?
                    syn::Type::Path(t) => {
                        ret_type = t.path.segments[t.path.segments.len()-1].ident.to_string();
                        //boilerplate += format!("-> {}", ret_type).as_ref();
                    },
                    _ => {}
                }
            }
        }
        boilerplate += format!("{{
        let p = 0 as *mut u8;
        let mut buf = std::ptr::slice_from_raw_parts(p, len as usize);
        let t: _{}_wrapped = unsafe {{ serde_cbor::from_slice(&*buf).unwrap() }};
        let r = {} (", parsed_fn.sig.ident, parsed_fn.sig.ident).as_ref();

        for input in &items {
            match input {
                syn::FnArg::Typed(p) => {
                    let pat_id = *p.pat.clone();
                    let param_name = match pat_id {
                        syn::Pat::Ident(t) => t.ident,
                        _ => continue
                    };
                    boilerplate += format!("t.{},", param_name.to_string()).as_ref();
                },
                _ => {}
            }
        }
        boilerplate += ");\n";
        boilerplate += "serde_cbor::to_vec(&r).unwrap()\n}"
    }
    println!("{}", boilerplate);
    boilerplate.parse().expect("Invalid tokens in macro expansion")
}