use lang::{LANGUAGES, Language};
use util;
use syntax::ptr::P;
use syntax::ast::{self, Block, BlockCheckMode, DUMMY_NODE_ID, Expr, ExprKind, Ident, ImplItemKind, ImplItem, PatKind, Path, PathSegment, PolyTraitRef, Stmt, StmtKind, ThinVec, TraitBoundModifier, TyParam, TyParamBound};
use syntax::ext::base::ExtCtxt;
use syntax::ext::build::AstBuilder;
use syntax::codemap::DUMMY_SP;
use std::collections::HashMap;
pub fn lang_marshalls(ecx: &mut ExtCtxt,
impl_item: &ImplItem) -> HashMap<String, Path> {
LANGUAGES.iter().map(|&lang| {
(lang.name().to_owned(),
config::lang_marshall_path(ecx, impl_item.ident, lang))
}).collect()
}
pub fn create_marshalls(ecx: &mut ExtCtxt,
impl_items: &[ImplItem]) -> Vec<ImplItem> {
let mut marshall_fns = Vec::new();
for impl_item in impl_items.iter() {
match impl_item.node {
ImplItemKind::Method(ref sig, ..) => {
let original_name = impl_item.ident;
let common_marshall_fn =
create_common_marshall(ecx, original_name, sig);
marshall_fns.push(common_marshall_fn);
for &lang in LANGUAGES {
let lang_marshall_fn = create_lang_marshall(ecx, original_name, sig, lang);
marshall_fns.push(lang_marshall_fn);
}
},
_ => (),
}
}
marshall_fns
}
fn create_common_marshall(ecx: &mut ExtCtxt,
original_name: Ident,
method_sig: &ast::MethodSig) -> ImplItem {
let mut marshall_sig = method_sig.clone();
let marshall_trait_path = Path {
segments: vec![
PathSegment::from_ident(Ident::from_str("plugger_core"), DUMMY_SP),
PathSegment::from_ident(Ident::from_str("Marshall"), DUMMY_SP),
],
span: DUMMY_SP,
};
let marshall_trait_ref = PolyTraitRef::new(Vec::new(), marshall_trait_path, DUMMY_SP);
marshall_sig.generics.ty_params.insert(0, TyParam {
attrs: ThinVec::new(),
ident: Ident::from_str("M"),
id: DUMMY_NODE_ID,
bounds: vec![TyParamBound::TraitTyParamBound(marshall_trait_ref, TraitBoundModifier::None)],
default: None,
span: DUMMY_SP,
});
let value_ty_path = ecx.path(DUMMY_SP, vec![
Ident::from_str("M"), Ident::from_str("Value"),
]);
let value_ty = ecx.ty_path(value_ty_path);
marshall_sig.decl = util::replace_parameter_types(marshall_sig.decl.clone(),
value_ty);
let marshalled_args: Vec<_> = method_sig.decl.inputs.iter().map(|arg| {
if arg.is_self() {
ecx.expr_self(DUMMY_SP) } else { let arg_name = match arg.pat.node {
PatKind::Ident(_, ref ident, _) => ident.node,
_ => unimplemented!(),
};
let ty_name = util::ty_name_str(&arg.ty);
let marshall_fn = Ident::from_str(&format!("to_{}", ty_name).to_lowercase());
P(quote_expr!(ecx, M::$marshall_fn($arg_name)).unwrap())
}
}).collect();
let original_fn_expr = quote_expr!(ecx, Self::$original_name);
let call_stmt = Stmt {
id: DUMMY_NODE_ID,
span: DUMMY_SP,
node: StmtKind::Expr(P(Expr {
id: DUMMY_NODE_ID,
span: DUMMY_SP,
attrs: ThinVec::new(),
node: ExprKind::Call(original_fn_expr, marshalled_args),
})),
};
let block = Block {
id: DUMMY_NODE_ID,
span: DUMMY_SP,
rules: BlockCheckMode::Default,
stmts: vec![call_stmt],
};
ImplItem {
id: DUMMY_NODE_ID,
span: DUMMY_SP,
ident: config::common_marshall_name(original_name),
node: ImplItemKind::Method(marshall_sig, P(block)),
vis: ast::Visibility::Public,
defaultness: ast::Defaultness::Final,
attrs: Vec::new(),
tokens: None,
}
}
fn create_lang_marshall(ecx: &mut ExtCtxt,
original_name: Ident,
method_sig: &ast::MethodSig,
lang: &Language) -> ImplItem {
let mut marshall_sig = method_sig.clone();
marshall_sig.decl = util::replace_parameter_types(marshall_sig.decl.clone(), lang.value_ty(ecx));
let common_marshall_expr = config::common_marshall_expr(ecx, original_name, lang);
let args = method_sig.decl.inputs.iter().map(|arg| {
if arg.is_self() {
ecx.expr_self(DUMMY_SP)
} else {
match arg.pat.node {
ast::PatKind::Ident(_, ident, _) => {
ecx.expr_ident(DUMMY_SP, ident.node)
},
_ => unimplemented!(),
}
}
}).collect();
let call_expr = ecx.expr_call(DUMMY_SP, common_marshall_expr, args);
let block = ecx.block_expr(call_expr);
ImplItem {
id: DUMMY_NODE_ID,
span: DUMMY_SP,
ident: config::lang_marshall_name(original_name, lang),
node: ImplItemKind::Method(marshall_sig, block),
vis: ast::Visibility::Public,
defaultness: ast::Defaultness::Final,
attrs: Vec::new(),
tokens: None,
}
}
mod config {
use lang::Language;
use syntax::ast::{self, Expr, Ident, Path};
use syntax::codemap::DUMMY_SP;
use syntax::ext::base::ExtCtxt;
use syntax::ext::build::AstBuilder;
use syntax::ptr::P;
pub fn common_marshall_name(original_fn: Ident) -> Ident {
Ident::from_str(&format!("{}_marshall", original_fn))
}
pub fn lang_marshall_name(original_fn: Ident, lang: &Language) -> Ident {
Ident::from_str(&format!("{}_{}", original_fn, lang.name()))
}
pub fn common_marshall_path(ecx: &mut ExtCtxt, original_fn: Ident, lang: &Language) -> Path {
let mut path = ecx.path(DUMMY_SP, vec![Ident::from_str("Self"),
common_marshall_name(original_fn)]);
path.segments.last_mut().unwrap().parameters = Some(P(ast::PathParameters::AngleBracketed(
ast::AngleBracketedParameterData {
span: DUMMY_SP,
lifetimes: Vec::new(),
types: vec![lang.marshall_ty(ecx)],
bindings: Vec::new(),
}
)));
path
}
pub fn lang_marshall_path(ecx: &mut ExtCtxt,
original_fn: Ident,
lang: &Language) -> Path {
ecx.path(DUMMY_SP, vec![Ident::from_str("Self"),
lang_marshall_name(original_fn, lang)])
}
pub fn common_marshall_expr(ecx: &mut ExtCtxt, original_fn: Ident, lang: &Language) -> P<Expr> {
let path = common_marshall_path(ecx, original_fn, lang);
ecx.expr_path(path)
}
}