1extern crate chrono;
2
3use chrono::DateTime;
4use std::fmt::Debug;
5
6pub trait Diff: Debug + PartialEq {
7 fn diff<'a>(&'a self, other: &'a Self) -> Option<Vec<Difference<'a>>>;
8}
9
10#[derive(Debug)]
12pub struct Difference<'a> {
13 pub field: String,
14 pub left: &'a Debug,
15 pub right: &'a Debug,
16}
17
18macro_rules! impl_for_prim {
19 ($t: ty) => {
20 impl Diff for $t {
21 fn diff<'a>(&'a self, other: &'a Self) -> Option<Vec<Difference<'a>>> {
22 if self != other {
23 return Some(vec![Difference {
24 field: String::new(),
25 left: self,
26 right: other,
27 }]);
28 }
29 None
30 }
31 }
32 };
33}
34
35
36
37impl<'b> Diff for &'b str {
38 fn diff<'a>(&'a self, other: &'a Self) -> Option<Vec<Difference<'a>>> {
39 if self != other {
40 return Some(vec![Difference {
41 field: String::new(),
42 left: self,
43 right: other,
44 }])
45 }
46 None
47 }
48}
49
50impl<T: chrono::TimeZone> Diff for DateTime<T> {
51 fn diff<'a>(&'a self, other: &'a Self) -> Option<Vec<Difference<'a>>> {
52 if self != other {
53 return Some(vec![Difference {
54 field: String::new(),
55 left: self,
56 right: other,
57 }]);
58 }
59 None
60 }
61}
62
63
64impl<T> Diff for Option<T> where T: std::fmt::Debug + PartialEq + Diff {
65 fn diff<'a>(&'a self, other: &'a Self) -> Option<Vec<Difference<'a>>> {
66 match (self, other) {
67 (&Some(ref left), &Some(ref right)) => {
68 left.diff(right)
69 }
70 (&None, &Some(_)) => {
71 Some(vec![Difference { field: format!("none"), left: self, right: other }])
72 },
73 (&Some(_), &None) => {
74 Some(vec![Difference { field: format!("some"), left: self, right: other }])
75 },
76 (&None, &None) => None,
77 }
78 }
79}
80
81
82impl<T> Diff for [T] where T: Diff {
83 fn diff<'a>(&'a self, other: &'a Self) -> Option<Vec<Difference<'a>>> {
84 if self != other {
85 let mut diffs = Vec::new();
86 for (i, (left, right)) in self.iter().zip(other.iter()).enumerate() {
87 if let Some(inner_diffs) = left.diff(right) {
88 for diff in inner_diffs {
89 let mut path = format!("[{}]", i);
90 if !diff.field.is_empty() {
91 path.push_str(".");
92 }
93 path.push_str(&diff.field);
94 diffs.push(Difference {
95 field: path,
96 left: diff.left,
97 right: diff.right,
98 });
99 }
100 }
101 }
102 if diffs.len() > 0 {
103 return Some(diffs)
104 }
105 }
106 None
107 }
108}
109
110impl_for_prim!(bool);
111impl_for_prim!(isize);
112impl_for_prim!(i8);
113impl_for_prim!(i16);
114impl_for_prim!(i32);
115impl_for_prim!(i64);
116impl_for_prim!(usize);
117impl_for_prim!(u8);
118impl_for_prim!(u16);
119impl_for_prim!(u32);
120impl_for_prim!(u64);
121impl_for_prim!(f32);
122impl_for_prim!(f64);
123impl_for_prim!(char);
124impl_for_prim!(String);
125impl_for_prim!(chrono::NaiveDateTime);