use super::{
diff_attributes,
diff_recursive,
increment_node_idx_to_descendant_count,
};
use crate::{
patch::{
AppendChildren,
InsertNode,
RemoveNode,
},
Element,
Node,
NodeIdx,
Patch,
};
use std::{
collections::BTreeMap,
fmt,
iter::FromIterator,
};
fn find_node_with_key<'a, NS, TAG, ATT, VAL, EVENT, MSG>(
hay_stack: &BTreeMap<
usize,
(Vec<&'a VAL>, &'a Node<NS, TAG, ATT, VAL, EVENT, MSG>),
>,
find_key: &Vec<&'a VAL>,
last_matched_node_idx: Option<usize>,
) -> Option<(usize, &'a Node<NS, TAG, ATT, VAL, EVENT, MSG>)>
where
NS: PartialEq + fmt::Debug,
TAG: PartialEq + fmt::Debug,
ATT: PartialEq + fmt::Debug,
VAL: PartialEq + fmt::Debug,
{
hay_stack.iter().find_map(|(node_idx, (key, node))| {
if key == find_key {
let last_matched_node_idx_val = last_matched_node_idx.unwrap_or(0);
if last_matched_node_idx.is_none()
|| *node_idx > last_matched_node_idx_val
{
Some((*node_idx, *node))
} else {
None
}
} else {
None
}
})
}
fn find_matched_new_child<'a, NS, TAG, ATT, VAL, EVENT, MSG>(
matched_old_new_keyed: &BTreeMap<
(usize, usize),
(
&'a Node<NS, TAG, ATT, VAL, EVENT, MSG>,
(NodeIdx, &'a Node<NS, TAG, ATT, VAL, EVENT, MSG>),
),
>,
find_old_idx: usize,
) -> Option<(usize, (NodeIdx, &'a Node<NS, TAG, ATT, VAL, EVENT, MSG>))> {
matched_old_new_keyed.iter().find_map(
|((old_idx, new_idx), (_, new_child))| {
if *old_idx == find_old_idx {
Some((*new_idx, *new_child))
} else {
None
}
},
)
}
fn find_child_node_with_idx<'a, NS, TAG, ATT, VAL, EVENT, MSG>(
haystack: &Vec<(usize, &'a Node<NS, TAG, ATT, VAL, EVENT, MSG>)>,
node_idx: usize,
) -> Option<(usize, &'a Node<NS, TAG, ATT, VAL, EVENT, MSG>)> {
haystack.iter().find_map(|(idx, node)| {
if *idx == node_idx {
Some((*idx, *node))
} else {
None
}
})
}
fn get_matched_old_new_idx<'a, NS, TAG, ATT, VAL, EVENT, MSG>(
matched_old_new_keyed: &BTreeMap<
(usize, usize),
(
&'a Node<NS, TAG, ATT, VAL, EVENT, MSG>,
(NodeIdx, &'a Node<NS, TAG, ATT, VAL, EVENT, MSG>),
),
>,
) -> (Vec<usize>, Vec<usize>) {
matched_old_new_keyed
.iter()
.map(|((old_idx, new_idx), _)| (old_idx, new_idx))
.unzip()
}
fn get_unmatched_children_node_idx<'a, NS, TAG, ATT, VAL, EVENT, MSG>(
child_nodes: &'a [Node<NS, TAG, ATT, VAL, EVENT, MSG>],
matched_idx: Vec<usize>,
) -> Vec<(usize, &'a Node<NS, TAG, ATT, VAL, EVENT, MSG>)> {
child_nodes
.iter()
.enumerate()
.filter(|(idx, _node)| !matched_idx.contains(&idx))
.collect()
}
pub fn diff_keyed_elements<'a, 'b, NS, TAG, ATT, VAL, EVENT, MSG, SKIP, REP>(
old_element: &'a Element<NS, TAG, ATT, VAL, EVENT, MSG>,
new_element: &'a Element<NS, TAG, ATT, VAL, EVENT, MSG>,
key: &ATT,
cur_node_idx: &'b mut usize,
new_element_node_idx: &'b mut usize,
skip: &SKIP,
rep: &REP,
) -> Vec<Patch<'a, NS, TAG, ATT, VAL, EVENT, MSG>>
where
NS: PartialEq + fmt::Debug,
TAG: PartialEq + fmt::Debug,
ATT: PartialEq + fmt::Debug,
VAL: PartialEq + fmt::Debug,
SKIP: Fn(
&'a Node<NS, TAG, ATT, VAL, EVENT, MSG>,
&'a Node<NS, TAG, ATT, VAL, EVENT, MSG>,
) -> bool,
REP: Fn(
&'a Node<NS, TAG, ATT, VAL, EVENT, MSG>,
&'a Node<NS, TAG, ATT, VAL, EVENT, MSG>,
) -> bool,
{
let mut patches = vec![];
let attributes_patches = diff_attributes(
old_element,
new_element,
cur_node_idx,
new_element_node_idx,
);
let old_keyed_elements: BTreeMap<
usize,
(Vec<&VAL>, &Node<NS, TAG, ATT, VAL, EVENT, MSG>),
> = BTreeMap::from_iter(
old_element.get_children().iter().enumerate().filter_map(
|(old_idx, old_child)| {
if let Some(old_key) = old_child.get_attribute_value(key) {
Some((old_idx, (old_key, old_child)))
} else {
None
}
},
),
);
let mut node_idx_new_elements: BTreeMap<
NodeIdx,
&Node<NS, TAG, ATT, VAL, EVENT, MSG>,
> = BTreeMap::new();
for new_child in new_element.get_children().iter() {
*new_element_node_idx += 1;
node_idx_new_elements.insert(*new_element_node_idx, new_child);
increment_node_idx_to_descendant_count(new_child, new_element_node_idx);
}
let mut new_keyed_elements: BTreeMap<
usize,
(Vec<&VAL>, (NodeIdx, &Node<NS, TAG, ATT, VAL, EVENT, MSG>)),
> = BTreeMap::new();
for (new_idx, (new_node_idx, new_child)) in
node_idx_new_elements.iter().enumerate()
{
if let Some(new_key) = new_child.get_attribute_value(key) {
new_keyed_elements
.insert(new_idx, (new_key, (*new_node_idx, new_child)));
}
}
let mut matched_old_new_keyed: BTreeMap<
(usize, usize),
(
&Node<NS, TAG, ATT, VAL, EVENT, MSG>,
(NodeIdx, &Node<NS, TAG, ATT, VAL, EVENT, MSG>),
),
> = BTreeMap::new();
let mut last_matched_old_idx = None;
let mut last_matched_new_idx = None;
for (new_idx, (new_key, new_element)) in new_keyed_elements.iter() {
if let Some((old_idx, old_element)) = find_node_with_key(
&old_keyed_elements,
new_key,
last_matched_old_idx,
) {
let last_matched_new_idx_val = last_matched_new_idx.unwrap_or(0);
if last_matched_new_idx.is_none()
|| *new_idx > last_matched_new_idx_val
{
last_matched_old_idx = Some(old_idx);
last_matched_new_idx = Some(*new_idx);
matched_old_new_keyed
.insert((old_idx, *new_idx), (old_element, *new_element));
} else {
}
}
}
let (matched_old_idx, matched_new_idx) =
get_matched_old_new_idx(&matched_old_new_keyed);
let mut unmatched_new_child: Vec<(
usize,
(NodeIdx, &'a Node<NS, TAG, ATT, VAL, EVENT, MSG>),
)> = vec![];
for (idx, (node_idx, new_element)) in
node_idx_new_elements.iter().enumerate()
{
if !matched_new_idx.contains(&idx) {
unmatched_new_child.push((idx, (*node_idx, new_element)));
}
}
let unmatched_old_child = get_unmatched_children_node_idx(
old_element.get_children(),
matched_old_idx,
);
let matched_old_new: BTreeMap<
(usize, usize),
(
&'a Node<NS, TAG, ATT, VAL, EVENT, MSG>,
(NodeIdx, &'a Node<NS, TAG, ATT, VAL, EVENT, MSG>),
),
> = BTreeMap::from_iter(
unmatched_new_child
.iter()
.filter_map(|(new_idx, new_child)| {
if let Some((old_idx, old_child)) =
find_child_node_with_idx(&unmatched_old_child, *new_idx)
{
Some(((old_idx, *new_idx), (old_child, *new_child)))
} else {
None
}
}),
);
matched_old_new_keyed.extend(matched_old_new);
let (_matched_old_idx_pass2, matched_new_idx_pass2) =
get_matched_old_new_idx(&matched_old_new_keyed);
let mut unmatched_new_child_pass2: Vec<(
usize,
NodeIdx,
&Node<NS, TAG, ATT, VAL, EVENT, MSG>,
)> = vec![];
for (idx, (new_node_idx, new_child)) in
node_idx_new_elements.iter().enumerate()
{
if !matched_new_idx_pass2.contains(&idx) {
unmatched_new_child_pass2.push((idx, *new_node_idx, new_child));
}
}
let mut matched_keyed_element_patches = vec![];
let mut remove_node_patches = vec![];
let mut insert_node_patches = vec![];
let mut already_inserted = vec![];
let new_child_excess_cur_node_idx = *cur_node_idx;
for (old_idx, old_child) in old_element.children.iter().enumerate() {
*cur_node_idx += 1;
if let Some((new_idx, (new_child_node_idx, new_child))) =
find_matched_new_child(&matched_old_new_keyed, old_idx)
{
for (idx, new_node_idx, unmatched) in unmatched_new_child_pass2
.iter()
.filter(|(idx, _, _)| *idx < new_idx)
{
if !already_inserted.contains(idx) {
insert_node_patches.push(
InsertNode::new(
Some(&old_element.tag),
*cur_node_idx,
*new_node_idx,
unmatched,
)
.into(),
);
already_inserted.push(*idx);
}
}
let mut this_new_child_node_idx = new_child_node_idx;
matched_keyed_element_patches.extend(diff_recursive(
old_child,
new_child,
cur_node_idx,
&mut this_new_child_node_idx,
key,
skip,
rep,
));
} else {
remove_node_patches
.push(RemoveNode::new(old_child.tag(), *cur_node_idx).into());
increment_node_idx_to_descendant_count(old_child, cur_node_idx);
}
}
let mut append_children_patches = vec![];
for (new_idx, new_node_idx, new_child) in unmatched_new_child_pass2.iter() {
if !already_inserted.contains(&new_idx) {
append_children_patches.push(
AppendChildren::new(
&old_element.tag,
new_child_excess_cur_node_idx,
vec![(*new_node_idx, new_child)],
)
.into(),
);
}
}
patches.extend(attributes_patches);
patches.extend(matched_keyed_element_patches);
patches.extend(insert_node_patches);
patches.extend(append_children_patches);
patches.extend(remove_node_patches);
patches
}