use super::*;
use std::borrow::Cow;
#[derive(Clone, Copy, PartialEq, Eq)]
enum Connective {
And,
Or,
}
impl Connective {
fn is_marker(self, step: &Step) -> bool {
matches!(
(self, step),
(Connective::And, Step::AndInfix) | (Connective::Or, Step::OrInfix)
)
}
}
impl ScriptGraphTraversalSource {
pub(super) fn fold_connectives(&self, root: bool) -> Cow<'_, [Step]> {
if !self
.steps
.iter()
.any(|step| matches!(step, Step::AndInfix | Step::OrInfix))
{
return Cow::Borrowed(&self.steps);
}
let steps = self.fold_connective(self.steps.clone(), Connective::Or, root);
Cow::Owned(self.fold_connective(steps, Connective::And, root))
}
fn fold_connective(&self, mut steps: Vec<Step>, kind: Connective, root: bool) -> Vec<Step> {
let Some(at) = steps.iter().position(|step| kind.is_marker(step)) else {
return steps;
};
let mut start = 0;
if root {
while start < at && matches!(steps[start], Step::Source(_)) {
start += 1;
}
if start < at {
start += 1;
}
}
while start < at && matches!(steps[start], Step::As(_)) {
start += 1;
}
let tail = steps.split_off(start);
let mut operands: Vec<Vec<Step>> = vec![Vec::new()];
for step in tail {
if kind.is_marker(&step) {
operands.push(Vec::new());
} else if let Some(current) = operands.last_mut() {
current.push(step);
}
}
let children = operands
.into_iter()
.map(|operand| self.connective_child(operand))
.collect();
steps.push(match kind {
Connective::And => Step::And(children),
Connective::Or => Step::Or(children),
});
steps
}
fn connective_child(&self, steps: Vec<Step>) -> ScriptGraphTraversalSource {
let mut child = self.clone();
child.steps = steps;
child
}
}