always_equal 1.0.3

A wrapper for types that can't implement Eq
Documentation
//! # 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:
//! 
//! ```rust
//! #[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:
//! 
//! ```rust
//! use std::fs::File;
//! use always_equal::test::AlwaysEqual;
//! 
//! #[derive (Debug, PartialEq)]
//! pub struct MyStruct {
//! 	pub b: bool,
//! 	pub i: i64,
//! 	pub file: AlwaysEqual <File>,
//! }
//! 
//! // 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::<AlwaysEqual <T>> () == sizeof::<T>`
//! _should_ be true.

/// Should be used for non-test builds

pub mod prod {
	use std::fmt;
	
	/// In prod mode, `AlwaysEqual <T>` 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 <T> {
		inner: T,
	}
	
	impl <T: fmt::Debug> fmt::Debug for AlwaysEqual <T> {
		fn fmt (&self, f: &mut fmt::Formatter) -> fmt::Result {
			self.inner.fmt (f)
		}
	}
	
	impl <T: fmt::Display> fmt::Display for AlwaysEqual <T> {
		fn fmt (&self, f: &mut fmt::Formatter) -> fmt::Result {
			self.inner.fmt (f)
		}
	}
	
	impl <T> AlwaysEqual <T> {
		pub fn into_inner (self) -> T {
			self.inner
		}
	}
	
	impl <T> From <T> for AlwaysEqual <T> {
		fn from (inner: T) -> Self {
			Self {
				inner,
			}
		}
	}
	
	impl <T> PartialEq for AlwaysEqual <T> {
		fn eq (&self, _other: &Self) -> bool {
			false
		}
	}
}

/// Should be used for test builds

pub mod test {
	/// In test mode, `AlwaysEqual <T>` has the same size as
	/// `Option <T>`. `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 <T> {
		inner: Option <T>,
	}

	impl <T> AlwaysEqual <T> {
		pub fn into_inner (self) -> T {
			match self.inner {
				Some (x) => x,
				None => unreachable! (),
			}
		}
		
		pub fn testing_blank () -> Self {
			Self {
				inner: None,
			}
		}
	}

	impl <T: Default> Default for AlwaysEqual <T> {
		fn default () -> Self {
			Self::from (T::default ())
		}
	}

	impl <T> From <T> for AlwaysEqual <T> {
		fn from (inner: T) -> Self {
			Self {
				inner: Some (inner),
			}
		}
	}

	impl <T> PartialEq for AlwaysEqual <T> {
		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 <File>;

	#[derive (Debug, Default, PartialEq)]
	struct MyStruct
	{
		file: AlwaysEqual <CantCompare>,
		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::<AlwaysEqual <File>>::new ();
		let v2 = Vec::<AlwaysEqual <File>>::new ();
		
		assert_eq! (v1, v2);
	}
}