#![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);
let impls = items.pop().ok_or(Error::NotEnoughItems)?;
let mut elms = items.pop().ok_or(Error::NotEnoughItems)?;
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();
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);
}
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);
}
}
}
let mut tokens = Tokens::new();
let mut methods = Vec::new();
let mut returns = Vec::new();
let mut match_arms = Vec::new();
for item in items {
let name = item.ident.as_ref();
let (sig,block) = match item.node {
syn::ImplItemKind::Method(sig, block) => (sig, block),
_ => return Err(Error::InvalidSyntax)
};
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 });
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 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 {
tts.to_tokens(&mut tokens);
}
}
}
}
}
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());
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
}
}
"#;
let tokens = parse_to_state_machine(test).unwrap();
println!("{:#?}", syn::parse_crate(tokens.as_str()));
}