vault_tasks_core/
sorter.rs1use std::cmp::Ordering;
2
3use chrono::NaiveTime;
4use lexical_sort::lexical_cmp;
5use strum::EnumIter;
6use strum_macros::FromRepr;
7
8use super::task::{DueDate, Task};
9
10#[derive(Default, Clone, Copy, FromRepr, EnumIter, strum_macros::Display)]
11pub enum SortingMode {
12 #[default]
13 #[strum(to_string = "Due Date")]
14 ByDueDate,
15 #[strum(to_string = "Title")]
16 ByName,
17}
18
19impl SortingMode {
20 #[must_use]
21 pub fn next(self) -> Self {
22 match self {
23 Self::ByDueDate => Self::ByName,
24 Self::ByName => Self::ByDueDate,
25 }
26 }
27 pub fn sort(tasks: &mut [Task], sorter: Self) {
28 tasks.sort_by(|t1, t2| Self::cmp(t1, t2, sorter));
29 }
30
31 fn cmp_due_date(t1: &Task, t2: &Task) -> Ordering {
33 match (&t1.due_date, &t2.due_date) {
34 (DueDate::Day(d1), DueDate::Day(d2)) => d1.cmp(d2),
35 (DueDate::DayTime(d1), DueDate::DayTime(d2)) => d1.cmp(d2),
36 (DueDate::Day(d1), DueDate::DayTime(d2)) => d1.and_time(NaiveTime::default()).cmp(d2),
37 (DueDate::DayTime(d1), DueDate::Day(d2)) => d1.cmp(&d2.and_time(NaiveTime::default())),
38 (DueDate::NoDate, DueDate::Day(_) | DueDate::DayTime(_)) => Ordering::Greater,
39 (DueDate::Day(_) | DueDate::DayTime(_), DueDate::NoDate) => Ordering::Less,
40 _ => Ordering::Equal,
41 }
42 }
43 fn cmp(t1: &Task, t2: &Task, sorter: Self) -> Ordering {
51 let res_initial_sort = match sorter {
52 Self::ByDueDate => Self::cmp_due_date(t1, t2),
53 Self::ByName => lexical_cmp(&t1.name, &t2.name),
54 };
55
56 if !matches!(res_initial_sort, Ordering::Equal) {
57 return res_initial_sort;
58 }
59
60 let res = t1.state.cmp(&t2.state);
62 if !matches!(res, Ordering::Equal) {
63 return res;
64 }
65
66 let res = match sorter {
68 Self::ByDueDate => lexical_cmp(&t1.name, &t2.name),
69 Self::ByName => Self::cmp_due_date(t1, t2),
70 };
71 if !matches!(res, Ordering::Equal) {
72 return res;
73 }
74
75 t1.priority.cmp(&t2.priority)
76 }
77}
78#[cfg(test)]
79mod tests {
80
81 use insta::{assert_debug_snapshot, with_settings};
82
83 use super::SortingMode;
84 use crate::{parser::task::parse_task, task::Task, TasksConfig};
85 #[test]
86 fn task_sort_by_name() {
87 let mut source = [
88 "- [ ] test 10/11",
89 "- [ ] test 10/9",
90 "- [ ] test 10/10 p5",
91 "- [ ] test 10/10 10:00",
92 "- [x] zèbre",
93 "- [x] zzz",
94 "- [ ] zzz",
95 "- [ ] test 10/10 p2",
96 "- [x] test",
97 "- [ ] test2",
98 "- [ ] test 10/10 5:00",
99 "- [ ] abc",
100 ];
101 let config = TasksConfig {
102 use_american_format: true,
103 ..Default::default()
104 };
105 let mut tasks: Vec<Task> = source
106 .iter_mut()
107 .map(|input| parse_task(input, String::new(), &config).unwrap())
108 .collect();
109
110 let sorting_mode = SortingMode::ByName;
111 SortingMode::sort(&mut tasks, sorting_mode);
112
113 let tasks = tasks
114 .iter()
115 .map(|task| task.get_fixed_attributes(&config, 2))
116 .collect::<Vec<String>>();
117
118 with_settings!({
119 info=>&source,
120 description => "", }, {
122 assert_debug_snapshot!(tasks);
123 });
124 }
125 #[test]
126 fn task_sort_by_due_date() {
127 let mut source = [
128 "- [ ] test 10/11",
129 "- [ ] test 10/9",
130 "- [ ] test 10/10 p5",
131 "- [ ] test 10/10 10:00",
132 "- [x] zèbre",
133 "- [x] zzz",
134 "- [ ] zzz",
135 "- [ ] test 10/10 p2",
136 "- [x] test",
137 "- [ ] test2",
138 "- [ ] test 10/10 5:00",
139 "- [ ] abc",
140 ];
141 let config = TasksConfig {
142 use_american_format: true,
143 ..Default::default()
144 };
145 let mut tasks: Vec<Task> = source
146 .iter_mut()
147 .map(|input| parse_task(input, String::new(), &config).unwrap())
148 .collect();
149
150 let sorting_mode = SortingMode::ByDueDate;
151 SortingMode::sort(&mut tasks, sorting_mode);
152
153 let tasks = tasks
154 .iter()
155 .map(|task| task.get_fixed_attributes(&config, 2))
156 .collect::<Vec<String>>();
157
158 with_settings!({
159 info=>&source,
160 description => "", }, {
162 assert_debug_snapshot!(tasks);
163 });
164 }
165}