use std::cmp::Ordering;
use crate::options::RankedItem;
pub fn default_base_sort<T>(a: &RankedItem<T>, b: &RankedItem<T>) -> Ordering {
a.ranked_value.cmp(&b.ranked_value)
}
pub fn sort_ranked_values<T>(
a: &RankedItem<T>,
b: &RankedItem<T>,
base_sort: &dyn Fn(&RankedItem<T>, &RankedItem<T>) -> Ordering,
) -> Ordering {
b.rank
.partial_cmp(&a.rank)
.unwrap_or(Ordering::Equal)
.then_with(|| a.key_index.cmp(&b.key_index))
.then_with(|| base_sort(a, b))
}
#[cfg(test)]
mod tests {
use std::borrow::Cow;
use super::*;
use crate::ranking::Ranking;
const ITEM: &str = "";
fn make_ranked(
rank: Ranking,
ranked_value: &'static str,
key_index: usize,
) -> RankedItem<'static, &'static str> {
RankedItem {
item: &ITEM,
index: 0,
rank,
ranked_value: Cow::Borrowed(ranked_value),
key_index,
key_threshold: None,
}
}
#[test]
fn base_sort_alphabetical_less() {
let a = make_ranked(Ranking::Equal, "apple", 0);
let b = make_ranked(Ranking::Equal, "banana", 0);
assert_eq!(default_base_sort(&a, &b), Ordering::Less);
}
#[test]
fn base_sort_alphabetical_greater() {
let a = make_ranked(Ranking::Equal, "banana", 0);
let b = make_ranked(Ranking::Equal, "apple", 0);
assert_eq!(default_base_sort(&a, &b), Ordering::Greater);
}
#[test]
fn base_sort_alphabetical_equal() {
let a = make_ranked(Ranking::Equal, "same", 0);
let b = make_ranked(Ranking::Equal, "same", 0);
assert_eq!(default_base_sort(&a, &b), Ordering::Equal);
}
#[test]
fn base_sort_case_sensitive_ordering() {
let a = make_ranked(Ranking::Equal, "Apple", 0);
let b = make_ranked(Ranking::Equal, "apple", 0);
assert_eq!(default_base_sort(&a, &b), Ordering::Less);
}
#[test]
fn base_sort_empty_strings() {
let a = make_ranked(Ranking::Equal, "", 0);
let b = make_ranked(Ranking::Equal, "", 0);
assert_eq!(default_base_sort(&a, &b), Ordering::Equal);
}
#[test]
fn base_sort_empty_vs_nonempty() {
let a = make_ranked(Ranking::Equal, "", 0);
let b = make_ranked(Ranking::Equal, "z", 0);
assert_eq!(default_base_sort(&a, &b), Ordering::Less);
}
#[test]
fn higher_rank_sorts_first() {
let a = make_ranked(Ranking::StartsWith, "a", 0);
let b = make_ranked(Ranking::Contains, "b", 0);
assert_eq!(
sort_ranked_values(&a, &b, &default_base_sort),
Ordering::Less
);
}
#[test]
fn lower_rank_sorts_second() {
let a = make_ranked(Ranking::Contains, "a", 0);
let b = make_ranked(Ranking::StartsWith, "b", 0);
assert_eq!(
sort_ranked_values(&a, &b, &default_base_sort),
Ordering::Greater
);
}
#[test]
fn case_sensitive_equal_before_equal() {
let a = make_ranked(Ranking::CaseSensitiveEqual, "a", 0);
let b = make_ranked(Ranking::Equal, "b", 0);
assert_eq!(
sort_ranked_values(&a, &b, &default_base_sort),
Ordering::Less
);
}
#[test]
fn matches_variant_compared_by_sub_score() {
let a = make_ranked(Ranking::Matches(1.8), "a", 0);
let b = make_ranked(Ranking::Matches(1.2), "b", 0);
assert_eq!(
sort_ranked_values(&a, &b, &default_base_sort),
Ordering::Less
);
}
#[test]
fn matches_lower_sub_score_sorts_second() {
let a = make_ranked(Ranking::Matches(1.2), "a", 0);
let b = make_ranked(Ranking::Matches(1.8), "b", 0);
assert_eq!(
sort_ranked_values(&a, &b, &default_base_sort),
Ordering::Greater
);
}
#[test]
fn lower_key_index_sorts_first_when_ranks_equal() {
let a = make_ranked(Ranking::Contains, "z", 0);
let b = make_ranked(Ranking::Contains, "a", 2);
assert_eq!(
sort_ranked_values(&a, &b, &default_base_sort),
Ordering::Less
);
}
#[test]
fn higher_key_index_sorts_second_when_ranks_equal() {
let a = make_ranked(Ranking::Contains, "a", 5);
let b = make_ranked(Ranking::Contains, "z", 1);
assert_eq!(
sort_ranked_values(&a, &b, &default_base_sort),
Ordering::Greater
);
}
#[test]
fn key_index_ignored_when_ranks_differ() {
let a = make_ranked(Ranking::StartsWith, "z", 10);
let b = make_ranked(Ranking::Contains, "a", 0);
assert_eq!(
sort_ranked_values(&a, &b, &default_base_sort),
Ordering::Less
);
}
#[test]
fn base_sort_breaks_tie_when_rank_and_key_index_equal() {
let a = make_ranked(Ranking::Contains, "apple", 0);
let b = make_ranked(Ranking::Contains, "banana", 0);
assert_eq!(
sort_ranked_values(&a, &b, &default_base_sort),
Ordering::Less
);
}
#[test]
fn base_sort_reverse_alphabetical_when_rank_and_key_index_equal() {
let a = make_ranked(Ranking::Contains, "banana", 0);
let b = make_ranked(Ranking::Contains, "apple", 0);
assert_eq!(
sort_ranked_values(&a, &b, &default_base_sort),
Ordering::Greater
);
}
#[test]
fn all_equal_returns_equal() {
let a = make_ranked(Ranking::Contains, "same", 0);
let b = make_ranked(Ranking::Contains, "same", 0);
assert_eq!(
sort_ranked_values(&a, &b, &default_base_sort),
Ordering::Equal
);
}
#[test]
fn custom_base_sort_reverse_alphabetical() {
let reverse_sort =
|a: &RankedItem<&str>, b: &RankedItem<&str>| b.ranked_value.cmp(&a.ranked_value);
let a = make_ranked(Ranking::Contains, "apple", 0);
let b = make_ranked(Ranking::Contains, "banana", 0);
assert_eq!(sort_ranked_values(&a, &b, &reverse_sort), Ordering::Greater);
}
#[test]
fn custom_base_sort_by_original_index() {
let index_sort = |a: &RankedItem<&str>, b: &RankedItem<&str>| a.index.cmp(&b.index);
let mut a = make_ranked(Ranking::Contains, "x", 0);
a.index = 5;
let mut b = make_ranked(Ranking::Contains, "y", 0);
b.index = 2;
assert_eq!(sort_ranked_values(&a, &b, &index_sort), Ordering::Greater);
}
#[test]
fn custom_base_sort_not_reached_when_rank_differs() {
let panic_sort = |_a: &RankedItem<&str>, _b: &RankedItem<&str>| -> Ordering {
panic!("base_sort should not be called when ranks differ");
};
let a = make_ranked(Ranking::StartsWith, "a", 0);
let b = make_ranked(Ranking::Contains, "b", 0);
assert_eq!(sort_ranked_values(&a, &b, &panic_sort), Ordering::Less);
}
#[test]
fn custom_base_sort_not_reached_when_key_index_differs() {
let panic_sort = |_a: &RankedItem<&str>, _b: &RankedItem<&str>| -> Ordering {
panic!("base_sort should not be called when key_indexes differ");
};
let a = make_ranked(Ranking::Contains, "a", 0);
let b = make_ranked(Ranking::Contains, "b", 3);
assert_eq!(sort_ranked_values(&a, &b, &panic_sort), Ordering::Less);
}
#[test]
fn sort_by_produces_correct_order() {
let mut ranked: Vec<RankedItem<&str>> = vec![
make_ranked(Ranking::Contains, "cherry", 0),
make_ranked(Ranking::StartsWith, "apple", 0),
make_ranked(Ranking::Contains, "banana", 0),
];
ranked.sort_by(|a, b| sort_ranked_values(a, b, &default_base_sort));
assert_eq!(ranked[0].ranked_value, "apple");
assert_eq!(ranked[1].ranked_value, "banana");
assert_eq!(ranked[2].ranked_value, "cherry");
}
#[test]
fn sort_by_with_mixed_key_indexes() {
let mut ranked: Vec<RankedItem<&str>> = vec![
make_ranked(Ranking::Contains, "a", 2),
make_ranked(Ranking::Contains, "b", 0),
make_ranked(Ranking::Contains, "c", 1),
];
ranked.sort_by(|a, b| sort_ranked_values(a, b, &default_base_sort));
assert_eq!(ranked[0].ranked_value, "b"); assert_eq!(ranked[1].ranked_value, "c"); assert_eq!(ranked[2].ranked_value, "a"); }
#[test]
fn sort_by_all_three_levels() {
let mut ranked: Vec<RankedItem<&str>> = vec![
make_ranked(Ranking::Contains, "zebra", 0),
make_ranked(Ranking::Contains, "alpha", 0),
make_ranked(Ranking::Contains, "mango", 1),
make_ranked(Ranking::StartsWith, "banana", 0),
];
ranked.sort_by(|a, b| sort_ranked_values(a, b, &default_base_sort));
assert_eq!(ranked[0].ranked_value, "banana");
assert_eq!(ranked[1].ranked_value, "alpha");
assert_eq!(ranked[2].ranked_value, "zebra");
assert_eq!(ranked[3].ranked_value, "mango");
}
}