use harn_parser::{DictEntry, HitlArg, Node, SNode};
pub(super) fn contains_pipe_placeholder(node: &SNode) -> bool {
match &node.node {
Node::Identifier(name) if name == "_" => true,
Node::FunctionCall { args, .. } => args.iter().any(contains_pipe_placeholder),
Node::MethodCall { object, args, .. } | Node::OptionalMethodCall { object, args, .. } => {
contains_pipe_placeholder(object) || args.iter().any(contains_pipe_placeholder)
}
Node::HitlExpr { args, .. } => args.iter().any(|arg| contains_pipe_placeholder(&arg.value)),
Node::BinaryOp { left, right, .. } => {
contains_pipe_placeholder(left) || contains_pipe_placeholder(right)
}
Node::UnaryOp { operand, .. } => contains_pipe_placeholder(operand),
Node::Ternary {
condition,
true_expr,
false_expr,
} => {
contains_pipe_placeholder(condition)
|| contains_pipe_placeholder(true_expr)
|| contains_pipe_placeholder(false_expr)
}
Node::Assignment { target, value, .. } => {
contains_pipe_placeholder(target) || contains_pipe_placeholder(value)
}
Node::RangeExpr { start, end, .. } => {
contains_pipe_placeholder(start) || contains_pipe_placeholder(end)
}
Node::ListLiteral(items) => items.iter().any(contains_pipe_placeholder),
Node::DictLiteral(entries)
| Node::StructConstruct {
fields: entries, ..
} => entries.iter().any(contains_pipe_placeholder_in_entry),
Node::EnumConstruct { args, .. } => args.iter().any(contains_pipe_placeholder),
Node::PropertyAccess { object, .. } | Node::OptionalPropertyAccess { object, .. } => {
contains_pipe_placeholder(object)
}
Node::SubscriptAccess { object, index }
| Node::OptionalSubscriptAccess { object, index } => {
contains_pipe_placeholder(object) || contains_pipe_placeholder(index)
}
Node::SliceAccess { object, start, end } => {
contains_pipe_placeholder(object)
|| start
.as_ref()
.is_some_and(|start| contains_pipe_placeholder(start))
|| end
.as_ref()
.is_some_and(|end| contains_pipe_placeholder(end))
}
Node::Spread(inner)
| Node::TryOperator { operand: inner }
| Node::TryStar { operand: inner } => contains_pipe_placeholder(inner),
_ => false,
}
}
pub(super) fn replace_pipe_placeholder(node: &SNode) -> SNode {
let new_node = match &node.node {
Node::Identifier(name) if name == "_" => Node::Identifier("__pipe".into()),
Node::FunctionCall {
name,
type_args,
args,
} => Node::FunctionCall {
name: name.clone(),
type_args: type_args.clone(),
args: args.iter().map(replace_pipe_placeholder).collect(),
},
Node::MethodCall {
object,
method,
args,
} => Node::MethodCall {
object: Box::new(replace_pipe_placeholder(object)),
method: method.clone(),
args: args.iter().map(replace_pipe_placeholder).collect(),
},
Node::OptionalMethodCall {
object,
method,
args,
} => Node::OptionalMethodCall {
object: Box::new(replace_pipe_placeholder(object)),
method: method.clone(),
args: args.iter().map(replace_pipe_placeholder).collect(),
},
Node::HitlExpr { kind, args } => Node::HitlExpr {
kind: *kind,
args: args
.iter()
.map(|arg| HitlArg {
name: arg.name.clone(),
value: replace_pipe_placeholder(&arg.value),
span: arg.span,
})
.collect(),
},
Node::BinaryOp { op, left, right } => Node::BinaryOp {
op: op.clone(),
left: Box::new(replace_pipe_placeholder(left)),
right: Box::new(replace_pipe_placeholder(right)),
},
Node::UnaryOp { op, operand } => Node::UnaryOp {
op: op.clone(),
operand: Box::new(replace_pipe_placeholder(operand)),
},
Node::Ternary {
condition,
true_expr,
false_expr,
} => Node::Ternary {
condition: Box::new(replace_pipe_placeholder(condition)),
true_expr: Box::new(replace_pipe_placeholder(true_expr)),
false_expr: Box::new(replace_pipe_placeholder(false_expr)),
},
Node::Assignment { target, value, op } => Node::Assignment {
target: Box::new(replace_pipe_placeholder(target)),
value: Box::new(replace_pipe_placeholder(value)),
op: op.clone(),
},
Node::RangeExpr {
start,
end,
inclusive,
} => Node::RangeExpr {
start: Box::new(replace_pipe_placeholder(start)),
end: Box::new(replace_pipe_placeholder(end)),
inclusive: *inclusive,
},
Node::ListLiteral(items) => {
Node::ListLiteral(items.iter().map(replace_pipe_placeholder).collect())
}
Node::DictLiteral(entries) => {
Node::DictLiteral(entries.iter().map(replace_pipe_placeholder_entry).collect())
}
Node::PropertyAccess { object, property } => Node::PropertyAccess {
object: Box::new(replace_pipe_placeholder(object)),
property: property.clone(),
},
Node::OptionalPropertyAccess { object, property } => Node::OptionalPropertyAccess {
object: Box::new(replace_pipe_placeholder(object)),
property: property.clone(),
},
Node::SubscriptAccess { object, index } => Node::SubscriptAccess {
object: Box::new(replace_pipe_placeholder(object)),
index: Box::new(replace_pipe_placeholder(index)),
},
Node::OptionalSubscriptAccess { object, index } => Node::OptionalSubscriptAccess {
object: Box::new(replace_pipe_placeholder(object)),
index: Box::new(replace_pipe_placeholder(index)),
},
Node::SliceAccess { object, start, end } => Node::SliceAccess {
object: Box::new(replace_pipe_placeholder(object)),
start: start
.as_ref()
.map(|start| Box::new(replace_pipe_placeholder(start))),
end: end
.as_ref()
.map(|end| Box::new(replace_pipe_placeholder(end))),
},
Node::EnumConstruct {
enum_name,
variant,
args,
} => Node::EnumConstruct {
enum_name: enum_name.clone(),
variant: variant.clone(),
args: args.iter().map(replace_pipe_placeholder).collect(),
},
Node::StructConstruct {
struct_name,
fields,
} => Node::StructConstruct {
struct_name: struct_name.clone(),
fields: fields.iter().map(replace_pipe_placeholder_entry).collect(),
},
Node::Spread(inner) => Node::Spread(Box::new(replace_pipe_placeholder(inner))),
Node::TryOperator { operand } => Node::TryOperator {
operand: Box::new(replace_pipe_placeholder(operand)),
},
Node::TryStar { operand } => Node::TryStar {
operand: Box::new(replace_pipe_placeholder(operand)),
},
_ => return node.clone(),
};
SNode::new(new_node, node.span)
}
fn contains_pipe_placeholder_in_entry(entry: &DictEntry) -> bool {
contains_pipe_placeholder(&entry.key) || contains_pipe_placeholder(&entry.value)
}
fn replace_pipe_placeholder_entry(entry: &DictEntry) -> DictEntry {
DictEntry {
key: replace_pipe_placeholder(&entry.key),
value: replace_pipe_placeholder(&entry.value),
}
}