use std::cmp::Ordering;
use crate::timer;
use crate::todo;
use crate::todotxt;
#[derive(Debug, Clone, Default)]
pub struct Conf {
pub fields: Option<String>,
pub rev: bool,
}
pub(crate) fn cmp_opt_dates(d1: Option<chrono::NaiveDate>, d2: Option<chrono::NaiveDate>) -> Ordering {
match (&d1, &d2) {
(None, None) => Ordering::Equal,
(Some(_), None) => Ordering::Less,
(None, Some(_)) => Ordering::Greater,
(Some(v1), Some(v2)) => v1.cmp(v2),
}
}
pub(crate) fn equal_opt_rec(r1: &Option<todotxt::Recurrence>, r2: &Option<todotxt::Recurrence>) -> bool {
match (&r1, &r2) {
(None, None) => true,
(Some(_), None) | (None, Some(_)) => false,
(Some(v1), Some(v2)) => v1 == v2,
}
}
fn cmp_opt_arrays(a1: &[String], a2: &[String]) -> Ordering {
if a1.is_empty() && !a2.is_empty() {
return Ordering::Greater;
} else if !a1.is_empty() && a2.is_empty() {
return Ordering::Less;
} else if a1.is_empty() && a2.is_empty() {
return Ordering::Equal;
}
let max = if a1.len() > a2.len() { a2.len() } else { a1.len() };
let mut ord = Ordering::Equal;
for idx in 0..max {
let s1_low = a1[idx].to_lowercase();
let s2_low = a2[idx].to_lowercase();
ord = s1_low.cmp(&s2_low);
if ord != Ordering::Equal {
break;
}
}
if ord == Ordering::Equal {
ord = a1.len().cmp(&a2.len())
}
ord
}
pub fn sort(ids: &mut todo::IDVec, todos: &todo::TaskSlice, c: &Conf) {
if c.fields.is_none() && !c.rev {
return;
}
let low: String;
let fields: Vec<&str> = match &c.fields {
None => Vec::new(),
Some(v) => {
low = v.trim_start_matches([' ', '=']).to_lowercase();
low.split([',', ':']).collect()
}
};
if !fields.is_empty() {
ids.sort_by(|a, b| {
if *a >= todos.len() && *b >= todos.len() {
return Ordering::Equal;
} else if *a >= todos.len() {
return Ordering::Greater;
} else if *b >= todos.len() {
return Ordering::Less;
}
let mut res: Ordering = Ordering::Equal;
for f in &fields {
res = match *f {
"pri" | "priority" => todos[*a].priority.cmp(&todos[*b].priority),
"due" => cmp_opt_dates(todos[*a].due_date, todos[*b].due_date),
"thr" => cmp_opt_dates(todos[*a].threshold_date, todos[*b].threshold_date),
"completed" | "finished" => cmp_opt_dates(todos[*a].finish_date, todos[*b].finish_date),
"created" | "create" => cmp_opt_dates(todos[*a].create_date, todos[*b].create_date),
"subject" | "text" | "subj" => todos[*a].subject.cmp(&todos[*b].subject),
"done" => {
let f1 = if timer::is_timer_on(&todos[*a]) {
1
} else if todos[*a].recurrence.is_some() {
2
} else if todos[*a].finished {
3
} else {
0
};
let f2 = if timer::is_timer_on(&todos[*b]) {
1
} else if todos[*b].recurrence.is_some() {
2
} else if todos[*b].finished {
3
} else {
0
};
f1.cmp(&f2)
}
"proj" | "project" => cmp_opt_arrays(&todos[*a].projects, &todos[*b].projects),
"ctx" | "context" => cmp_opt_arrays(&todos[*a].contexts, &todos[*b].contexts),
"active" => {
let a_act = timer::is_timer_on(&todos[*a]);
let b_act = timer::is_timer_on(&todos[*b]);
b_act.cmp(&a_act)
}
"src" => {
let a_src = if let Some(s) = &todos[*a].source { s.name.clone() } else { String::new() };
let b_src = if let Some(s) = &todos[*b].source { s.name.clone() } else { String::new() };
a_src.cmp(&b_src)
}
_ => Ordering::Equal,
};
if res != Ordering::Equal {
break;
}
}
res
});
}
if c.rev {
ids.reverse();
}
}