use crate::node::{Mark, Node};
use serde::{Deserialize, Serialize};
use serde_json::{Map, Value};
use std::fmt;
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(tag = "op", rename_all = "camelCase")]
pub enum Change {
SetAttr {
path: Vec<usize>,
key: String,
value: Value,
},
RemoveAttr {
path: Vec<usize>,
key: String,
},
SetText {
path: Vec<usize>,
text: Option<String>,
},
SetMarks {
path: Vec<usize>,
marks: Option<Vec<Mark>>,
},
SetExtra {
path: Vec<usize>,
key: String,
value: Value,
},
RemoveExtra {
path: Vec<usize>,
key: String,
},
Insert {
path: Vec<usize>,
index: usize,
node: Node,
},
Remove {
path: Vec<usize>,
index: usize,
},
Replace {
path: Vec<usize>,
node: Node,
},
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct ApplyError {
pub path: Vec<usize>,
}
impl fmt::Display for ApplyError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "apply: no node at path {:?}", self.path)
}
}
impl std::error::Error for ApplyError {}
impl Node {
pub fn diff(&self, other: &Node) -> Vec<Change> {
let mut out = Vec::new();
let mut path = Vec::new();
diff_node(self, other, &mut path, &mut out);
out
}
pub fn apply(&mut self, changes: &[Change]) -> std::result::Result<(), ApplyError> {
apply(self, changes)
}
}
pub fn diff(a: &Node, b: &Node) -> Vec<Change> {
a.diff(b)
}
pub fn apply(root: &mut Node, changes: &[Change]) -> std::result::Result<(), ApplyError> {
for change in changes {
apply_one(root, change)?;
}
Ok(())
}
fn diff_node(a: &Node, b: &Node, path: &mut Vec<usize>, out: &mut Vec<Change>) {
if a == b {
return; }
if a.node_type != b.node_type
|| empty_shape_mismatch(
a.attrs.as_ref().map(Map::is_empty),
b.attrs.as_ref().map(Map::is_empty),
)
|| empty_shape_mismatch(
a.content.as_ref().map(Vec::is_empty),
b.content.as_ref().map(Vec::is_empty),
)
{
out.push(Change::Replace {
path: path.clone(),
node: b.clone(),
});
return;
}
diff_attrs(a.attrs.as_ref(), b.attrs.as_ref(), path, out);
if a.text != b.text {
out.push(Change::SetText {
path: path.clone(),
text: b.text.clone(),
});
}
if a.marks != b.marks {
out.push(Change::SetMarks {
path: path.clone(),
marks: b.marks.clone(),
});
}
diff_extra(&a.extra, &b.extra, path, out);
diff_children(a.children(), b.children(), path, out);
}
fn empty_shape_mismatch(a_is_empty: Option<bool>, b_is_empty: Option<bool>) -> bool {
match b_is_empty {
Some(true) => a_is_empty != Some(true),
None => a_is_empty == Some(true),
Some(false) => false,
}
}
fn diff_attrs(
a: Option<&Map<String, Value>>,
b: Option<&Map<String, Value>>,
path: &mut [usize],
out: &mut Vec<Change>,
) {
let empty = Map::new();
let am = a.unwrap_or(&empty);
let bm = b.unwrap_or(&empty);
for (k, v) in bm {
if am.get(k) != Some(v) {
out.push(Change::SetAttr {
path: path.to_vec(),
key: k.clone(),
value: v.clone(),
});
}
}
for k in am.keys() {
if !bm.contains_key(k) {
out.push(Change::RemoveAttr {
path: path.to_vec(),
key: k.clone(),
});
}
}
}
fn diff_extra(
am: &Map<String, Value>,
bm: &Map<String, Value>,
path: &mut [usize],
out: &mut Vec<Change>,
) {
for (k, v) in bm {
if am.get(k) != Some(v) {
out.push(Change::SetExtra {
path: path.to_vec(),
key: k.clone(),
value: v.clone(),
});
}
}
for k in am.keys() {
if !bm.contains_key(k) {
out.push(Change::RemoveExtra {
path: path.to_vec(),
key: k.clone(),
});
}
}
}
enum Step {
Match,
Del(usize),
Ins(usize),
}
fn diff_children(a: &[Node], b: &[Node], path: &mut Vec<usize>, out: &mut Vec<Change>) {
let mut start = 0;
while start < a.len() && start < b.len() && a[start] == b[start] {
start += 1;
}
let mut ea = a.len();
let mut eb = b.len();
while ea > start && eb > start && a[ea - 1] == b[eb - 1] {
ea -= 1;
eb -= 1;
}
let am = &a[start..ea];
let bm = &b[start..eb];
if am.is_empty() && bm.is_empty() {
return;
}
let steps = lcs_align(am, bm);
let mut cursor = start; let mut dels: Vec<usize> = Vec::new();
let mut inss: Vec<usize> = Vec::new();
for step in steps {
match step {
Step::Match => {
flush_gap(am, bm, &dels, &inss, path, &mut cursor, out);
dels.clear();
inss.clear();
cursor += 1; }
Step::Del(i) => dels.push(i),
Step::Ins(j) => inss.push(j),
}
}
flush_gap(am, bm, &dels, &inss, path, &mut cursor, out);
}
fn flush_gap(
am: &[Node],
bm: &[Node],
dels: &[usize],
inss: &[usize],
path: &mut Vec<usize>,
cursor: &mut usize,
out: &mut Vec<Change>,
) {
let pairs = dels.len().min(inss.len());
for k in 0..pairs {
let (ai, bj) = (dels[k], inss[k]);
if am[ai].node_type == bm[bj].node_type {
path.push(*cursor);
diff_node(&am[ai], &bm[bj], path, out);
path.pop();
} else {
let mut child = path.clone();
child.push(*cursor);
out.push(Change::Replace {
path: child,
node: bm[bj].clone(),
});
}
*cursor += 1;
}
for _ in &dels[pairs..] {
out.push(Change::Remove {
path: path.clone(),
index: *cursor,
}); }
for &bj in &inss[pairs..] {
out.push(Change::Insert {
path: path.clone(),
index: *cursor,
node: bm[bj].clone(),
});
*cursor += 1;
}
}
fn lcs_align(a: &[Node], b: &[Node]) -> Vec<Step> {
let (m, n) = (a.len(), b.len());
if m == 0 {
return (0..n).map(Step::Ins).collect();
}
if n == 0 {
return (0..m).map(Step::Del).collect();
}
let mut dp = vec![vec![0u32; n + 1]; m + 1];
for i in (0..m).rev() {
for j in (0..n).rev() {
dp[i][j] = if a[i] == b[j] {
dp[i + 1][j + 1] + 1
} else {
dp[i + 1][j].max(dp[i][j + 1])
};
}
}
let mut steps = Vec::with_capacity(m.max(n));
let (mut i, mut j) = (0usize, 0usize);
while i < m && j < n {
if a[i] == b[j] {
steps.push(Step::Match);
i += 1;
j += 1;
} else if dp[i + 1][j] >= dp[i][j + 1] {
steps.push(Step::Del(i));
i += 1;
} else {
steps.push(Step::Ins(j));
j += 1;
}
}
while i < m {
steps.push(Step::Del(i));
i += 1;
}
while j < n {
steps.push(Step::Ins(j));
j += 1;
}
steps
}
fn node_at_mut<'a>(
root: &'a mut Node,
path: &[usize],
) -> std::result::Result<&'a mut Node, ApplyError> {
root.node_at_mut(path).ok_or_else(|| ApplyError {
path: path.to_vec(),
})
}
fn apply_one(root: &mut Node, change: &Change) -> std::result::Result<(), ApplyError> {
match change {
Change::SetAttr { path, key, value } => {
node_at_mut(root, path)?.set_attr(key.clone(), value.clone());
}
Change::RemoveAttr { path, key } => {
node_at_mut(root, path)?.remove_attr(key);
}
Change::SetText { path, text } => {
node_at_mut(root, path)?.text = text.clone();
}
Change::SetMarks { path, marks } => {
node_at_mut(root, path)?.marks = marks.clone();
}
Change::SetExtra { path, key, value } => {
node_at_mut(root, path)?
.extra
.insert(key.clone(), value.clone());
}
Change::RemoveExtra { path, key } => {
node_at_mut(root, path)?.extra.remove(key);
}
Change::Insert { path, index, node } => {
let parent = node_at_mut(root, path)?;
if *index > parent.child_count() {
let mut p = path.clone();
p.push(*index);
return Err(ApplyError { path: p });
}
parent.insert_child(*index, node.clone());
}
Change::Remove { path, index } => {
if node_at_mut(root, path)?.remove_child(*index).is_none() {
let mut p = path.clone();
p.push(*index);
return Err(ApplyError { path: p });
}
}
Change::Replace { path, node } => {
if path.is_empty() {
*root = node.clone();
} else {
let (parent_path, last) = path.split_at(path.len() - 1);
let parent = node_at_mut(root, parent_path)?;
if parent.replace_child(last[0], node.clone()).is_none() {
return Err(ApplyError { path: path.clone() });
}
}
}
}
Ok(())
}