use core::{cmp, fmt};
pub struct CmpByKey<'kf, KF, T> {
inner: T,
key_func: &'kf KF
}
impl<'kf, KF, T> CmpByKey<'kf, KF, T> {
pub fn new<'rkf>(value: T, key_func: &'rkf KF) -> Self where
'rkf: 'kf
{
Self{ inner: value, key_func: key_func }
}
pub fn remove_wrapper(self) -> T {
self.inner
}
fn get_key<K>(&self) -> K where
KF: Fn(&T) -> K
{
(self.key_func)(&self.inner)
}
}
impl<T, K, KF, OT, OKF, OK> PartialOrd<CmpByKey<'_, OKF, OT>> for CmpByKey<'_, KF, T> where
K: PartialOrd<OK>,
KF: Fn(&T) -> K,
OKF: Fn(&OT) -> OK,
{
fn partial_cmp(&self, other: &CmpByKey<'_, OKF, OT>) -> Option<cmp::Ordering> {
self.get_key().partial_cmp(&other.get_key())
}
}
impl<T, K: Ord, KF> Ord for CmpByKey<'_, KF, T> where
KF: Fn(&T) -> K
{
fn cmp(&self, other: &Self) -> cmp::Ordering {
self.get_key().cmp(&other.get_key())
}
}
impl<T, K, KF, OT, OKF, OK> PartialEq<CmpByKey<'_, OKF, OT>> for CmpByKey<'_, KF, T> where
K: PartialEq<OK>,
KF: Fn(&T) -> K,
OKF: Fn(&OT) -> OK
{
fn eq(&self, other: &CmpByKey<'_, OKF, OT>) -> bool {
self.get_key() == other.get_key()
}
}
impl<T, K: Eq, KF> Eq for CmpByKey<'_, KF, T> where KF: Fn(&T) -> K {}
impl<T: fmt::Debug, KF> fmt::Debug for CmpByKey<'_, KF, T> {
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
self.inner.fmt(f)
}
}
#[cfg(test)]
mod tests {
use super::CmpByKey;
#[test]
fn can_compare_two_i32_with_reversed_order() {
let i_to_key = |x: &i32| -> i32 { -*x };
let x32 = CmpByKey::new(32, &i_to_key);
let y33 = CmpByKey::new(33, &i_to_key);
assert!(x32 > y33, "should be in reversed order" );
}
#[test]
fn works_with_simple_references() {
let i_to_key = |x: &&i32| -> i32 { -**x };
let x32 = 32;
let x33 = 33;
let r32 = CmpByKey::new(&x32, &i_to_key);
let r33 = CmpByKey::new(&x33, &i_to_key);
assert!(r32 > r33, "should be in reversed order" );
}
#[test]
fn works_with_references() {
let i_to_key = |x: &&Vec<i32>| {x.len()};
let x32 = vec![32];
let x33 = vec![33,33];
let r32 = CmpByKey::new(&x32, &i_to_key);
let r33 = CmpByKey::new(&x33, &i_to_key);
assert!(r33 > r32, "should fit the length" );
}
#[test]
fn dosnt_have_to_be_the_same_closure() {
let x32 = CmpByKey::new(32, &|x: &i32| -> i32 { -*x });
let y33 = CmpByKey::new(33, &|x: &i32| -> i32 { -*x });
assert!(x32 > y33, "should be in reversed order" );
}
#[test]
fn can_compare_different_types_with_same_key() {
struct A {v: i32}
struct B {v: i32}
let a = CmpByKey::new(A{v:3}, &|t: &A| t.v);
let b = CmpByKey::new(B{v:4}, &|t: &B| t.v);
assert!(a < b)
}
}