# mtplatx-doc-diff
**Semantic tree diff: `Document` → `Patch<Operation>`, replayable.**
The diff engine compares two `Document`s at the **semantic** level — never at
the source-text level — and emits an ordered `Patch` of `Operation`s that can
be replayed to transform `old` into `new`. The implementation is a Myers-style
block LCS over top-level blocks; nested structures (lists, tables, quotes)
re-run the same algorithm over their child vectors.
## Example
```rust
use mtplatx_doc_core::{Block, Document, Inline};
use mtplatx_doc_diff::{apply, diff, DiffStats, Operation};
let mut before = Document::new();
before.push(Block::heading(1, vec![Inline::from("A")]));
before.push(Block::paragraph(vec![Inline::from("one")]));
let mut after = before.clone();
after.blocks[0] = Block::heading(1, vec![Inline::from("B")]);
after.push(Block::paragraph(vec![Inline::from("new block")]));
let patch = diff(&before, &after);
let stats = DiffStats::from(&patch);
println!(
"{} insert(s), {} replace(s), edit distance {}",
stats.inserts,
stats.replaces,
patch.edit_distance(),
);
for op in &patch.operations {
match op {
Operation::Replace { index, .. } => println!("block {index} replaced"),
Operation::Insert { index, .. } => println!("inserted at {index}"),
Operation::UpdateMetadata { to, .. } => println!("metadata -> {to}"),
_ => {}
}
}
// Replay: apply(before, &patch) == after
let replayed = apply(before, &patch).unwrap();
assert_eq!(replayed, after);
```
## API
- `diff(&Document, &Document) -> Patch` — semantic block-level diff
- `apply(Document, &Patch) -> Result<Document, DiffError>` — replay the patch
- `Operation` — `Insert`, `Delete`, `Replace`, `Move`, `UpdateMetadata` (serde-tagged `op`)
- `Patch` — `operations`, `len()`, `is_empty()`, `edit_distance()`
- `DiffStats` — `From<&Patch>` summary counters
## License
Licensed under the MIT License (see the `license` field in `Cargo.toml`).
Part of the [Mtplatx](https://github.com/master8848/Mtplatx) document engine.