use crate::RowSource;
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum SortDirection {
Ascending,
Descending,
None,
}
impl SortDirection {
pub fn next(self) -> SortDirection {
match self {
SortDirection::None => SortDirection::Ascending,
SortDirection::Ascending => SortDirection::Descending,
SortDirection::Descending => SortDirection::None,
}
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct SortState {
pub column: usize,
pub direction: SortDirection,
}
impl SortState {
pub fn new(column: usize, direction: SortDirection) -> Self {
Self { column, direction }
}
}
pub fn sort_indices<S: RowSource + ?Sized>(
source: &S,
column: usize,
direction: SortDirection,
) -> Vec<usize> {
let n = source.row_count();
let mut indices: Vec<usize> = (0..n).collect();
if direction == SortDirection::None {
return indices;
}
let keys: Vec<_> = (0..n)
.map(|i| {
let row = source.row(i);
row.into_iter().nth(column)
})
.collect();
indices.sort_by(|&a, &b| {
let ord = match (&keys[a], &keys[b]) {
(Some(ca), Some(cb)) => ca.compare(cb),
(Some(_), None) => std::cmp::Ordering::Greater,
(None, Some(_)) => std::cmp::Ordering::Less,
(None, None) => std::cmp::Ordering::Equal,
};
match direction {
SortDirection::Descending => ord.reverse(),
_ => ord,
}
});
indices
}
#[cfg(test)]
mod tests {
use super::*;
use crate::{Cell, ColumnDef};
struct Data {
rows: Vec<Vec<Cell>>,
cols: Vec<ColumnDef>,
}
impl RowSource for Data {
fn row_count(&self) -> usize {
self.rows.len()
}
fn row(&self, i: usize) -> Vec<Cell> {
self.rows[i].clone()
}
fn column_defs(&self) -> &[ColumnDef] {
&self.cols
}
}
fn data() -> Data {
Data {
rows: vec![
vec![Cell::Int(3), Cell::from("c")],
vec![Cell::Int(1), Cell::from("a")],
vec![Cell::Int(2), Cell::from("b")],
],
cols: vec![],
}
}
#[test]
fn ascending_by_int_column() {
let d = data();
let order = sort_indices(&d, 0, SortDirection::Ascending);
assert_eq!(order, vec![1, 2, 0]); }
#[test]
fn descending_by_int_column() {
let d = data();
let order = sort_indices(&d, 0, SortDirection::Descending);
assert_eq!(order, vec![0, 2, 1]); }
#[test]
fn none_is_identity() {
let d = data();
let order = sort_indices(&d, 0, SortDirection::None);
assert_eq!(order, vec![0, 1, 2]);
}
#[test]
fn text_column_sort() {
let d = data();
let order = sort_indices(&d, 1, SortDirection::Ascending);
assert_eq!(order, vec![1, 2, 0]);
}
#[test]
fn stable_multi_column() {
let d = Data {
rows: vec![
vec![Cell::Int(1), Cell::from("y")],
vec![Cell::Int(1), Cell::from("x")],
vec![Cell::Int(0), Cell::from("z")],
],
cols: vec![],
};
let by_secondary = sort_indices(&d, 1, SortDirection::Ascending);
let mut idx = by_secondary;
let keys: Vec<_> = (0..d.row_count()).map(|i| d.row(i)[0].clone()).collect();
idx.sort_by(|&a, &b| keys[a].compare(&keys[b]));
assert_eq!(idx, vec![2, 1, 0]);
}
#[test]
fn direction_cycles() {
assert_eq!(SortDirection::None.next(), SortDirection::Ascending);
assert_eq!(SortDirection::Ascending.next(), SortDirection::Descending);
assert_eq!(SortDirection::Descending.next(), SortDirection::None);
}
}