1use crate::RowSource;
8
9#[derive(Clone, Copy, Debug, PartialEq, Eq)]
11pub enum SortDirection {
12 Ascending,
14 Descending,
16 None,
18}
19
20impl SortDirection {
21 pub fn next(self) -> SortDirection {
25 match self {
26 SortDirection::None => SortDirection::Ascending,
27 SortDirection::Ascending => SortDirection::Descending,
28 SortDirection::Descending => SortDirection::None,
29 }
30 }
31}
32
33#[derive(Clone, Copy, Debug, PartialEq, Eq)]
35pub struct SortState {
36 pub column: usize,
38 pub direction: SortDirection,
40}
41
42impl SortState {
43 pub fn new(column: usize, direction: SortDirection) -> Self {
45 Self { column, direction }
46 }
47}
48
49pub fn sort_indices<S: RowSource + ?Sized>(
56 source: &S,
57 column: usize,
58 direction: SortDirection,
59) -> Vec<usize> {
60 let n = source.row_count();
61 let mut indices: Vec<usize> = (0..n).collect();
62 if direction == SortDirection::None {
63 return indices;
64 }
65 let keys: Vec<_> = (0..n)
67 .map(|i| {
68 let row = source.row(i);
69 row.into_iter().nth(column)
70 })
71 .collect();
72 indices.sort_by(|&a, &b| {
73 let ord = match (&keys[a], &keys[b]) {
74 (Some(ca), Some(cb)) => ca.compare(cb),
75 (Some(_), None) => std::cmp::Ordering::Greater,
76 (None, Some(_)) => std::cmp::Ordering::Less,
77 (None, None) => std::cmp::Ordering::Equal,
78 };
79 match direction {
80 SortDirection::Descending => ord.reverse(),
81 _ => ord,
82 }
83 });
84 indices
85}
86
87#[cfg(test)]
88mod tests {
89 use super::*;
90 use crate::{Cell, ColumnDef};
91
92 struct Data {
93 rows: Vec<Vec<Cell>>,
94 cols: Vec<ColumnDef>,
95 }
96 impl RowSource for Data {
97 fn row_count(&self) -> usize {
98 self.rows.len()
99 }
100 fn row(&self, i: usize) -> Vec<Cell> {
101 self.rows[i].clone()
102 }
103 fn column_defs(&self) -> &[ColumnDef] {
104 &self.cols
105 }
106 }
107
108 fn data() -> Data {
109 Data {
110 rows: vec![
111 vec![Cell::Int(3), Cell::from("c")],
112 vec![Cell::Int(1), Cell::from("a")],
113 vec![Cell::Int(2), Cell::from("b")],
114 ],
115 cols: vec![],
116 }
117 }
118
119 #[test]
120 fn ascending_by_int_column() {
121 let d = data();
122 let order = sort_indices(&d, 0, SortDirection::Ascending);
123 assert_eq!(order, vec![1, 2, 0]); }
125
126 #[test]
127 fn descending_by_int_column() {
128 let d = data();
129 let order = sort_indices(&d, 0, SortDirection::Descending);
130 assert_eq!(order, vec![0, 2, 1]); }
132
133 #[test]
134 fn none_is_identity() {
135 let d = data();
136 let order = sort_indices(&d, 0, SortDirection::None);
137 assert_eq!(order, vec![0, 1, 2]);
138 }
139
140 #[test]
141 fn text_column_sort() {
142 let d = data();
143 let order = sort_indices(&d, 1, SortDirection::Ascending);
144 assert_eq!(order, vec![1, 2, 0]);
146 }
147
148 #[test]
149 fn stable_multi_column() {
150 let d = Data {
153 rows: vec![
154 vec![Cell::Int(1), Cell::from("y")],
155 vec![Cell::Int(1), Cell::from("x")],
156 vec![Cell::Int(0), Cell::from("z")],
157 ],
158 cols: vec![],
159 };
160 let by_secondary = sort_indices(&d, 1, SortDirection::Ascending);
161 let mut idx = by_secondary;
164 let keys: Vec<_> = (0..d.row_count()).map(|i| d.row(i)[0].clone()).collect();
166 idx.sort_by(|&a, &b| keys[a].compare(&keys[b]));
167 assert_eq!(idx, vec![2, 1, 0]);
170 }
171
172 #[test]
173 fn direction_cycles() {
174 assert_eq!(SortDirection::None.next(), SortDirection::Ascending);
175 assert_eq!(SortDirection::Ascending.next(), SortDirection::Descending);
176 assert_eq!(SortDirection::Descending.next(), SortDirection::None);
177 }
178}