use quote::{quote, quote_spanned, TokenStreamExt};
use syn::{
Expr,
ExprAssign,
ExprAssignOp,
ExprBlock,
ExprField,
ExprPath,
Ident,
Path,
Stmt,
parse,
};
use syn::Expr::{Assign, AssignOp, Block, Field};
use syn::fold::{Fold, fold_expr};
use syn::Member::Named;
use super::{MsgModelMap, PropertyModelMap};
pub struct Adder<'a> {
msg_map: &'a MsgModelMap,
property_map: &'a PropertyModelMap,
}
impl<'a> Adder<'a> {
pub fn new(property_map: &'a PropertyModelMap, msg_map: &'a MsgModelMap) -> Self {
Adder {
msg_map,
property_map,
}
}
}
impl<'a> Adder<'a> {
fn fold_assign(&self, lhs: Expr, mut new_assign: Expr) -> Expr {
let mut statements = vec![];
let new_statements =
if let Field(ExprField { ref base, member: Named(ref ident), .. }) = lhs {
if is_model_path(base) {
Some(create_stmts(ident, self.property_map, self.msg_map))
}
else {
None
}
}
else {
None
};
if let Some(mut stmts) = new_statements {
let statement: Stmt = parse(quote! {
#new_assign;
}.into()).expect("expression statement");
statements.push(statement);
statements.append(&mut stmts);
new_assign = parse(quote! {{
#(#statements)*
}}.into()).expect("statements");
}
new_assign
}
}
impl<'a> Fold for Adder<'a> {
fn fold_expr(&mut self, expr: Expr) -> Expr {
let lhs_clone =
match expr {
Assign(ExprAssign { ref left, .. }) | AssignOp(ExprAssignOp { ref left, .. }) => *left.clone(),
_ => return fold_expr(self, expr),
};
let new_expr = fold_expr(self, expr);
self.fold_assign(lhs_clone, new_expr)
}
}
#[derive(Debug, Eq, Hash, PartialEq)]
pub struct Message {
pub expr: Expr,
pub name: Ident,
pub widget_name: Ident,
}
#[derive(Debug, Eq, Hash, PartialEq)]
pub struct Property {
pub expr: Expr,
pub is_relm_widget: bool,
pub name: Ident,
pub widget_name: Ident,
}
fn create_stmts(ident: &Ident, property_map: &PropertyModelMap, msg_map: &MsgModelMap) -> Vec<Stmt> {
let mut stmts = vec![];
stmts.append(&mut create_stmts_for_props(ident, property_map));
stmts.append(&mut create_stmts_for_msgs(ident, msg_map));
stmts
}
fn create_stmts_for_msgs(ident: &Ident, msg_map: &MsgModelMap) -> Vec<Stmt> {
let mut stmts = vec![];
if let Some(messages) = msg_map.get(ident) {
for msg in messages {
let widget_name = &msg.widget_name;
let mut value = quote! {};
value.append_all(&[&msg.expr]);
let variant = &msg.name;
let stmt = quote_spanned! { ident.span() =>
{ self.components.#widget_name.stream().emit(#variant(#value)); }
};
let expr: Expr = parse(stmt.into())
.expect("parse() in create_stmts");
if let Block(ExprBlock { ref block, .. }) = expr {
stmts.push(block.stmts[0].clone());
}
}
}
stmts
}
fn create_stmts_for_props(ident: &Ident, property_map: &PropertyModelMap) -> Vec<Stmt> {
let mut stmts = vec![];
if let Some(properties) = property_map.get(ident) {
for property in properties {
let widget_name = &property.widget_name;
let prop_name = Ident::new(&format!("set_{}", property.name), property.name.span());
let mut tokens = quote! {};
tokens.append_all(&[&property.expr]);
let stmt =
quote_spanned! { ident.span() =>
{ self.widgets.#widget_name.#prop_name(#tokens); }
};
let expr: Expr = parse(stmt.into()).expect("parse() in create_stmts");
if let Block(ExprBlock { ref block, .. }) = expr {
stmts.push(block.stmts[0].clone());
}
}
}
stmts
}
fn is_model_path(expr: &Expr) -> bool {
if let Field(ExprField { ref base, ref member, .. }) = *expr {
if let Expr::Path(ExprPath { path: Path { ref segments, .. }, ..}) = **base {
if let Named(member_name) = member {
return segments.len() == 1 && segments[0].ident == "self" && member_name == "model";
}
}
}
false
}