tree_edit_distance/
lib.rs

1//! # Overview
2//!
3//! This crate provides an implementation of a recursive generalized version of the
4//! [Levenshtein distance][levenshtein] for arbitrary sequences that finds the smallest possible
5//! diff between two trees, according to a user-defined measure for the cost of inserting and
6//! removing nodes. The smallest possible diff is defined by the the lowest cost sequence of edits
7//! that transforms one tree into the other.
8//!
9//! [levenshtein]:  https://en.wikipedia.org/wiki/Levenshtein_distance
10//!
11//! # Example
12//!
13//! ```rust
14//! use tree_edit_distance::*;
15//! use std::mem::{discriminant, Discriminant};
16//! use std::iter::empty;
17//!
18//! enum Json {
19//!     Null,
20//!     Bool(bool),
21//!     Number(f64),
22//!     String(String),
23//!     Array(Vec<Json>),
24//!     Map(Vec<(String, Json)>),
25//! }
26//!
27//! impl Node for Json {
28//!     type Kind = Discriminant<Json>;
29//!     fn kind(&self) -> Self::Kind {
30//!         discriminant(self)
31//!     }
32//!
33//!     type Weight = u64;
34//!     fn weight(&self) -> Self::Weight {
35//!         1
36//!     }
37//! }
38//!
39//! impl Tree for Json {
40//!     type Children<'c> = Box<dyn Iterator<Item = &'c Self> + 'c>
41//!     where
42//!         Self: 'c;
43//!
44//!     fn children(&self) -> Self::Children<'_> {
45//!         match self {
46//!             Json::Array(a) => Box::new(a.iter()),
47//!             Json::Map(m) => Box::new(m.iter().map(|(_, v)| v)),
48//!             _ => Box::new(empty()),
49//!         }
50//!     }
51//! }
52//! #
53//! # impl From<serde_json::Value> for Json {
54//! #     fn from(obj: serde_json::Value) -> Self {
55//! #         use serde_json::Value::*;
56//! #         match obj {
57//! #             Null => Json::Null,
58//! #             Bool(b) => Json::Bool(b),
59//! #             Number(n) => Json::Number(n.as_i64().unwrap() as f64),
60//! #             String(s) => Json::String(s),
61//! #             Array(a) => Json::Array(a.into_iter().map(Into::into).collect()),
62//! #             Object(m) => Json::Map(
63//! #                 m.into_iter()
64//! #                     .map(|(k, v)| (k, v.into()))
65//! #                     .collect(),
66//! #             ),
67//! #         }
68//! #     }
69//! # }
70//!
71//! macro_rules! json {
72//!     ($( $tokens:tt )*) => {
73//!         // ...
74//! #         Json::from(::serde_json::json!({$($tokens)*}))
75//!     };
76//! }
77//!
78//! let john = json! {
79//!     "name": "John Doe",
80//!     "age": 43,
81//!     "phones": [
82//!         "+44 1234567",
83//!         "+44 2345678"
84//!     ]
85//! };
86//!
87//! let jane = json! {
88//!     "name": "Jane Doe",
89//!     "maiden name": "Smith",
90//!     "age": 40,
91//!     "phones": [
92//!         "+44 7654321",
93//!     ]
94//! };
95//!
96//! let (edits, cost) = diff(&john, &jane);
97//!
98//! assert_eq!(cost, 2);
99//!
100//! assert_eq!(&*edits, &[
101//!     Edit::Replace(Box::new([
102//!         Edit::Replace(Box::default()),      // "name"
103//!         Edit::Insert,                       // "maiden name"
104//!         Edit::Replace(Box::default()),      // "age"
105//!         Edit::Replace(Box::new([            // "phones"
106//!             Edit::Remove,
107//!             Edit::Replace(Box::default()),
108//!         ])),
109//!     ]))
110//! ]);
111//! ```
112
113mod diff;
114mod edit;
115mod tree;
116
117pub use diff::*;
118pub use edit::*;
119pub use tree::*;
120
121mod cost;
122mod fold;
123mod memoize;
124
125pub(crate) use cost::*;
126pub(crate) use fold::*;
127pub(crate) use memoize::*;