use super::*;
pub fn all_have_keys(children: &[VirtualNode]) -> bool {
!children.is_empty() && children.iter().all(VirtualNode::has_key)
}
pub fn diff_children(old: &[VirtualNode], new: &[VirtualNode]) -> Vec<DiffOp> {
if all_have_keys(old) && all_have_keys(new) {
diff_keyed(old, new)
} else {
diff_positional(old, new)
}
}
pub fn diff_keyed(old: &[VirtualNode], new: &[VirtualNode]) -> Vec<DiffOp> {
let mut ops: Vec<DiffOp> = Vec::new();
let mut new_key_set: HashSet<&str> = HashSet::with_capacity(new.len());
for new_child in new.iter() {
if let Some(key) = new_child.key() {
new_key_set.insert(key);
}
}
for (new_index, new_child) in new.iter().enumerate() {
let Some(key) = new_child.key() else {
continue;
};
let existed_in_old: bool = old.iter().any(|old_child| old_child.key() == Some(key));
if existed_in_old {
ops.push(DiffOp::Update { index: new_index });
} else {
ops.push(DiffOp::Insert {
index: new_index,
node: new_child.clone(),
});
}
}
for (old_index, old_child) in old.iter().enumerate().rev() {
let Some(key) = old_child.key() else {
continue;
};
if !new_key_set.contains(key) {
ops.push(DiffOp::Remove { index: old_index });
}
}
ops
}
pub fn diff_positional(old: &[VirtualNode], new: &[VirtualNode]) -> Vec<DiffOp> {
let mut ops: Vec<DiffOp> = Vec::new();
let common_len: usize = old.len().min(new.len());
for index in 0..common_len {
ops.push(DiffOp::Update { index });
}
if new.len() > old.len() {
for (offset, new_child) in new.iter().skip(common_len).enumerate() {
ops.push(DiffOp::Insert {
index: common_len + offset,
node: new_child.clone(),
});
}
} else if old.len() > new.len() {
for index in (common_len..old.len()).rev() {
ops.push(DiffOp::Remove { index });
}
}
ops
}