fret_ui_headless/table/
column_ordering.rs1use std::collections::HashMap;
2
3use super::{ColumnDef, ColumnId};
4
5pub type ColumnOrderState = Vec<ColumnId>;
10
11pub fn set_column_order(order: &mut ColumnOrderState, ids: impl IntoIterator<Item = ColumnId>) {
12 order.clear();
13 for id in ids {
14 order.push(id);
15 }
16}
17
18pub fn set_column_order_for<'a>(
19 order: &mut ColumnOrderState,
20 ids: impl IntoIterator<Item = &'a str>,
21) {
22 set_column_order(order, ids.into_iter().map(ColumnId::from));
23}
24
25pub fn move_column(order: &mut ColumnOrderState, id: &ColumnId, to_index: usize) {
26 if order.is_empty() {
27 order.push(id.clone());
28 return;
29 }
30
31 let from = order.iter().position(|c| c.as_ref() == id.as_ref());
32 let mut next: Vec<ColumnId> = Vec::with_capacity(order.len() + 1);
33
34 for (idx, c) in order.iter().enumerate() {
35 if Some(idx) == from {
36 continue;
37 }
38 next.push(c.clone());
39 }
40
41 let insert_at = to_index.min(next.len());
42 next.insert(insert_at, id.clone());
43 *order = next;
44}
45
46pub fn moved_column(order: &ColumnOrderState, id: &ColumnId, to_index: usize) -> ColumnOrderState {
47 let mut next = order.clone();
48 move_column(&mut next, id, to_index);
49 next
50}
51
52pub fn order_columns<'c, TData>(
53 columns: &'c [ColumnDef<TData>],
54 order: &[ColumnId],
55) -> Vec<&'c ColumnDef<TData>> {
56 if columns.is_empty() {
57 return Vec::new();
58 }
59 if order.is_empty() {
60 return columns.iter().collect();
61 }
62
63 let mut remaining: HashMap<&str, &ColumnDef<TData>> =
64 columns.iter().map(|c| (c.id.as_ref(), c)).collect();
65
66 let mut out: Vec<&ColumnDef<TData>> = Vec::with_capacity(columns.len());
67 for id in order {
68 if let Some(col) = remaining.remove(id.as_ref()) {
69 out.push(col);
70 }
71 }
72
73 for col in columns {
74 if remaining.remove(col.id.as_ref()).is_some() {
75 out.push(col);
76 }
77 }
78
79 out
80}
81
82#[cfg(test)]
83mod tests {
84 use super::*;
85
86 #[test]
87 fn order_columns_respects_explicit_order_then_appends_rest() {
88 #[derive(Debug)]
89 struct Item;
90
91 let columns = vec![
92 ColumnDef::<Item>::new("a"),
93 ColumnDef::<Item>::new("b"),
94 ColumnDef::<Item>::new("c"),
95 ];
96
97 let out = order_columns(&columns, &[ColumnId::from("c"), ColumnId::from("a")]);
98 let ids = out.iter().map(|c| c.id.as_ref()).collect::<Vec<_>>();
99
100 assert_eq!(ids, vec!["c", "a", "b"]);
101 }
102
103 #[test]
104 fn order_columns_ignores_unknown_ids() {
105 #[derive(Debug)]
106 struct Item;
107
108 let columns = vec![ColumnDef::<Item>::new("a"), ColumnDef::<Item>::new("b")];
109 let out = order_columns(&columns, &[ColumnId::from("x"), ColumnId::from("b")]);
110
111 let ids = out.iter().map(|c| c.id.as_ref()).collect::<Vec<_>>();
112 assert_eq!(ids, vec!["b", "a"]);
113 }
114
115 #[test]
116 fn set_column_order_preserves_input_order_including_duplicates() {
117 let mut order: ColumnOrderState = vec!["a".into()];
118 set_column_order_for(&mut order, ["b", "a", "b"]);
119 assert_eq!(
120 order.iter().map(|c| c.as_ref()).collect::<Vec<_>>(),
121 vec!["b", "a", "b"]
122 );
123 }
124
125 #[test]
126 fn move_column_inserts_when_missing_and_reorders_when_present() {
127 let mut order: ColumnOrderState = vec!["a".into(), "b".into(), "c".into()];
128 move_column(&mut order, &ColumnId::from("b"), 0);
129 assert_eq!(
130 order.iter().map(|c| c.as_ref()).collect::<Vec<_>>(),
131 vec!["b", "a", "c"]
132 );
133
134 move_column(&mut order, &ColumnId::from("x"), 1);
135 assert_eq!(
136 order.iter().map(|c| c.as_ref()).collect::<Vec<_>>(),
137 vec!["b", "x", "a", "c"]
138 );
139 }
140}