jam_derive 0.0.1

This macro provides the struct to state machine transformation for the jam crate
Documentation
/*service!(
#r"
[description]
blablabl
[tags]
music, audiobook
[version]
0.0.1
"

struct MusicService {
    songs: Songs,
    lists: Lists
}

impl MusicService {
    fn get_list_by_hash(&self, hash: Hash) -> Result<List, Error> {
        match self.lists.find_by_hash(hash) {
            Ok(list) => Ok(list),
            Err(e) => Err(Error::NotFound)
        }
    }
}
)

fn main() {
    let service = MusicService::new("localhost:8080").unwrap();

    service::DynamicCall("get_list_by_hash")
        .append(Hash::new(432432))
        .call("localhost:8080")
        .map(|x| println!("{:?}", x));
}*/
#![feature(proc_macro, proc_macro_lib)]
#![crate_type = "proc-macro"]
#![recursion_limit="256"]
extern crate proc_macro;


extern crate syn;

#[macro_use]
extern crate quote;

use proc_macro::TokenStream;
use syn::{Item, parse_items};
use syn::{Field, TokenTree, Variant, Ident, ItemKind, VariantData, Ty, Pat, Mutability, BindingMode};

use quote::{Tokens, ToTokens};

#[derive(Debug)]
enum Error {
    ParseError(String),
    NotEnoughItems,
    IdentNotFound,
    DeriveServiceMissing,
    ImplNameMismatch,
    InvalidSyntax
}

fn create_path_segment(name: &str, append_type: Ty) -> syn::Ty {
    let path = Ty::Path(None, syn::Path { 
        global: false, 
        segments: vec![syn::PathSegment {
            ident: Ident::new(name),
            parameters: syn::PathParameters::AngleBracketed(
                syn::AngleBracketedParameterData {
                    lifetimes: vec![],
                    types: vec![append_type],
                    bindings: vec![]
                }
            )
        }]
    });

    path
}

