use super::funejson::Value; use super::dates_day_of_week::day_of_week; use super::todo_due_status::due_status; use super::todo_item::Todo; use super::todo_item_validate_todo::{todos_from_value, todos_to_value}; const SECTIONS: [(&str, &str); 7] = [ ("overdue", "Overdue"), ("today", "Today"), ("tomorrow", "Tomorrow"), ("this-week", "This week"), ("later", "Later"), ("none", "No date"), ("done", "Done"), ]; /// Todos in planner sections, fixed order, empty sections left out. Sections /// spanning several dates are sorted by due date, stably; the rest keep the /// caller's order. /// /// # Panics /// Panics on an impossible or malformed date. pub fn group_by_due(todos: &[Todo], today: &str) -> Vec { day_of_week(today); // a bad today is an error even for an empty list let statuses: Vec = todos.iter().map(|todo| due_status(todo, today)).collect(); let mut groups = Vec::new(); for (status, title) in SECTIONS.iter() { let mut members: Vec = todos .iter() .zip(statuses.iter()) .filter(|(_, s)| s.as_str() == *status) .map(|(todo, _)| todo.clone()) .collect(); if members.is_empty() { continue; } if matches!(*status, "overdue" | "this-week" | "later") { // sort_by is stable, so equal dates keep input order. members.sort_by(|a, b| a.due.cmp(&b.due)); } groups.push(DueGroup { status: status.to_string(), title: title.to_string(), todos: members }); } groups } pub fn due_group_to_value(group: &DueGroup) -> Value { Value::obj(vec![ ("status", Value::str(&group.status)), ("title", Value::str(&group.title)), ("todos", todos_to_value(&group.todos)), ]) } pub fn fune_vector(args: &[Value]) -> Value { let groups = group_by_due(&todos_from_value(&args[0]), args[1].as_str()); Value::Arr(groups.iter().map(due_group_to_value).collect()) }