use syn;
use syn::visit::Visit;
use std::{mem, path::PathBuf};
use wearte_config::Config;
use super::Context;
use crate::generator::visits::is_super;
use crate::parser::{Helper, Node};
pub(super) fn find_loop_var(c: &Config, ctx: Context, path: PathBuf, nodes: &[Node]) -> bool {
FindEach::new(c, ctx, path).find(nodes)
}
struct FindEach<'a> {
loop_var: bool,
c: &'a Config<'a>,
ctx: Context<'a>,
on_path: PathBuf,
on_: usize,
}
impl<'a> FindEach<'a> {
fn new<'n>(c: &'n Config<'n>, ctx: Context<'n>, on_path: PathBuf) -> FindEach<'n> {
FindEach {
c,
ctx,
on_path,
loop_var: false,
on_: 0,
}
}
pub fn find(&mut self, nodes: &'a [Node]) -> bool {
for n in nodes {
match n {
Node::Local(expr) => self.visit_stmt(expr),
Node::Expr(_, expr) | Node::Safe(_, expr) => self.visit_expr(expr),
Node::Helper(h) => match h {
Helper::If((_, first, block), else_if, els) => {
self.visit_expr(first);
if self.loop_var {
break;
}
self.find(block);
for (_, e, b) in else_if {
if self.loop_var {
break;
}
self.visit_expr(e);
if self.loop_var {
break;
}
self.find(b);
}
if self.loop_var {
break;
}
if let Some((_, els)) = els {
self.find(els);
}
}
Helper::With(_, e, b) => {
self.visit_expr(e);
if self.loop_var {
break;
}
self.on_ += 1;
self.find(b);
self.on_ -= 1;
}
Helper::Unless(_, expr, block) => {
self.visit_expr(expr);
if self.loop_var {
break;
}
self.find(block);
}
Helper::Each(_, expr, block) => {
self.visit_expr(expr);
if self.loop_var {
break;
}
self.on_ += 1;
self.find(block);
self.on_ -= 1;
}
Helper::Defined(..) => unimplemented!(),
},
Node::Partial(_, path, _) => {
let p = self.c.resolve_partial(&self.on_path, path);
let nodes = self.ctx.get(&p).unwrap();
let parent = mem::replace(&mut self.on_path, p);
self.find(nodes);
self.on_path = parent;
}
Node::Raw(..) | Node::Lit(..) | Node::Comment(_) => (),
}
if self.loop_var {
break;
}
}
self.loop_var
}
}
impl<'a> Visit<'a> for FindEach<'a> {
fn visit_expr_path(&mut self, i: &'a syn::ExprPath) {
macro_rules! search {
($ident:expr) => {
match $ident.as_ref() {
"index" | "index0" | "first" | "last" => self.loop_var = true,
ident => {
let ident = ident.as_bytes();
if 7 < ident.len()
&& &ident[0..7] == b"_index_"
&& ident[7].is_ascii_digit()
{
self.loop_var = true;
}
}
}
};
}
if !self.loop_var {
if i.path.segments.len() == 1 {
search!(i.path.segments[0].ident.to_string());
} else if 0 < self.on_ {
if let Some((j, ident)) = is_super(&i.path.segments) {
if j == self.on_ {
search!(ident);
}
}
}
}
}
}