fn parse_to_state_machine(test: &str) -> Result<Tokens, Error> {
    let mut items = syn::parse_items(test).map_err(|x| Error::ParseError(x))?;
       
    println!("{:#?}", test);

    // parse the struct at the beginning
    let impls = items.pop().ok_or(Error::NotEnoughItems)?;
    let mut elms  = items.pop().ok_or(Error::NotEnoughItems)?;

    // identify the name of the struct
    let struct_name = elms.ident.as_ref();

    let mut before_fields: Vec<Ident> = Vec::new();
    let mut after_fields: Vec<Ident> = Vec::new();
    let mut first_fields: Vec<Ident> = Vec::new();

    let mut node_tokens = Tokens::new();

    // transform all fields to Arc<RefCell<Field>>
    let mut aval_fields: Vec<String> = Vec::new();
    if let ItemKind::Struct(ref mut variant, _) = elms.node {
        if let &mut VariantData::Struct(ref mut fields) = variant {
            for field in fields.iter_mut() {
                if let Ty::Path(_, syn::Path { global: _, segments: ref segments}) = field.ty {
                    if let Some(&syn::PathSegment { ident: ref ident, parameters: _ }) = segments.last() {
                        aval_fields.push(ident.as_ref().into());
                    }
                }

                before_fields.push(Ident::new("self_".to_owned() + field.ident.as_ref().unwrap().as_ref()));
                after_fields.push(Ident::new("self.".to_owned() + field.ident.as_ref().unwrap().as_ref() + ".borrow_mut()"));
                first_fields.push(Ident::new("self . ".to_owned() + field.ident.as_ref().unwrap().as_ref()));

                field.ty = create_path_segment("Arc", create_path_segment("RefCell", field.ty.clone()));
            }
        }

        variant.to_tokens(&mut node_tokens);
    }

    // compare the impl name with struct name for validity
    //println!("{:#?}", impls.node);
    if let ItemKind::Impl(_,_,_, path, ty, items) = impls.node {
        if let Some(syn::Path { global: _, segments: segments }) = path {
            if let Some(&syn::PathSegment { ident: ref ident, parameters: _ }) = segments.last() {
                if ident.as_ref() != "Service" {
                    return Err(Error::DeriveServiceMissing);
                }
            }
        }

        if let Ty::Path(_, syn::Path { global: _, segments: segments}) = *ty {
            if let Some(&syn::PathSegment { ident: ref ident, parameters: _ }) = segments.last() {
                if ident.as_ref() != struct_name {
                    return Err(Error::ImplNameMismatch);
                }
            }
        }

        // save all tokens here
        let mut tokens = Tokens::new();

        // save all methods here
        let mut methods = Vec::new();

        // save all return types here
        let mut returns = Vec::new();

        // save the match arms here
        let mut match_arms = Vec::new();

        // transmute all impl fields to a single state machine
        for item in items {
            //println!("{:#?}",item);
            let name = item.ident.as_ref();
            let (sig,block) = match item.node {
                syn::ImplItemKind::Method(sig, block) => (sig, block),
                _  => return Err(Error::InvalidSyntax)
            };
            
            // collect all parameter as (name, type)
            let mut inputs: Vec<(String, String)> = Vec::new();
            let mut inputs_fields: Vec<Field> = Vec::new();

            for input in sig.decl.inputs {
                if let syn::FnArg::SelfRef(_,_) = input {}
                else if let syn::FnArg::Captured(path, ty) = input {
                    if let syn::Pat::Ident(_, ident1, _) = path {
                        if let Ty::Path(_, syn::Path { global: _, segments: segments}) = ty.clone() {
                            if let Some(&syn::PathSegment { ident: ref ident2, parameters: _ }) = segments.last() {
                                inputs.push((ident1.as_ref().into(), ident2.as_ref().into()));
                            }
                        }

                        inputs_fields.push( Field { ident: Some(ident1), vis: syn::Visibility::Public, attrs: Vec::new(), ty: ty });
                    }
                } else {
                    return Err(Error::InvalidSyntax);
                }
            }

            let arg_struct_name = Ident::new(struct_name.to_owned() + name);
            let method_name = Ident::new(name);
            let method_inner = struct_name.to_owned() + name;


            methods.push(Variant { ident: method_name.clone(), attrs: Vec::new(), data: VariantData::Tuple(vec![Field { ident: None, vis: syn::Visibility::Inherited, attrs: Vec::new(), ty: syn::parse_type(&method_inner).unwrap() }]), discriminant: None });//arg_struct_name.clone());

            let arg_tokens = quote!(
                #[derive(Serialize, Deserialize)]
                pub struct #arg_struct_name {
                    #(#inputs_fields),*
                }
            );

            tokens.append(&arg_tokens);

            if let syn::FunctionRetTy::Ty(kind) = sig.decl.output {
                returns.push(Variant { ident: method_name.clone(), attrs: Vec::new(), data: VariantData::Tuple(vec![Field { ident: None, vis: syn::Visibility::Inherited, attrs: Vec::new(), ty: kind.clone() }]), discriminant: None });
            }

            let t1 = after_fields.clone();
            let t2 = before_fields.clone();

            let ret_type = Ident::new(struct_name.to_owned() + "Ret::" + name);
            let mut block_tokens = Tokens::new();
            block.to_tokens(&mut block_tokens);

            let mut block_string = block_tokens.to_string();

            for (before, after) in first_fields.iter().zip(before_fields.iter()) {
                block_string = block_string.replace(before.as_ref(), after.as_ref());
            }

            let reparsed_block = syn::parse::block(&block_string).expect("Couldn't parse back!");

            let body = quote!({
                let tmp = {
                    #(
                        let mut #t2 = #t1;
                    )*

                    #reparsed_block
                };

                #ret_type(tmp)
            });
        

            let match_arm_name = struct_name.to_owned() + "Args::" + name;
            match_arms.push(syn::Arm { attrs: Vec::new(), pats: vec![syn::Pat::TupleStruct(syn::parse_path(&match_arm_name).unwrap(), vec![Pat::Ident(BindingMode::ByValue(Mutability::Immutable), Ident::new("args"), None)], None)], guard: None, body: Box::new(syn::parse_expr(&body.to_string()).unwrap()) });

        }
       
        let enum_args = Ident::new(struct_name.to_owned() + "Args");
        let enum_ret  = Ident::new(struct_name.to_owned() + "Ret");
        let new_struct = Ident::new(struct_name);
        let node = elms.node;

        let enum_tokens = quote!(
            pub struct #new_struct
                #node_tokens

            #[derive(Serialize, Deserialize)]
            pub enum #enum_args {
                #(#methods),*
            }

            #[derive(Serialize, Deserialize)]
            pub enum #enum_ret {
                InvalidMethod,
                #(#returns),*
            }

            impl Service for #new_struct {
                fn process(&mut self, input: &[u8]) -> Vec<u8> {
                    use bincode::{serialize, deserialize, Infinite};
                    
                    let args: #enum_args = deserialize(input).unwrap();

                    let mut ret: #enum_ret = #enum_ret::InvalidMethod;

                    let ret = match args {
                        #(#match_arms),*
                    };

                    serialize(&ret, Infinite).unwrap()
                }
            }
        );


        tokens.append(&enum_tokens);

        println!("{}", tokens.to_string());
    
        return Ok(tokens);
    }

    panic!("");
}

