use std::fmt;
use crate::ast::{ListItem, ListOperator, Node, NodeKind};
pub(super) fn write_pipeline(f: &mut fmt::Formatter<'_>, commands: &[Node]) -> fmt::Result {
if commands.len() == 1 {
return write!(f, "{}", commands[0]);
}
let mut groups: Vec<Vec<&Node>> = Vec::new();
for cmd in commands {
if matches!(cmd.kind, NodeKind::Redirect { .. }) {
if let Some(last) = groups.last_mut() {
last.push(cmd);
} else {
groups.push(vec![cmd]);
}
} else {
groups.push(vec![cmd]);
}
}
write_pipeline_groups(f, &groups, 0)
}
fn write_pipeline_groups(
f: &mut fmt::Formatter<'_>,
groups: &[Vec<&Node>],
idx: usize,
) -> fmt::Result {
if idx >= groups.len() {
return Ok(());
}
if idx == groups.len() - 1 {
for (j, node) in groups[idx].iter().enumerate() {
if j > 0 {
write!(f, " ")?;
}
write!(f, "{node}")?;
}
return Ok(());
}
write!(f, "(pipe ")?;
for (j, node) in groups[idx].iter().enumerate() {
if j > 0 {
write!(f, " ")?;
}
write!(f, "{node}")?;
}
write!(f, " ")?;
write_pipeline_groups(f, groups, idx + 1)?;
write!(f, ")")
}
pub(super) fn write_list(f: &mut fmt::Formatter<'_>, items: &[ListItem]) -> fmt::Result {
if items.len() == 1 && items[0].operator.is_none() {
return write!(f, "{}", items[0].command);
}
let cmds: Vec<&Node> = items.iter().map(|i| &i.command).collect();
let ops: Vec<ListOperator> = items.iter().filter_map(|i| i.operator).collect();
write_list_left_assoc(f, &cmds, &ops)
}
fn write_list_left_assoc(
f: &mut fmt::Formatter<'_>,
items: &[&Node],
ops: &[ListOperator],
) -> fmt::Result {
if items.len() == 1 && ops.len() == 1 {
let sexp_op = list_op_name(ops[0]);
return write!(f, "({sexp_op} {})", items[0]);
}
if items.len() <= 1 && ops.is_empty() {
if let Some(item) = items.first() {
return write!(f, "{item}");
}
return Ok(());
}
if items.len() == ops.len() {
let sexp_op = list_op_name(ops[ops.len() - 1]);
write!(f, "({sexp_op} ")?;
write_list_left_assoc(f, items, &ops[..ops.len() - 1])?;
return write!(f, ")");
}
for i in (1..ops.len()).rev() {
write!(f, "({} ", list_op_name(ops[i]))?;
}
write!(f, "({} {} {})", list_op_name(ops[0]), items[0], items[1])?;
for i in 1..ops.len() {
write!(f, " {})", items[i + 1])?;
}
Ok(())
}
const fn list_op_name(op: ListOperator) -> &'static str {
match op {
ListOperator::And => "and",
ListOperator::Or => "or",
ListOperator::Semi => "semi",
ListOperator::Background => "background",
}
}
pub(super) fn write_in_list(f: &mut fmt::Formatter<'_>, words: Option<&[Node]>) -> fmt::Result {
if let Some(ws) = words {
write!(f, " (in")?;
for w in ws {
write!(f, " {w}")?;
}
write!(f, ")")?;
}
Ok(())
}