use crate::bash_parser::{BashAst, BashStmt};
pub fn format_ast(ast: &BashAst) -> String {
let mut output = String::new();
output.push_str("=== AST ===\n");
output.push_str(&format!("Statements: {}\n", ast.statements.len()));
for (i, stmt) in ast.statements.iter().enumerate() {
output.push_str(&format!("\n[{}] {}\n", i, format_statement(stmt, 0)));
}
output
}
fn format_statement(stmt: &BashStmt, indent: usize) -> String {
let indent_str = " ".repeat(indent);
match stmt {
BashStmt::Command { name, args, .. } => {
if args.is_empty() {
format!("{}Command: {}", indent_str, name)
} else {
format!("{}Command: {} (args: {})", indent_str, name, args.len())
}
}
BashStmt::Assignment { name, .. } => {
format!("{}Assignment: {}", indent_str, name)
}
BashStmt::If {
then_block,
elif_blocks,
else_block,
..
} => {
let mut s = format!("{}If statement", indent_str);
s.push_str(&format!(
"\n{} then: {} statements",
indent_str,
then_block.len()
));
if !elif_blocks.is_empty() {
s.push_str(&format!(
"\n{} elif: {} branches",
indent_str,
elif_blocks.len()
));
}
if else_block.is_some() {
s.push_str(&format!("\n{} else: present", indent_str));
}
s
}
BashStmt::While { body, .. } => {
format!("{}While loop ({} statements)", indent_str, body.len())
}
BashStmt::For { variable, body, .. } => {
format!(
"{}For loop: {} ({} statements)",
indent_str,
variable,
body.len()
)
}
BashStmt::ForCStyle {
init,
condition,
increment,
body,
..
} => {
format!(
"{}C-style for loop: {}; {}; {} ({} statements)",
indent_str,
init,
condition,
increment,
body.len()
)
}
BashStmt::Function { name, body, .. } => {
format!(
"{}Function: {} ({} statements)",
indent_str,
name,
body.len()
)
}
BashStmt::Case { arms, .. } => {
format!("{}Case statement ({} arms)", indent_str, arms.len())
}
BashStmt::Until { body, .. } => {
format!("{}Until loop ({} statements)", indent_str, body.len())
}
BashStmt::Return { .. } => {
format!("{}Return statement", indent_str)
}
BashStmt::Comment { text, .. } => {
format!(
"{}Comment: {}",
indent_str,
text.lines().next().unwrap_or("")
)
}
BashStmt::Pipeline { commands, .. } => {
format!("{}Pipeline ({} commands)", indent_str, commands.len())
}
BashStmt::AndList { .. } => {
format!("{}AndList (&&)", indent_str)
}
BashStmt::OrList { .. } => {
format!("{}OrList (||)", indent_str)
}
BashStmt::BraceGroup { body, .. } => {
format!("{}BraceGroup ({} statements)", indent_str, body.len())
}
BashStmt::Coproc { name, body, .. } => {
if let Some(n) = name {
format!("{}Coproc: {} ({} statements)", indent_str, n, body.len())
} else {
format!("{}Coproc ({} statements)", indent_str, body.len())
}
}
BashStmt::Select { variable, body, .. } => {
format!(
"{}Select: {} ({} statements)",
indent_str,
variable,
body.len()
)
}
BashStmt::Negated { command, .. } => {
format!(
"{}Negated: {}",
indent_str,
format_statement(command, indent + 1)
)
}
}
}
#[cfg(test)]
#[path = "ast_display_tests_dummy_span.rs"]
mod tests_extracted;