#[proc_macro_derive(RPCService)]
pub fn derive_rpc(input: TokenStream) -> TokenStream {
    let mut tokens = Tokens::new();
    
    //let mut inner = String::new();

    let ast = syn::parse_macro_input(&input.to_string()).unwrap();
    if let syn::Body::Enum(vars) = ast.body {
        let first_variant = vars.first().unwrap();

        if let Some(syn::ConstExpr::Other(syn::Expr { ref node, ref attrs })) = first_variant.discriminant {
            if let &syn::ExprKind::TupField(ref expr, _) = node {
                let &syn::Expr { ref node, ref attrs} = expr.as_ref();
                
                if let &syn::ExprKind::Tup(ref exprs) = node {
                    let &syn::Expr { ref node, ref attrs} = exprs.first().unwrap();
                    
                    if let &syn::ExprKind::Mac(syn::Mac { ref path, ref tts }) = node {
                //if let &syn::TokenTree::Delimited(syn::Delimited { ref delim, ref tts }) = tts.first().unwrap() {
                    //let inner = tts.first().unwrap();
                    //println!("{:#?}", tts);
                        tts.to_tokens(&mut tokens);
                    //for token in tts.iter() {
                    //    println!("{}",token);
                    //}
                //}
                //
                    }
                }
            }
        }
    }
  
    let tokens = tokens.to_string();
    let inner = tokens.split('|').skip(1).next().unwrap();

    let tokens = parse_to_state_machine(&inner).unwrap();

    println!("{}", tokens.as_str());


    //syn::parse_crate(tokens.as_str()).unwrap()
    tokens.parse().unwrap()
}

fn main() {
    let test = r#"
        struct MusicService {
            #[share] songs: Songs,
            #[share] lists: Lists
        }
       
        impl Service for MusicService {
            fn get(&self, hash: Hash) -> Result<List, Error> {
                match self.lists.find.by_hash(hash) {
                    Ok(list) => Ok(list),
                    Err(e) => Err(Error::NotFound)
                }

                self.lists
            }
        }
    "#;

    /*
     enum MusicServiceArgs {
        get(MusicServiceGet)
     }

     impl Service for MusicService {
        fn process(&self, param: MusicServiceArgs) -> MusicSeriveSend {
            match param {
                MusicServiceRecv::get(hash) => {
                    let self_lists = self.lists.borrow();

                    let tmp = {
                        // content
                        //
                    };
                    
                    return Ok(MusicServiceSend::get(tmp));
                }
            }
        }
                    
    }
    */
    //println!("{:#?}", syn::parse_items(test));

    let tokens = parse_to_state_machine(test).unwrap();
    println!("{:#?}", syn::parse_crate(tokens.as_str()));
}