use super::task::{Priority, Task, TaskStatus};
#[derive(Debug, Clone, PartialEq, Default)]
pub enum Filter {
All,
#[default]
Pending,
InProgress,
Completed,
Archived,
DueToday,
DueThisWeek,
Overdue,
ByProject(String),
ByTag(String),
ByPriority(Priority),
}
impl Filter {
pub fn apply<'a>(&self, tasks: &'a [Task]) -> Vec<&'a Task> {
tasks.iter().filter(|task| self.matches(task)).collect()
}
pub fn matches(&self, task: &Task) -> bool {
match self {
Filter::All => true,
Filter::Pending => task.status == TaskStatus::Pending,
Filter::InProgress => task.status == TaskStatus::InProgress,
Filter::Completed => task.status == TaskStatus::Completed,
Filter::Archived => task.status == TaskStatus::Archived,
Filter::DueToday => task.is_due_today(),
Filter::DueThisWeek => task.is_due_this_week(),
Filter::Overdue => task.is_overdue(),
Filter::ByProject(project_id) => task.project_id.as_ref() == Some(project_id),
Filter::ByTag(tag) => task.tags.contains(tag),
Filter::ByPriority(priority) => task.priority == *priority,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub enum SortOrder {
#[default]
DueDateAsc,
DueDateDesc,
PriorityDesc,
PriorityAsc,
CreatedDesc,
CreatedAsc,
Alphabetical,
}
impl SortOrder {
pub fn apply(&self, tasks: &mut [&Task]) {
match self {
SortOrder::DueDateAsc => {
tasks.sort_by(|a, b| {
match (&a.due_date, &b.due_date) {
(Some(a_due), Some(b_due)) => a_due.cmp(b_due),
(Some(_), None) => std::cmp::Ordering::Less,
(None, Some(_)) => std::cmp::Ordering::Greater,
(None, None) => a.created_at.cmp(&b.created_at),
}
});
}
SortOrder::DueDateDesc => {
tasks.sort_by(|a, b| {
match (&a.due_date, &b.due_date) {
(Some(a_due), Some(b_due)) => b_due.cmp(a_due),
(Some(_), None) => std::cmp::Ordering::Less,
(None, Some(_)) => std::cmp::Ordering::Greater,
(None, None) => b.created_at.cmp(&a.created_at),
}
});
}
SortOrder::PriorityDesc => {
tasks.sort_by(|a, b| b.priority.cmp(&a.priority));
}
SortOrder::PriorityAsc => {
tasks.sort_by(|a, b| a.priority.cmp(&b.priority));
}
SortOrder::CreatedDesc => {
tasks.sort_by(|a, b| b.created_at.cmp(&a.created_at));
}
SortOrder::CreatedAsc => {
tasks.sort_by(|a, b| a.created_at.cmp(&b.created_at));
}
SortOrder::Alphabetical => {
tasks.sort_by(|a, b| a.title.cmp(&b.title));
}
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use chrono::{Duration, Utc};
#[test]
fn test_filter_all() {
let tasks = vec![Task::new("Task 1"), Task::new("Task 2")];
let filtered = Filter::All.apply(&tasks);
assert_eq!(filtered.len(), 2);
}
#[test]
fn test_filter_by_status() {
let task1 = Task::new("Pending");
let mut task2 = Task::new("Completed");
task2.complete();
let tasks = vec![task1.clone(), task2.clone()];
let pending = Filter::Pending.apply(&tasks);
assert_eq!(pending.len(), 1);
assert_eq!(pending[0].title, "Pending");
let completed = Filter::Completed.apply(&tasks);
assert_eq!(completed.len(), 1);
assert_eq!(completed[0].title, "Completed");
}
#[test]
fn test_filter_by_priority() {
let mut task1 = Task::new("Low");
task1.priority = Priority::Low;
let mut task2 = Task::new("High");
task2.priority = Priority::High;
let tasks = vec![task1, task2];
let high_priority = Filter::ByPriority(Priority::High).apply(&tasks);
assert_eq!(high_priority.len(), 1);
assert_eq!(high_priority[0].title, "High");
}
#[test]
fn test_filter_overdue() {
let mut task1 = Task::new("Overdue");
task1.due_date = Some(Utc::now() - Duration::hours(1));
let mut task2 = Task::new("Future");
task2.due_date = Some(Utc::now() + Duration::days(1));
let tasks = vec![task1, task2];
let overdue = Filter::Overdue.apply(&tasks);
assert_eq!(overdue.len(), 1);
assert_eq!(overdue[0].title, "Overdue");
}
#[test]
fn test_sort_by_priority() {
let mut task1 = Task::new("Low");
task1.priority = Priority::Low;
let mut task2 = Task::new("High");
task2.priority = Priority::High;
let mut task3 = Task::new("Medium");
task3.priority = Priority::Medium;
let tasks = vec![task1, task2, task3];
let mut refs: Vec<&Task> = tasks.iter().collect();
SortOrder::PriorityDesc.apply(&mut refs);
assert_eq!(refs[0].title, "High");
assert_eq!(refs[1].title, "Medium");
assert_eq!(refs[2].title, "Low");
}
#[test]
fn test_sort_alphabetical() {
let task1 = Task::new("Zebra");
let task2 = Task::new("Apple");
let task3 = Task::new("Mango");
let tasks = vec![task1, task2, task3];
let mut refs: Vec<&Task> = tasks.iter().collect();
SortOrder::Alphabetical.apply(&mut refs);
assert_eq!(refs[0].title, "Apple");
assert_eq!(refs[1].title, "Mango");
assert_eq!(refs[2].title, "Zebra");
}
#[test]
fn test_sort_due_date_with_none() {
let mut task_with_due = Task::new("Has due date");
task_with_due.due_date = Some(Utc::now() + Duration::days(1));
let task_no_due_1 = Task::new("No due date 1");
let mut task_no_due_2 = Task::new("No due date 2");
task_no_due_2.created_at = Utc::now() + Duration::seconds(1);
let tasks = vec![task_no_due_1, task_with_due, task_no_due_2];
let mut refs: Vec<&Task> = tasks.iter().collect();
SortOrder::DueDateAsc.apply(&mut refs);
assert_eq!(refs[0].title, "Has due date");
assert_eq!(refs[1].title, "No due date 1");
assert_eq!(refs[2].title, "No due date 2");
}
}