#![expect(clippy::ptr_arg, reason = "Follow original type to use `&mut Vec<_>`")]
use crate::{Pos, Redirect, RedirectPort, SourceFile, Stmt, SwitchCase, Word, WordFrag};
macro_rules! define_visitor {
(
$i:lifetime;
$(
fn $func:ident ($v:ident $(,$arg:ident : $arg_ty:ty)* $(,)?)
$func_body:block
)*
) => {
pub trait VisitorMut<$i> {
$(
fn $func(&mut self, $($arg : $arg_ty),*) {
$func(self, $($arg),*);
}
)*
}
$(
pub fn $func<$i, V: VisitorMut<$i> + ?Sized>($v: &mut V, $($arg : $arg_ty),*)
$func_body
)*
};
}
define_visitor! {
'i;
fn visit_source_file_mut(v, file: &'i mut SourceFile) {
file.stmts.iter_mut().for_each(|s| v.visit_stmt_mut(s));
}
fn visit_stmt_mut(v, s: &'i mut Stmt) {
match s {
Stmt::Command(pos, ws) => v.visit_command_stmt_mut(*pos, ws),
Stmt::Block(pos, b) => v.visit_block_stmt_mut(*pos, b),
Stmt::If(pos, cond, then, else_) => v.visit_if_stmt_mut(*pos, cond, then, else_.as_deref_mut()),
Stmt::For(pos, var, seq, body) => v.visit_for_stmt_mut(*pos, var, seq, body),
Stmt::While(pos, cond, body) => v.visit_while_stmt_mut(*pos, cond, body),
Stmt::Break(pos) => v.visit_break_stmt_mut(*pos),
Stmt::Continue(pos) => v.visit_continue_stmt_mut(*pos),
Stmt::Function(pos, def, body) => v.visit_function_stmt_mut(*pos, def, body),
Stmt::Return(pos, w) => v.visit_return_stmt_mut(*pos, w.as_mut()),
Stmt::Switch(pos, testee, cases) => v.visit_switch_stmt_mut(*pos, testee, cases),
Stmt::Redirect(pos, s, redirects) => v.visit_redirect_stmt_mut(*pos, s, redirects),
Stmt::Pipe(pos, pipes, rhs) => v.visit_pipe_stmt_mut(*pos, pipes, rhs),
Stmt::Not(pos, s) => v.visit_not_stmt_mut(*pos, s),
Stmt::UnaryAnd(pos, s) => v.visit_unary_and_stmt_mut(*pos, s),
Stmt::UnaryOr(pos, s) => v.visit_unary_or_stmt_mut(*pos, s),
Stmt::BinaryAnd(pos, lhs, rhs) => v.visit_binary_and_stmt_mut(*pos, lhs, rhs),
Stmt::BinaryOr(pos, lhs, rhs) => v.visit_binary_or_stmt_mut(*pos, lhs, rhs),
}
}
fn visit_command_stmt_mut(v, _pos: Pos, ws: &'i mut Vec<Word>) {
ws.iter_mut().for_each(|w| v.visit_word_mut(w));
}
fn visit_block_stmt_mut(v, _pos: Pos, stmts: &'i mut Vec<Stmt>) {
stmts.iter_mut().for_each(|w| v.visit_stmt_mut(w));
}
fn visit_if_stmt_mut(v, _pos: Pos, cond: &'i mut Stmt, then: &'i mut Stmt, else_: Option<&'i mut Stmt>) {
v.visit_stmt_mut(cond);
v.visit_stmt_mut(then);
if let Some(else_) = else_ {
v.visit_stmt_mut(else_);
}
}
fn visit_for_stmt_mut(v, _pos: Pos, var: &'i mut Word, seq: &'i mut Vec<Word>, body: &'i mut Stmt) {
v.visit_word_mut(var);
seq.iter_mut().for_each(|w| v.visit_word_mut(w));
v.visit_stmt_mut(body);
}
fn visit_while_stmt_mut(v, _pos: Pos, cond: &'i mut Stmt, body: &'i mut Stmt) {
v.visit_stmt_mut(cond);
v.visit_stmt_mut(body);
}
fn visit_break_stmt_mut(_v, _pos: Pos) {}
fn visit_continue_stmt_mut(_v, _pos: Pos) {}
fn visit_function_stmt_mut(v, _pos: Pos, def: &'i mut Vec<Word>, body: &'i mut Stmt) {
def.iter_mut().for_each(|w| v.visit_word_mut(w));
v.visit_stmt_mut(body);
}
fn visit_return_stmt_mut(v, _pos: Pos, arg: Option<&'i mut Word>) {
if let Some(arg) = arg {
v.visit_word_mut(arg);
}
}
fn visit_switch_stmt_mut(v, _pos: Pos, testee: &'i mut Word, cases: &'i mut Vec<SwitchCase>) {
v.visit_word_mut(testee);
cases.iter_mut().for_each(|case| v.visit_switch_case_mut(case));
}
fn visit_redirect_stmt_mut(v, _pos: Pos, s: &'i mut Stmt, redirects: &'i mut Vec<Redirect>) {
v.visit_stmt_mut(s);
redirects.iter_mut().for_each(|redir| v.visit_redirect_mut(redir));
}
fn visit_pipe_stmt_mut(v, _pos: Pos, pipes: &'i mut Vec<(Stmt, RedirectPort)>, rhs: &'i mut Stmt) {
for (lhs, port) in pipes {
v.visit_stmt_mut(lhs);
v.visit_redirect_port_mut(port);
}
v.visit_stmt_mut(rhs);
}
fn visit_not_stmt_mut(v, _pos: Pos, s: &'i mut Stmt) {
v.visit_stmt_mut(s);
}
fn visit_unary_and_stmt_mut(v, _pos: Pos, s: &'i mut Stmt) {
v.visit_stmt_mut(s);
}
fn visit_unary_or_stmt_mut(v, _pos: Pos, s: &'i mut Stmt) {
v.visit_stmt_mut(s);
}
fn visit_binary_and_stmt_mut(v, _pos: Pos, lhs: &'i mut Stmt, rhs: &'i mut Stmt) {
v.visit_stmt_mut(lhs);
v.visit_stmt_mut(rhs);
}
fn visit_binary_or_stmt_mut(v, _pos: Pos, lhs: &'i mut Stmt, rhs: &'i mut Stmt) {
v.visit_stmt_mut(lhs);
v.visit_stmt_mut(rhs);
}
fn visit_switch_case_mut(v, case: &'i mut SwitchCase) {
case.globs.iter_mut().for_each(|w| v.visit_word_mut(w));
v.visit_stmt_mut(&mut case.body);
}
fn visit_redirect_mut(v, redirect: &'i mut Redirect) {
v.visit_redirect_port_mut(&mut redirect.port);
}
fn visit_redirect_port_mut(_v, _port: &'i mut RedirectPort) {}
fn visit_word_mut(v, w: &'i mut Word) {
match w {
Word::Simple(_) => {}
Word::Complex(frags) => frags.iter_mut().for_each(|f| v.visit_word_frag_mut(f)),
}
}
fn visit_word_frag_mut(v, frag: &'i mut WordFrag) {
match frag {
WordFrag::Literal(_) |
WordFrag::Variable { .. } |
WordFrag::VariableNoSplit { .. } |
WordFrag::Home { .. } |
WordFrag::Wildcard |
WordFrag::WildcardRecursive => {}
WordFrag::Command(s) |
WordFrag::CommandNoSplit(s) => v.visit_stmt_mut(s),
WordFrag::Brace(ws) => ws.iter_mut().for_each(|w| v.visit_word_mut(w)),
}
}
}