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
115
116
117
118
119
120
121
122
123
124
125
extern crate chrono;

use chrono::DateTime;
use std::fmt::Debug;

pub trait Diff: Debug + PartialEq {
    fn diff<'a>(&'a self, other: &'a Self) -> Option<Vec<Difference<'a>>>;
}

/// Field that differs
#[derive(Debug)]
pub struct Difference<'a> {
    pub field: String,
    pub left: &'a Debug,
    pub right: &'a Debug,
}

macro_rules! impl_for_prim {
    ($t: ty) => {
        impl Diff for $t {
            fn diff<'a>(&'a self, other: &'a Self) -> Option<Vec<Difference<'a>>> {
                if self != other {
                    return Some(vec![Difference {
                        field: String::new(),
                        left: self,
                        right: other,
                    }]);
                }
                None
            }
        }       
    };
}



impl<'b> Diff for &'b str {
    fn diff<'a>(&'a self, other: &'a Self) -> Option<Vec<Difference<'a>>> {
        if self != other {
            return Some(vec![Difference {
                field: String::new(),
                left: self,
                right: other,
            }])
        }
        None
    }
}

impl<T: chrono::TimeZone> Diff for DateTime<T> {
    fn diff<'a>(&'a self, other: &'a Self) -> Option<Vec<Difference<'a>>> {
        if self != other {
            return Some(vec![Difference {
                field: String::new(),
                left: self,
                right: other,
            }]);
        }
        None
    }
}


impl<T> Diff for Option<T> where T: std::fmt::Debug + PartialEq + Diff {
    fn diff<'a>(&'a self, other: &'a Self) -> Option<Vec<Difference<'a>>> {
        match (self, other) {
            (&Some(ref left), &Some(ref right)) => {
                left.diff(right)
            }
            (&None, &Some(_)) => {
                Some(vec![Difference { field: format!("none"), left: self, right: other }])
            },
            (&Some(_), &None) => {
                Some(vec![Difference { field: format!("some"), left: self, right: other }])
            },
            (&None, &None) => None,
        }
    }
}


impl<T> Diff for [T] where T: Diff {
    fn diff<'a>(&'a self, other: &'a Self) -> Option<Vec<Difference<'a>>> {
        if self != other {
            let mut diffs = Vec::new();
            for (i, (left, right)) in self.iter().zip(other.iter()).enumerate() {
                if let Some(inner_diffs) = left.diff(right) {
                    for diff in inner_diffs {
                        let mut path = format!("[{}]", i);
                        if !diff.field.is_empty() {
                            path.push_str(".");
                        }
                        path.push_str(&diff.field);
                        diffs.push(Difference {
                            field: path,
                            left: diff.left,
                            right: diff.right,
                        });
                    }
                }
            }
            if diffs.len() > 0 {
                return Some(diffs)
            }
        }
        None
    }
}

impl_for_prim!(bool);
impl_for_prim!(isize);
impl_for_prim!(i8);
impl_for_prim!(i16);
impl_for_prim!(i32);
impl_for_prim!(i64);
impl_for_prim!(usize);
impl_for_prim!(u8);
impl_for_prim!(u16);
impl_for_prim!(u32);
impl_for_prim!(u64);
impl_for_prim!(f32);
impl_for_prim!(f64);
impl_for_prim!(char);
impl_for_prim!(String);
impl_for_prim!(chrono::NaiveDateTime);