use crate::{
Attribute,
Element,
Node,
Patch,
};
use std::{
cmp::min,
mem,
};
pub fn diff<'a, T, EVENT, MSG>(
old: &'a Node<T, &'static str, EVENT, MSG>,
new: &'a Node<T, &'static str, EVENT, MSG>,
) -> Vec<Patch<'a, T, &'static str, EVENT, MSG>>
where
MSG: 'static,
EVENT: 'static,
T: PartialEq,
{
diff_with_key(old, new, &"key")
}
pub fn diff_with_key<'a, T, ATT, EVENT, MSG>(
old: &'a Node<T, ATT, EVENT, MSG>,
new: &'a Node<T, ATT, EVENT, MSG>,
key: &ATT,
) -> Vec<Patch<'a, T, ATT, EVENT, MSG>>
where
MSG: 'static,
EVENT: 'static,
T: PartialEq,
ATT: PartialEq + Ord + ToString + Clone,
{
diff_recursive(&old, &new, &mut 0, key)
}
fn diff_recursive<'a, 'b, T, ATT, EVENT, MSG>(
old: &'a Node<T, ATT, EVENT, MSG>,
new: &'a Node<T, ATT, EVENT, MSG>,
cur_node_idx: &'b mut usize,
key: &ATT,
) -> Vec<Patch<'a, T, ATT, EVENT, MSG>>
where
MSG: 'static,
EVENT: 'static,
T: PartialEq,
ATT: PartialEq + Ord + ToString + Clone,
{
let mut patches = vec![];
let mut replace = mem::discriminant(old) != mem::discriminant(new);
if let (Node::Element(old_element), Node::Element(new_element)) = (old, new)
{
if old_element.tag != new_element.tag {
replace = true;
}
let old_key_value = old_element.get_attr_value(key);
let new_key_value = new_element.get_attr_value(key);
if let (Some(old_key_value), Some(new_key_value)) =
(old_key_value, new_key_value)
{
replace = old_key_value != new_key_value;
}
}
if replace {
patches.push(Patch::Replace(
old.tag().expect("must have a tag"),
*cur_node_idx,
&new,
));
if let Node::Element(old_element_node) = old {
for child in old_element_node.children.iter() {
increment_node_idx_for_children(child, cur_node_idx);
}
}
return patches;
}
match (old, new) {
(Node::Text(old_text), Node::Text(new_text)) => {
if old_text != new_text {
patches.push(Patch::ChangeText(*cur_node_idx, &new_text));
}
}
(Node::Element(old_element), Node::Element(new_element)) => {
let attributes_patches =
diff_attributes(old_element, new_element, cur_node_idx);
patches.extend(attributes_patches);
let listener_patches =
diff_event_listener(old_element, new_element, cur_node_idx);
patches.extend(listener_patches);
let old_child_count = old_element.children.len();
let new_child_count = new_element.children.len();
if new_child_count > old_child_count {
let append_patch: Vec<&'a Node<T, ATT, EVENT, MSG>> =
new_element.children[old_child_count..].iter().collect();
patches.push(Patch::AppendChildren(
&old_element.tag,
*cur_node_idx,
append_patch,
))
}
if new_child_count < old_child_count {
patches.push(Patch::TruncateChildren(
&old_element.tag,
*cur_node_idx,
new_child_count,
))
}
let min_count = min(old_child_count, new_child_count);
for index in 0..min_count {
*cur_node_idx += 1;
let old_child = &old_element
.children
.get(index)
.expect("No old child node");
let new_child = &new_element
.children
.get(index)
.expect("No new chold node");
patches.append(&mut diff_recursive(
&old_child,
&new_child,
cur_node_idx,
key,
))
}
if new_child_count < old_child_count {
for child in old_element.children[min_count..].iter() {
increment_node_idx_for_children(child, cur_node_idx);
}
}
}
(Node::Text(_), Node::Element(_))
| (Node::Element(_), Node::Text(_)) => {
unreachable!(
"Unequal variant discriminants should already have been handled"
);
}
};
patches
}
fn diff_attributes<'a, 'b, T, ATT, EVENT, MSG>(
old_element: &'a Element<T, ATT, EVENT, MSG>,
new_element: &'a Element<T, ATT, EVENT, MSG>,
cur_node_idx: &'b mut usize,
) -> Vec<Patch<'a, T, ATT, EVENT, MSG>>
where
MSG: 'static,
EVENT: 'static,
ATT: PartialEq + Ord + ToString + Clone,
{
let mut patches = vec![];
let mut add_attributes: Vec<Attribute<ATT, EVENT, MSG>> = vec![];
let mut remove_attributes: Vec<ATT> = vec![];
for new_attr in new_element.attributes().iter() {
let old_attr_value = old_element.get_attr_value(&new_attr.name);
let new_attr_value = new_element.get_attr_value(&new_attr.name);
if old_attr_value.is_none() || old_attr_value != new_attr_value {
if let Some(new_attr_value) = new_attr_value {
add_attributes.push(Attribute {
namespace: new_attr.namespace,
name: new_attr.name.clone(),
value: new_attr_value.into(),
});
}
}
}
for old_attr in old_element.attributes().iter() {
if new_element.get_attr_value(&old_attr.name).is_some() {
} else {
remove_attributes.push(old_attr.name.clone());
}
}
if !add_attributes.is_empty() {
patches.push(Patch::AddAttributes(
&old_element.tag,
*cur_node_idx,
add_attributes,
));
}
if !remove_attributes.is_empty() {
patches.push(Patch::RemoveAttributes(
&old_element.tag,
*cur_node_idx,
remove_attributes,
));
}
patches
}
fn diff_event_listener<'a, 'b, T, ATT, EVENT, MSG>(
old_element: &'a Element<T, ATT, EVENT, MSG>,
new_element: &'a Element<T, ATT, EVENT, MSG>,
cur_node_idx: &'b mut usize,
) -> Vec<Patch<'a, T, ATT, EVENT, MSG>>
where
MSG: 'static,
EVENT: 'static,
ATT: PartialEq + Ord + ToString + Clone,
{
let mut patches = vec![];
let mut add_event_listener: Vec<&Attribute<ATT, EVENT, MSG>> = vec![];
let mut remove_event_listener: Vec<ATT> = vec![];
for new_event in new_element.events().iter() {
let old_event = old_element.get_event(&new_event.name);
if old_event.is_none() {
add_event_listener.push(new_event);
}
}
for old_event in old_element.events().iter() {
if new_element.get_event(&old_event.name).is_none() {
remove_event_listener.push(old_event.name.clone());
}
}
if !add_event_listener.is_empty() {
patches.push(Patch::AddEventListener(
&old_element.tag,
*cur_node_idx,
add_event_listener,
));
}
if !remove_event_listener.is_empty() {
patches.push(Patch::RemoveEventListener(
&old_element.tag,
*cur_node_idx,
remove_event_listener,
));
}
patches
}
fn increment_node_idx_for_children<T, ATT, EVENT, MSG>(
old: &Node<T, ATT, EVENT, MSG>,
cur_node_idx: &mut usize,
) where
ATT: Clone,
{
*cur_node_idx += 1;
if let Node::Element(element_node) = old {
for child in element_node.children.iter() {
increment_node_idx_for_children(&child, cur_node_idx);
}
}
}