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

mod diff;
mod node;

pub use diff::{diff, Edit};
pub use node::Node;