#![crate_type = "dylib"]
#![feature(quote, concat_idents, plugin_registrar, rustc_private)]
#![feature(custom_attribute)]
extern crate syntax;
extern crate rustc_plugin;
extern crate regex;
use syntax::ext::base::SyntaxExtension;
use syntax::symbol::Symbol;
use syntax::source_map::Span;
use syntax::ast::{Ident, MetaItem, ItemKind, MetaItemKind, NestedMetaItemKind, LitKind};
use syntax::ext::base::{Annotatable, ExtCtxt};
use syntax::ext::build::AstBuilder;
use std::collections::HashMap;
use regex::Captures;
#[doc(hidden)]
#[plugin_registrar]
pub fn registrar(reg: &mut rustc_plugin::Registry) {
reg.register_syntax_extension(
Symbol::intern("sql"),
SyntaxExtension::MultiModifier(Box::new(decorator))
);
}
fn decorator(ecx: &mut ExtCtxt,
sp: Span,
meta_item: &MetaItem,
annotated: Annotatable) -> Vec<Annotatable> {
let re = regex::Regex::new(r"\$([a-zA-Z0-9_\.]+)").unwrap();
let mut sql = Vec::new();
if let MetaItemKind::List(ref sql_) = meta_item.node {
for stmt in sql_.iter() {
if let NestedMetaItemKind::Literal(ref stmt) = stmt.node {
if let LitKind::Str(ref stmt, _) = stmt.node {
let mut variable_numbers = HashMap::new();
let mut binds = Vec::new();
let replaced = re.replace_all(&stmt.as_str(), |cap: &Captures| {
let var = cap.get(1).unwrap().as_str();
if variable_numbers.get(var).is_none() {
let len = format!("${}", variable_numbers.len() + 1);
let mut expr = Some(ecx.expr_self(sp));
for field in var.split('.') {
expr = Some(ecx.expr_field_access(
sp,
expr.take().unwrap(),
Ident::from_str(field)
))
}
binds.push(
ecx.expr_addr_of(
sp,
expr.unwrap()
)
);
variable_numbers.insert(var.to_string(), len);
}
variable_numbers.get(var).unwrap().to_string()
}).to_string();
let binds = ecx.expr_vec_slice(sp, binds);
sql.push(quote_stmt!(
ecx,
buf.bind($replaced, $binds).execute(0);
).unwrap())
}
}
}
}
let mut output = Vec::new();
match annotated.clone() {
Annotatable::Item(it) => {
if let ItemKind::Struct(_, _) = it.node {
output.push(annotated);
let name = &it.ident;
output.push(Annotatable::Item(
quote_item!(
ecx,
impl pleingres::Request for $name {
fn request(&mut self, buf: &mut pleingres::Buffer) {
$sql
debug!("request sent");
}
}
).unwrap()
));
} else {
panic!("not a struct")
}
},
_ => panic!("not an item")
}
output
}