use crate::{
node::attribute::group_attributes_per_name,
patch::{
AddAttributes,
AppendChildren,
ChangeText,
RemoveAttributes,
RemoveNode,
ReplaceNode,
},
Attribute,
Element,
Node,
Patch,
};
use keyed_elements::diff_keyed_elements;
use std::{
cmp,
fmt,
mem,
};
mod keyed_elements;
pub fn diff_with_key<'a, NS, TAG, ATT, VAL, EVENT, MSG>(
old_node: &'a Node<NS, TAG, ATT, VAL, EVENT, MSG>,
new_node: &'a Node<NS, TAG, ATT, VAL, EVENT, MSG>,
key: &ATT,
) -> Vec<Patch<'a, NS, TAG, ATT, VAL, EVENT, MSG>>
where
TAG: PartialEq + fmt::Debug,
ATT: PartialEq + fmt::Debug,
NS: PartialEq + fmt::Debug,
VAL: PartialEq + fmt::Debug,
{
diff_recursive(
old_node,
new_node,
&mut 0,
&mut 0,
key,
&|_old, _new| false,
&|_old, _new| false,
)
}
pub fn diff_with_functions<'a, NS, TAG, ATT, VAL, EVENT, MSG, SKIP, REP>(
old_node: &'a Node<NS, TAG, ATT, VAL, EVENT, MSG>,
new_node: &'a Node<NS, TAG, ATT, VAL, EVENT, MSG>,
key: &ATT,
skip: &SKIP,
rep: &REP,
) -> Vec<Patch<'a, NS, TAG, ATT, VAL, EVENT, MSG>>
where
TAG: PartialEq + fmt::Debug,
ATT: PartialEq + fmt::Debug,
NS: 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,
{
diff_recursive(old_node, new_node, &mut 0, &mut 0, key, skip, rep)
}
pub fn increment_node_idx_to_descendant_count<NS, TAG, ATT, VAL, EVENT, MSG>(
node: &Node<NS, TAG, ATT, VAL, EVENT, MSG>,
cur_node_idx: &mut usize,
) {
match node {
Node::Element(element_node) => {
for child in element_node.get_children().iter() {
*cur_node_idx += 1;
increment_node_idx_to_descendant_count(&child, cur_node_idx);
}
}
Node::Text(_txt) => {
}
}
}
fn is_any_children_keyed<'a, NS, TAG, ATT, VAL, EVENT, MSG>(
element: &'a Element<NS, TAG, ATT, VAL, EVENT, MSG>,
key: &ATT,
) -> bool
where
ATT: PartialEq,
{
element
.get_children()
.iter()
.any(|child| is_keyed_node(child, key))
}
fn is_keyed_node<'a, NS, TAG, ATT, VAL, EVENT, MSG>(
node: &'a Node<NS, TAG, ATT, VAL, EVENT, MSG>,
key: &ATT,
) -> bool
where
ATT: PartialEq,
{
if let Some(attributes) = node.get_attributes() {
attributes.iter().any(|att| att.name == *key)
} else {
false
}
}
fn diff_recursive<'a, 'b, NS, TAG, ATT, VAL, EVENT, MSG, SKIP, REP>(
old_node: &'a Node<NS, TAG, ATT, VAL, EVENT, MSG>,
new_node: &'a Node<NS, TAG, ATT, VAL, EVENT, MSG>,
cur_node_idx: &'b mut usize,
new_node_idx: &'b mut usize,
key: &ATT,
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,
{
if skip(old_node, new_node) {
increment_node_idx_to_descendant_count(old_node, cur_node_idx);
increment_node_idx_to_descendant_count(new_node, new_node_idx);
return vec![];
}
if rep(old_node, new_node) {
let replace_patch = ReplaceNode::new(
old_node.tag(),
*cur_node_idx,
*new_node_idx,
&new_node,
)
.into();
increment_node_idx_to_descendant_count(old_node, cur_node_idx);
increment_node_idx_to_descendant_count(new_node, new_node_idx);
return vec![replace_patch];
}
let mut patches = vec![];
let mut replace =
mem::discriminant(old_node) != mem::discriminant(new_node);
if let (Node::Element(old_element), Node::Element(new_element)) =
(old_node, new_node)
{
if old_element.tag != new_element.tag {
replace = true;
}
}
if replace {
patches.push(
ReplaceNode::new(
old_node.tag(),
*cur_node_idx,
*new_node_idx,
&new_node,
)
.into(),
);
increment_node_idx_to_descendant_count(old_node, cur_node_idx);
increment_node_idx_to_descendant_count(new_node, new_node_idx);
return patches;
}
match (old_node, new_node) {
(Node::Text(old_text), Node::Text(new_text)) => {
if old_text != new_text {
let ct = ChangeText::new(
*cur_node_idx,
old_text,
*new_node_idx,
new_text,
);
patches.push(Patch::ChangeText(ct));
}
}
(Node::Element(old_element), Node::Element(new_element)) => {
if is_any_children_keyed(old_element, key)
|| is_any_children_keyed(new_element, key)
{
let keyed_patches = diff_keyed_elements(
old_element,
new_element,
key,
cur_node_idx,
new_node_idx,
skip,
rep,
);
patches.extend(keyed_patches);
} else {
let non_keyed_patches = diff_non_keyed_elements(
old_element,
new_element,
key,
cur_node_idx,
new_node_idx,
skip,
rep,
);
patches.extend(non_keyed_patches);
}
}
(Node::Text(_), Node::Element(_))
| (Node::Element(_), Node::Text(_)) => {
unreachable!("Unequal variant discriminants should already have been handled");
}
};
patches
}
fn diff_non_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_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 this_cur_node_idx = *cur_node_idx;
let mut patches = vec![];
let attributes_patches =
diff_attributes(old_element, new_element, cur_node_idx, new_node_idx);
patches.extend(attributes_patches);
let old_child_count = old_element.children.len();
let new_child_count = new_element.children.len();
let min_count = cmp::min(old_child_count, new_child_count);
for index in 0..min_count {
*cur_node_idx += 1;
*new_node_idx += 1;
let old_child = &old_element
.children
.get(index)
.expect("No old_node child node");
let new_child =
&new_element.children.get(index).expect("No new chold node");
let more_patches = diff_recursive(
old_child,
new_child,
cur_node_idx,
new_node_idx,
key,
skip,
rep,
);
patches.extend(more_patches);
}
if new_child_count > old_child_count {
let mut append_patch: Vec<(
usize,
&'a Node<NS, TAG, ATT, VAL, EVENT, MSG>,
)> = vec![];
for append_child in new_element.children.iter().skip(old_child_count) {
*new_node_idx += 1;
append_patch.push((*new_node_idx, append_child));
increment_node_idx_to_descendant_count(append_child, new_node_idx);
}
patches.push(
AppendChildren::new(
&old_element.tag,
this_cur_node_idx,
append_patch,
)
.into(),
)
}
if new_child_count < old_child_count {
for old_child in old_element.get_children().iter().skip(new_child_count)
{
*cur_node_idx += 1;
patches
.push(RemoveNode::new(old_child.tag(), *cur_node_idx).into());
increment_node_idx_to_descendant_count(old_child, cur_node_idx);
}
}
patches
}
fn diff_attributes<'a, 'b, NS, TAG, ATT, VAL, EVENT, MSG>(
old_element: &'a Element<NS, TAG, ATT, VAL, EVENT, MSG>,
new_element: &'a Element<NS, TAG, ATT, VAL, EVENT, MSG>,
cur_node_idx: &'b mut usize,
new_node_idx: &'b mut usize,
) -> Vec<Patch<'a, NS, TAG, ATT, VAL, EVENT, MSG>>
where
NS: PartialEq + fmt::Debug,
ATT: PartialEq + fmt::Debug,
VAL: PartialEq + fmt::Debug,
{
let mut patches = vec![];
let mut add_attributes: Vec<&Attribute<NS, ATT, VAL, EVENT, MSG>> = vec![];
let mut remove_attributes: Vec<&Attribute<NS, ATT, VAL, EVENT, MSG>> =
vec![];
let new_attributes_grouped =
group_attributes_per_name(new_element.get_attributes());
let old_attributes_grouped =
group_attributes_per_name(old_element.get_attributes());
for (new_attr_name, new_attrs) in new_attributes_grouped.iter() {
let old_attr_values = old_attributes_grouped
.iter()
.find(|(att_name, _)| att_name == new_attr_name)
.map(|(_, attrs)| {
attrs.iter().map(|attr| &attr.value).collect::<Vec<_>>()
});
let new_attr_values = new_attributes_grouped
.iter()
.find(|(att_name, _)| att_name == new_attr_name)
.map(|(_, attrs)| {
attrs.iter().map(|attr| &attr.value).collect::<Vec<_>>()
});
if let Some(old_attr_values) = old_attr_values {
let new_attr_values =
new_attr_values.expect("must have new attr values");
if old_attr_values != new_attr_values {
add_attributes.extend(new_attrs);
}
} else {
add_attributes.extend(new_attrs);
}
}
for (old_attr_name, old_attrs) in old_attributes_grouped.iter() {
if let Some(_pre_attr) = new_attributes_grouped
.iter()
.find(|(new_attr_name, _)| new_attr_name == old_attr_name)
{
} else {
remove_attributes.extend(old_attrs);
}
}
if !add_attributes.is_empty() {
patches.push(
AddAttributes::new(
&old_element.tag,
*cur_node_idx,
*new_node_idx,
add_attributes,
)
.into(),
);
}
if !remove_attributes.is_empty() {
patches.push(
RemoveAttributes::new(
&old_element.tag,
*cur_node_idx,
*new_node_idx,
remove_attributes,
)
.into(),
);
}
patches
}