//! # Always-Equal //! //! Always-Equal lets you wrap `PartialEq` implementations around //! things that cannot be compared, like a `File`. //! //! To use Always-Equal, put these two conditional `use` //! statements in your module: //! //! ``` //! #[cfg (test)] //! use always_equal::test::AlwaysEqual; //! //! #[cfg (not (test))] //! use always_equal::prod::AlwaysEqual; //! ``` //! //! Then wrap `AlwaysEqual` around anything that needs //! PartialEq but can't possibly implement it: //! //! ``` //! use std::fs::File; //! use always_equal::test::AlwaysEqual; //! //! #[derive (Debug, PartialEq)] //! pub struct MyStruct { //! pub b: bool, //! pub i: i64, //! pub file: AlwaysEqual , //! } //! //! // In test code, you can create an empty wrapper using //! // `testing_blank`, which is equal to any other wrapper: //! //! let expected = MyStruct { //! b: true, //! i: 0, //! file: AlwaysEqual::testing_blank (), //! }; //! //! # let my_file = File::open ("Cargo.toml").unwrap (); //! let actual = MyStruct { //! b: true, //! i: 0, //! file: my_file.into (), //! }; //! //! assert_eq! (expected, actual); //! ``` //! //! This is implemented with `Option` in test mode. //! In production mode, wrappers are never equal to any other //! wrapper, and the `Option` is removed so that //! `sizeof::> () == sizeof::` //! _should_ be true. /// Should be used for non-test builds pub mod prod { use std::fmt; /// In prod mode, `AlwaysEqual ` has the same size as `T`. /// `Debug` and `Display` are passed through if `T` implements /// them. `PartialEq` always returns `false` in prod mode. /// /// Wrapping a string, checking its equality, and unwrapping it: /// /// ``` /// use always_equal::prod::AlwaysEqual; /// /// let s = "some string"; /// let a = AlwaysEqual::from (s); /// let b = AlwaysEqual::from (s); /// assert_ne! (a, b); /// let s2 = a.into_inner (); /// ``` pub struct AlwaysEqual { inner: T, } impl fmt::Debug for AlwaysEqual { fn fmt (&self, f: &mut fmt::Formatter) -> fmt::Result { self.inner.fmt (f) } } impl fmt::Display for AlwaysEqual { fn fmt (&self, f: &mut fmt::Formatter) -> fmt::Result { self.inner.fmt (f) } } impl AlwaysEqual { pub fn into_inner (self) -> T { self.inner } } impl From for AlwaysEqual { fn from (inner: T) -> Self { Self { inner, } } } impl PartialEq for AlwaysEqual { fn eq (&self, _other: &Self) -> bool { false } } } /// Should be used for test builds pub mod test { /// In test mode, `AlwaysEqual ` has the same size as /// `Option `. `Debug` is derived. `PartialEq` returns /// `true` if either operand is a testing blank. /// /// Wrapping a string, comparing it with a testing blank, /// and unwrapping it: /// /// ``` /// use always_equal::test::AlwaysEqual; /// /// let s = "some string"; /// let a = AlwaysEqual::from (s); /// let b = AlwaysEqual::testing_blank (); /// assert_eq! (a, b); /// let s2 = a.into_inner (); /// ``` #[derive (Debug)] pub struct AlwaysEqual { inner: Option , } impl AlwaysEqual { pub fn into_inner (self) -> T { match self.inner { Some (x) => x, None => unreachable! (), } } pub fn testing_blank () -> Self { Self { inner: None, } } } impl Default for AlwaysEqual { fn default () -> Self { Self::from (T::default ()) } } impl From for AlwaysEqual { fn from (inner: T) -> Self { Self { inner: Some (inner), } } } impl PartialEq for AlwaysEqual { fn eq (&self, other: &Self) -> bool { self.inner.is_none () || other.inner.is_none () } } } #[cfg (test)] mod tests { use std::fs::File; use super::test::*; // Can't impl Clone or PartialEq because of the File type CantCompare = Option ; #[derive (Debug, Default, PartialEq)] struct MyStruct { file: AlwaysEqual , name: &'static str, } #[test] fn test_1 () { let concrete_1 = MyStruct { file: None.into (), name: "my_struct", }; let concrete_2 = MyStruct { file: None.into (), name: "my_struct", }; let concrete_bad = MyStruct { file: None.into (), name: "not_my_struct", }; assert_ne! (concrete_1, concrete_2); assert_ne! (concrete_2, concrete_bad); assert_ne! (concrete_bad, concrete_1); let dummy_1 = MyStruct { file: AlwaysEqual::testing_blank (), name: "my_struct", }; let dummy_2 = MyStruct { file: AlwaysEqual::testing_blank (), name: "my_struct", }; let dummy_bad = MyStruct { file: AlwaysEqual::testing_blank (), name: "not_my_struct", }; assert_eq! (dummy_1, dummy_2); assert_ne! (dummy_2, dummy_bad); assert_ne! (dummy_bad, dummy_1); assert_eq! (concrete_1, dummy_1); assert_eq! (concrete_bad, dummy_bad); } #[test] fn test_2 () { let v1 = Vec::>::new (); let v2 = Vec::>::new (); assert_eq! (v1, v2); } }