use std::cmp::Ordering;
pub trait SortKey<T> {
type Key<'a>
where
T: 'a;
fn key<'a>(&self, item: &'a T) -> Self::Key<'a>;
fn item_cmp<'a, C>(&'a self, compare: &'a C) -> impl Fn(&T, &T) -> Ordering + 'a
where
C: for<'b> crate::compare::Compare<Self::Key<'b>>,
{
move |a, b| {
let ka = self.key(a);
let kb = self.key(b);
compare.compare(&ka, &kb)
}
}
}
#[derive(Clone, Copy)]
pub struct KeyCompare<SK, Cmp> {
sort_key: SK,
compare: Cmp,
}
impl<SK, Cmp> KeyCompare<SK, Cmp> {
pub fn new(sort_key: SK, compare: Cmp) -> Self {
Self { sort_key, compare }
}
}
impl<T, SK, Cmp> crate::compare::Compare<T> for KeyCompare<SK, Cmp>
where
SK: SortKey<T>,
Cmp: for<'a> crate::compare::Compare<SK::Key<'a>>,
{
fn compare(&self, a: &T, b: &T) -> Ordering {
let ka = self.sort_key.key(a);
let kb = self.sort_key.key(b);
self.compare.compare(&ka, &kb)
}
}
#[derive(Clone, Copy)]
pub struct Owned<F>(pub F);
impl<T, K, F> SortKey<T> for Owned<F>
where
F: Fn(&T) -> K,
{
type Key<'a>
= K
where
T: 'a;
fn key(&self, item: &T) -> K {
(self.0)(item)
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::compare::Natural;
#[test]
fn owned_adapter_extracts_key() {
let key_fn = Owned(|v: &(i32, &str)| v.0);
assert_eq!(
key_fn.key(&(42, "hello")),
42,
"Owned should extract the key via the closure"
);
}
#[test]
fn owned_adapter_works_with_closures_that_compute() {
let key_fn = Owned(|v: &Vec<i32>| v.iter().sum::<i32>());
assert_eq!(
key_fn.key(&vec![1, 2, 3]),
6,
"Owned should support arbitrary computation in the closure"
);
}
#[test]
fn borrowed_sort_key_returns_reference() {
struct SeqKey;
impl SortKey<(Vec<u8>, Vec<u8>)> for SeqKey {
type Key<'a> = &'a [u8];
fn key<'a>(&self, item: &'a (Vec<u8>, Vec<u8>)) -> &'a [u8] {
&item.0
}
}
let record = (b"ACGT".to_vec(), b"!!!!".to_vec());
let key = SeqKey.key(&record);
assert_eq!(
key, b"ACGT",
"a borrowed SortKey should return a reference into the item"
);
}
#[test]
fn item_cmp_orders_by_extracted_key() {
let key_fn = Owned(|v: &(i32, &str)| v.0);
let cmp = key_fn.item_cmp(&Natural);
let a = (1, "first");
let b = (3, "second");
let c = (2, "third");
assert_eq!(
cmp(&a, &b),
std::cmp::Ordering::Less,
"item_cmp should order by the extracted key"
);
assert_eq!(
cmp(&b, &c),
std::cmp::Ordering::Greater,
"item_cmp should order by the extracted key"
);
assert_eq!(
cmp(&a, &a),
std::cmp::Ordering::Equal,
"item_cmp should return Equal for identical keys"
);
}
#[test]
fn item_cmp_with_borrowed_key() {
struct NameKey;
impl SortKey<(String, i32)> for NameKey {
type Key<'a> = &'a str;
fn key<'a>(&self, item: &'a (String, i32)) -> &'a str {
&item.0
}
}
let cmp = NameKey.item_cmp(&Natural);
let alice = ("alice".to_string(), 1);
let bob = ("bob".to_string(), 2);
assert_eq!(
cmp(&alice, &bob),
std::cmp::Ordering::Less,
"item_cmp should work with borrowed keys"
);
}
#[test]
fn item_cmp_can_sort_a_slice() {
let key_fn = Owned(|v: &(i32, &str)| v.0);
let cmp = key_fn.item_cmp(&Natural);
let mut items = vec![(3, "c"), (1, "a"), (2, "b")];
items.sort_by(&cmp);
assert_eq!(
items,
vec![(1, "a"), (2, "b"), (3, "c")],
"item_cmp should produce a closure usable with sort_by"
);
}
}