is_same/
lib.rs

1//! This crate provides the IsSame trait, which is specifically for
2//! diffing immutable data that has been transformed. The trait differs
3//! from PartialEq in some important ways:
4//!
5//! - Floating point values are compared by their bit patterns,
6//!   preventing NaN values from making a data structure permanently
7//!   compare as not equal to itself. This also lets you detect a value
8//!   changing from `-0.0` to `0.0`.
9//! - Referential equality is used to make comparisons more efficient.
10//!   The library assumes that the contents of `Rc<T>` and `Arc<T>` are
11//!   immutable and can't change, so they only need to be compared by
12//!   their pointers.
13//!
14//! There is also a `is-same-derive` crate which can automatically
15//! derive an IsSame implementation for your structs:
16//! ```rs
17//! use is_same_derive::IsSame;
18//!
19//! #[derive(IsSame)]
20//! struct MyStruct {
21//!     count: usize,
22//!     ch: char,
23//!     text: String,
24//! }
25//! ```
26
27#![forbid(missing_docs)]
28#![deny(clippy::all)]
29
30use std::any::TypeId;
31use std::collections::{BTreeMap, BTreeSet, HashMap, HashSet};
32use std::hash::{BuildHasher, Hash};
33use std::path::{Path, PathBuf};
34use std::rc::Rc;
35use std::sync::Arc;
36
37/// Compares two versions of a piece of data to see if it has changed.
38pub trait IsSame<Rhs = Self>
39where
40    Rhs: ?Sized,
41{
42    /// Returns true if the two values are identical.
43    fn is_same(&self, other: &Rhs) -> bool;
44
45    /// Equivalent to `!self.is_same(other)`.
46    fn is_not_same(&self, other: &Rhs) -> bool {
47        !self.is_same(other)
48    }
49}
50
51impl<T> IsSame for Rc<T>
52where
53    T: ?Sized,
54{
55    fn is_same(&self, other: &Self) -> bool {
56        Rc::ptr_eq(self, other)
57    }
58}
59
60impl<T> IsSame for Arc<T>
61where
62    T: ?Sized,
63{
64    fn is_same(&self, other: &Self) -> bool {
65        Arc::ptr_eq(self, other)
66    }
67}
68
69impl<T, Rhs> IsSame<Rhs> for Vec<T>
70where
71    T: IsSame,
72    Rhs: AsRef<[T]> + ?Sized,
73{
74    fn is_same(&self, other: &Rhs) -> bool {
75        let other = other.as_ref();
76        if self.as_ptr() == other.as_ptr() {
77            true
78        } else if self.len() != other.len() {
79            false
80        } else {
81            self.iter()
82                .zip(other.iter())
83                .all(|(left, right)| left.is_same(right))
84        }
85    }
86}
87
88impl<Key, Value> IsSame for BTreeMap<Key, Value>
89where
90    Key: IsSame + Ord,
91    Value: IsSame,
92{
93    fn is_same(&self, other: &Self) -> bool {
94        let mut left = self.iter();
95        let mut right = other.iter();
96
97        loop {
98            let a = left.next();
99            let b = right.next();
100            match (a, b) {
101                (None, None) => return true,
102                (Some((left_key, left_val)), Some((right_key, right_val)))
103                    if left_key == right_key =>
104                {
105                    if left_val.is_not_same(right_val) {
106                        return false;
107                    }
108                }
109                (_, _) => return false,
110            }
111        }
112    }
113}
114
115impl<Key> IsSame for BTreeSet<Key>
116where
117    Key: IsSame + Ord,
118{
119    fn is_same(&self, other: &Self) -> bool {
120        let mut left = self.iter();
121        let mut right = other.iter();
122
123        loop {
124            let a = left.next();
125            let b = right.next();
126            match (a, b) {
127                (None, None) => return true,
128                (Some(left_key), Some(right_key)) if left_key == right_key => (),
129                (_, _) => return false,
130            }
131        }
132    }
133}
134
135impl<Key, Value, State> IsSame for HashMap<Key, Value, State>
136where
137    Key: IsSame + Eq + Hash,
138    Value: IsSame,
139    State: BuildHasher,
140{
141    fn is_same(&self, other: &Self) -> bool {
142        // Both a fast path and required to make sure we don't miss any
143        // keys that exist in `other` but not `self`. Assumes that the
144        // Key type has a non-broken PartialEq implementation, which
145        // could cause two entries to have the same key.
146        if self.len() != other.len() {
147            return false;
148        }
149        for (left_key, left_val) in self {
150            if let Some(right_val) = other.get(left_key) {
151                if left_val.is_not_same(&right_val) {
152                    return false;
153                }
154            } else {
155                return false;
156            }
157        }
158
159        true
160    }
161}
162
163impl<Key, State> IsSame for HashSet<Key, State>
164where
165    Key: IsSame + Eq + Hash,
166    State: BuildHasher,
167{
168    fn is_same(&self, other: &Self) -> bool {
169        self == other
170    }
171}
172
173impl IsSame for f32 {
174    fn is_same(&self, other: &Self) -> bool {
175        self.to_bits() == other.to_bits()
176    }
177}
178
179impl IsSame for f64 {
180    fn is_same(&self, other: &Self) -> bool {
181        self.to_bits() == other.to_bits()
182    }
183}
184
185impl<'a, T> IsSame for &'a T
186where
187    T: IsSame + ?Sized + 'a,
188{
189    fn is_same(&self, other: &Self) -> bool {
190        if (*self as *const T) == (*other as *const T) {
191            true
192        } else {
193            (*self).is_same(other)
194        }
195    }
196}
197
198impl<'a, T> IsSame for [T]
199where
200    T: IsSame + 'a,
201{
202    fn is_same(&self, other: &Self) -> bool {
203        if self.len() != other.len() {
204            false
205        } else {
206            for i in 0..self.len() {
207                if self[i].is_not_same(&other[i]) {
208                    return false;
209                }
210            }
211            true
212        }
213    }
214}
215
216impl<Rhs> IsSame<Rhs> for PathBuf
217where
218    Rhs: AsRef<Path> + ?Sized,
219{
220    fn is_same(&self, other: &Rhs) -> bool {
221        self == other.as_ref()
222    }
223}
224
225impl<Rhs> IsSame<Rhs> for Path
226where
227    Rhs: AsRef<Path> + ?Sized,
228{
229    fn is_same(&self, other: &Rhs) -> bool {
230        self == other.as_ref()
231    }
232}
233
234macro_rules! simple_impl {
235    ($name:ty) => {
236        impl IsSame for $name {
237            fn is_same(&self, other: &Self) -> bool {
238                self == other
239            }
240        }
241    };
242}
243
244simple_impl!(u8);
245simple_impl!(u16);
246simple_impl!(u32);
247simple_impl!(u64);
248simple_impl!(u128);
249simple_impl!(usize);
250simple_impl!(i8);
251simple_impl!(i16);
252simple_impl!(i32);
253simple_impl!(i64);
254simple_impl!(i128);
255simple_impl!(isize);
256simple_impl!(bool);
257simple_impl!(char);
258simple_impl!(());
259simple_impl!(String);
260simple_impl!(str);
261simple_impl!(TypeId);
262
263macro_rules! tuple_impl {
264    ($($tyname:ident, $left:ident, $right:ident;)+) => {
265        impl<$($tyname),+> IsSame for ($($tyname,)+)
266        where
267            $($tyname : IsSame),+
268        {
269            fn is_same(&self, other: &Self) -> bool {
270                let ($(ref $left,)+) = self;
271                let ($(ref $right,)+) = other;
272                $( $left.is_same($right) )&&+
273            }
274        }
275    };
276}
277
278tuple_impl! {
279    T1, left, right;
280}
281
282tuple_impl! {
283    T1, left1, right1;
284    T2, left2, right2;
285}
286
287tuple_impl! {
288    T1, left1, right1;
289    T2, left2, right2;
290    T3, left3, right3;
291}
292
293tuple_impl! {
294    T1, left1, right1;
295    T2, left2, right2;
296    T3, left3, right3;
297    T4, left4, right4;
298}
299
300tuple_impl! {
301    T1, left1, right1;
302    T2, left2, right2;
303    T3, left3, right3;
304    T4, left4, right4;
305    T5, left5, right5;
306}
307
308tuple_impl! {
309    T1, left1, right1;
310    T2, left2, right2;
311    T3, left3, right3;
312    T4, left4, right4;
313    T5, left5, right5;
314    T6, left6, right6;
315}
316
317tuple_impl! {
318    T1, left1, right1;
319    T2, left2, right2;
320    T3, left3, right3;
321    T4, left4, right4;
322    T5, left5, right5;
323    T6, left6, right6;
324    T7, left7, right7;
325}
326
327tuple_impl! {
328    T1, left1, right1;
329    T2, left2, right2;
330    T3, left3, right3;
331    T4, left4, right4;
332    T5, left5, right5;
333    T6, left6, right6;
334    T7, left7, right7;
335    T8, left8, right8;
336}
337
338macro_rules! array_impl {
339    ($( $count:tt )+) => {$(
340        impl<T> IsSame for [T; $count]
341        where
342            T: IsSame,
343        {
344            fn is_same(&self, other: &Self) -> bool {
345                for i in 0..$count {
346                    if self[i].is_not_same(&other[i]) {
347                        return false;
348                    }
349                }
350                true
351            }
352        }
353    )+};
354}
355
356array_impl!(
357    0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
358    20 21 22 23 24 25 26 27 28 29 30 31 32
359);