#![recursion_limit = "128"] extern crate proc_macro; extern crate proc_macro2; #[macro_use] extern crate syn; #[macro_use] extern crate quote; extern crate regex; use proc_macro2::*; use std::collections::HashMap; use regex::Captures; use syn::DeriveInput; use proc_macro::TokenStream; #[proc_macro_derive(Request)] pub fn request(input: TokenStream) -> TokenStream { let input = parse_macro_input!(input as DeriveInput); let ref name = input.ident; let enum_ = match input.data { syn::Data::Enum(ref enum_) => enum_, _ => panic!("only enums are supported") }; let request_clauses = enum_.variants.iter().map(|var| { let var_name = &var.ident; quote! { #name::#var_name(ref mut a) => a.request(buf) } }); let from_instances = enum_.variants.iter().map(|var| { let ty = &var.fields.iter().next().unwrap().ty; let var_name = &var.ident; quote! { impl std::convert::From<#ty> for #name { fn from(s: #ty) -> #name { #name::#var_name(s) } } impl std::convert::From<#name> for #ty { fn from(s: #name) -> #ty { match s { #name::#var_name(s) => s, _ => panic!(concat!("Wrong conversion into ", stringify!(#ty), " for type ", stringify!(#name))) } } } } }); TokenStream::from(quote!{ impl pleingres::Request for #name { fn request(&mut self, buf: &mut pleingres::Buffer) { match *self { #(#request_clauses),* } } } #(#from_instances)* }) } #[proc_macro_derive(HandleRowJoin)] pub fn handle_row_join(input: TokenStream) -> TokenStream { let input = parse_macro_input!(input as DeriveInput); let ref name = input.ident; let enum_ = match input.data { syn::Data::Enum(ref enum_) => enum_, _ => panic!("only enums are supported") }; let row_clauses = enum_.variants.iter().map(|var| { let var_name = &var.ident; quote! { #name::#var_name(ref mut a) => a.row(row) } }); let complete_clauses = enum_.variants.iter().map(|var| { let var_name = &var.ident; quote! { #name::#var_name(ref mut a) => a.complete(n) } }); let ready_clauses = enum_.variants.iter().map(|var| { let var_name = &var.ident; quote! { #name::#var_name(ref mut a) => a.ready_for_query() } }); let err_clauses = enum_.variants.iter().map(|var| { let var_name = &var.ident; quote! { #name::#var_name(ref mut a) => a.err(err) } }); TokenStream::from(quote!{ impl pleingres::HandleRow for #name { fn row(&mut self, row: pleingres::Row) -> bool { match *self { #(#row_clauses),* } } fn complete(&mut self, n: u32) { match *self { #(#complete_clauses),* } } fn ready_for_query(&mut self) { match *self { #(#ready_clauses),* } } fn err(&mut self, err: &str) { match *self { #(#err_clauses),* } } } }) } #[proc_macro_attribute] pub fn sql(attr: proc_macro::TokenStream, item: proc_macro::TokenStream) -> proc_macro::TokenStream { let attr = proc_macro2::TokenStream::from(attr); let item = proc_macro2::TokenStream::from(item); let re = regex::Regex::new(r"\$([a-zA-Z0-9_\.]+)").unwrap(); let binds = attr.into_iter().filter_map(|s| { match s { TokenTree::Literal(_) => {}, _ => return None, } let s = format!("{}", s); let s = s.trim_matches('"'); let mut variable_numbers = HashMap::new(); let mut binds = Vec::new(); let replaced = re.replace_all(&s, |cap: &Captures| { let var = cap.get(1).unwrap().as_str(); if variable_numbers.get(var).is_none() { // Mapping the variable name to a number. let len = format!("${}", variable_numbers.len() + 1); variable_numbers.insert(var.to_string(), len); // Making an expression let fields = var.split('.').map(|field| { syn::Ident::new(&field, Span::call_site()) }); binds.push(quote!( &self.#(#fields).* )); } variable_numbers.get(var).unwrap().to_string() }); Some(quote! { buf.bind(#replaced, &[#(#binds),*]).execute(0); }) }); let mut name = None; let mut item = item.into_iter(); let mut item_ = Vec::new(); loop { match item.next() { Some(TokenTree::Ident(id)) => { if id.to_string() == "struct" { let it = item.next().unwrap(); name = Some(syn::Ident::new(&format!("{}", it), it.span())); item_.push(TokenTree::Ident(id)); item_.push(it); } else { item_.push(TokenTree::Ident(id)); } } None => break, Some(it) => { item_.push(it) } } } let name = name.unwrap(); use std::iter::FromIterator; let item = proc_macro2::TokenStream::from_iter(item_); let tokens = quote! { impl pleingres::Request for #name { fn request(&mut self, buf: &mut pleingres::Buffer) { #(#binds)* debug!("request sent"); } } #item }; proc_macro::TokenStream::from(tokens) }