mod generic;
#[cfg(fmt_cmp_semver_exempt)]
mod spec;
use std::cmp::Ordering;
use std::fmt::{self, Debug, Display, Formatter};
use std::hash::{Hash, Hasher};
use std::mem;
use super::{FmtEq, FmtOrd};
#[cfg(not(fmt_cmp_semver_exempt))]
use self::generic as imp;
#[cfg(fmt_cmp_semver_exempt)]
use self::spec as imp;
#[cfg_attr(feature = "alloc", doc = " ```")]
#[cfg_attr(not(feature = "alloc"), doc = " ```ignore")]
#[derive(Clone, Copy, Debug)]
#[repr(transparent)]
pub struct Cmp<T: ?Sized = dyn Display>(pub T);
impl<T: Display + ?Sized> Cmp<T> {
#[must_use]
pub fn from_ref(value: &T) -> &Self {
fn inner<'a, T: ?Sized>(value: &'a T) -> &'a Cmp<T> {
unsafe { mem::transmute::<&'a T, &'a Cmp<T>>(value) }
}
inner(value)
}
#[cfg(feature = "alloc")]
#[must_use]
pub fn from_boxed(boxed: alloc::boxed::Box<T>) -> alloc::boxed::Box<Self> {
let leaked: &mut Cmp<T> = Cmp::from_mut(alloc::boxed::Box::leak(boxed));
unsafe { alloc::boxed::Box::<Cmp<T>>::from_raw(leaked) }
}
#[cfg(feature = "alloc")]
#[must_use]
pub fn into_boxed_inner(self: alloc::boxed::Box<Self>) -> alloc::boxed::Box<T> {
let leaked: &mut T = &mut alloc::boxed::Box::leak(self).0;
unsafe { alloc::boxed::Box::<T>::from_raw(leaked) }
}
#[cfg(feature = "alloc")]
fn from_mut(value: &mut T) -> &mut Self {
fn inner<'a, T: ?Sized>(value: &'a mut T) -> &'a mut Cmp<T> {
unsafe { mem::transmute::<&'a mut T, &'a mut Cmp<T>>(value) }
}
inner(value)
}
}
impl<T> AsRef<T> for Cmp<T> {
fn as_ref(&self) -> &T {
&self.0
}
}
impl<T: Default + Display> Default for Cmp<T> {
fn default() -> Self {
Cmp(T::default())
}
}
impl<'a, T: Display + ?Sized> From<&'a T> for &'a Cmp<T> {
fn from(t: &T) -> &Cmp<T> {
Cmp::from_ref(t)
}
}
#[cfg(feature = "alloc")]
impl<T: Display + ?Sized> From<alloc::boxed::Box<T>> for alloc::boxed::Box<Cmp<T>> {
fn from(boxed: alloc::boxed::Box<T>) -> Self {
Cmp::from_boxed(boxed)
}
}
impl<T: Display + ?Sized> Display for Cmp<T> {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
self.0.fmt(f)
}
}
impl<T: Display + ?Sized, U: Display + ?Sized> PartialEq<Cmp<U>> for Cmp<T> {
fn eq(&self, other: &Cmp<U>) -> bool {
eq(&self.0, &other.0)
}
}
impl<T: Display + ?Sized> Eq for Cmp<T> {}
impl<T: Display + ?Sized, U: Display + ?Sized> PartialOrd<Cmp<U>> for Cmp<T> {
fn partial_cmp(&self, other: &Cmp<U>) -> Option<Ordering> {
Some(cmp(&self.0, &other.0))
}
}
impl<T: Display + ?Sized> Ord for Cmp<T> {
fn cmp(&self, other: &Self) -> Ordering {
cmp(&self.0, &other.0)
}
}
impl<T: Display + ?Sized> Hash for Cmp<T> {
fn hash<H: Hasher>(&self, state: &mut H) {
hash(&self.0, state)
}
}
impl<T: Display + ?Sized> FmtEq for Cmp<T> {}
impl<T: Display + ?Sized> FmtOrd for Cmp<T> {}
#[must_use]
pub fn eq<T: Display + ?Sized, U: Display + ?Sized>(lhs: &T, rhs: &U) -> bool {
imp::eq(lhs, rhs)
}
#[must_use]
pub fn cmp<T: Display + ?Sized, U: Display + ?Sized>(lhs: &T, rhs: &U) -> Ordering {
imp::cmp(lhs, rhs)
}
pub fn hash<T: Display + ?Sized, H: Hasher>(hashee: &T, hasher: &mut H) {
imp::hash(hashee, hasher)
}
#[cfg(test)]
mod tests {
#[cfg(not(feature = "alloc"))]
extern crate alloc;
use alloc::string::ToString;
use std::fmt::{Debug, Formatter};
use super::*;
#[test]
fn fmt_cmp() {
#[derive(Debug)]
struct SplitFmt<'a>(&'a str, usize);
impl Display for SplitFmt<'_> {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
let SplitFmt(s, n) = *self;
let mut pos = 0;
s.split_inclusive(|_| {
let ret = n == 0 || (pos != 0 && pos % n == 0);
pos += 1;
ret
})
.try_for_each(|s| f.write_str(s))
}
}
#[track_caller]
fn check<T: Debug + Display, U: Debug + Display>(x: T, y: U) {
let (x_str, y_str) = (x.to_string(), y.to_string());
let expected = x_str.cmp(&y_str);
assert_eq!(cmp(&x, &y), expected);
assert_eq!(cmp(&y, &x), expected.reverse(), "rev");
assert_eq!(generic::cmp(&x, &y), expected, "generic");
assert_eq!(generic::cmp(&y, &x), expected.reverse(), "generic,rev");
for s in [&*x_str, &*y_str] {
for n in 0..s.len() {
let split = SplitFmt(s, n);
assert_eq!(split.to_string(), s, "`{:?}` is broken", split);
}
}
for (nx, ny) in (0..x_str.len()).flat_map(|i| (0..y_str.len()).map(move |j| (i, j))) {
let (x, y) = (SplitFmt(&x_str, nx), SplitFmt(&y_str, ny));
assert_eq!(cmp(&x, &y), expected, "{:?}", (nx, ny));
assert_eq!(cmp(&y, &x), expected.reverse(), "{:?},rev", (nx, ny));
assert_eq!(generic::cmp(&x, &y), expected, "generic,{:?}", (nx, ny));
assert_eq!(
generic::cmp(&y, &x),
expected.reverse(),
"generic,{:?},rev",
(nx, ny)
);
}
}
check("", "");
check("", 42);
check("abracadabra", "abracadabra");
check(0., -0.);
check(f64::NAN, f64::NAN);
check(42, 240);
check(42, 2);
check("abracadabra", "abracad");
check("abracadabra", "abrabanana");
}
#[test]
fn soundness() {
let _ = &Cmp::from_ref(&1);
#[cfg(feature = "alloc")]
{
let _ = Cmp::from_boxed(alloc::boxed::Box::new(1)).into_boxed_inner();
}
let _ = Cmp::from_ref(&std::fmt::Error);
#[cfg(feature = "alloc")]
{
let _ = Cmp::from_boxed(alloc::boxed::Box::new(std::fmt::Error)).into_boxed_inner();
}
let _ = Cmp::from_ref("hello");
#[cfg(feature = "alloc")]
{
let _ = Cmp::from_boxed(alloc::string::String::from("hello").into_boxed_str())
.into_boxed_inner();
}
let _ = <Cmp>::from_ref(&1);
#[cfg(feature = "alloc")]
{
let _ = <Cmp>::from_boxed(alloc::boxed::Box::new(1)).into_boxed_inner();
}
}
}