struct_diff/
lib.rs

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/// Field that differs
11#[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);