#![forbid(unsafe_code)]
use std::cmp::Reverse;
use std::fmt::{self, Display, Formatter};
use std::path::PathBuf;
use chrono::NaiveDate;
use crate::{
App, TfError, files::FileStatus, modes::tasks_mode::RecurringStatus, priority::Priority,
};
#[derive(Clone, PartialEq)]
pub enum CompletionStatus {
Incomplete,
Completed,
}
impl Display for CompletionStatus {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
match self {
CompletionStatus::Incomplete => write!(f, "incomplete"),
CompletionStatus::Completed => write!(f, "completed"),
}
}
}
#[derive(Debug, Clone)]
pub struct TaskSet {
pub name: String,
pub priority: Option<Priority>,
pub due_date: Option<NaiveDate>,
pub completed_date: Option<NaiveDate>,
pub tasks: Vec<Task>,
pub tags: Vec<String>,
}
impl TaskSet {
pub fn new(
name: String,
priority: Option<Priority>,
due_date: Option<NaiveDate>,
completed_date: Option<NaiveDate>,
tags: Vec<String>,
) -> Self {
Self {
name,
priority,
due_date,
completed_date,
tasks: vec![],
tags,
}
}
}
#[derive(Debug, Clone)]
pub struct Task {
pub text: String,
pub completed: bool,
pub recurring: bool,
pub due_date: Option<NaiveDate>,
pub completed_date: Option<NaiveDate>,
pub priority: Option<Priority>,
pub tags: Vec<String>,
pub line: usize,
pub order: Option<usize>,
}
#[derive(Debug, Clone)]
pub struct RichTask {
pub task: Task,
pub task_set: String,
pub file_name: String,
pub file_path: PathBuf,
pub file_status: FileStatus,
}
impl RichTask {
pub fn collect(app: &App) -> Result<Vec<Self>, TfError> {
let mut files = app.filemode.files.clone();
match app.taskmode.file_status {
FileStatus::Active => {
files.retain(|f| f.status == FileStatus::Active);
}
FileStatus::Archived => {
files.retain(|f| f.status == FileStatus::Archived);
}
FileStatus::Stale => {
files.retain(|f| f.status == FileStatus::Stale);
}
}
let mut tasks = vec![];
for file in files {
for task_set in file.task_sets {
for task in task_set.tasks {
tasks.push(RichTask {
task,
task_set: task_set.name.clone(),
file_name: file.head[0].clone(),
file_path: file.file.clone(),
file_status: file.status.clone(),
})
}
}
}
tasks.retain(|task| match app.taskmode.completion_status {
CompletionStatus::Incomplete => !task.task.completed,
CompletionStatus::Completed => task.task.completed,
});
if !app.taskmode.tag_dialog.submitted_input.is_empty() {
tasks.retain(|task| {
let input = &app.taskmode.tag_dialog.submitted_input;
if input.starts_with('!') {
let input = input.strip_prefix('!').unwrap().to_string();
!task.task.tags.contains(&input)
} else {
task.task.tags.contains(input)
}
})
}
if !app.taskmode.search_dialog.submitted_input.is_empty() {
tasks.retain(|task| {
task.task
.text
.to_lowercase()
.contains(&app.taskmode.search_dialog.submitted_input)
})
}
if app.taskmode.recurring_status != RecurringStatus::All {
tasks.retain(|task| match app.taskmode.recurring_status {
RecurringStatus::All => true,
RecurringStatus::Recurring => task.task.recurring,
RecurringStatus::NonRecurring => !task.task.recurring,
});
}
tasks.sort_by_key(|task| Reverse(task.task.priority));
tasks.sort_by_key(|task| task.task.order.unwrap_or(9999));
match app.taskmode.completion_status {
CompletionStatus::Incomplete => {
let mut with_due_dates = tasks.clone();
with_due_dates.retain(|task| task.task.due_date.is_some());
with_due_dates.sort_by_key(|task| task.task.due_date);
let mut without_due_dates = tasks;
without_due_dates.retain(|task| task.task.due_date.is_none());
with_due_dates.append(&mut without_due_dates);
tasks = with_due_dates;
}
CompletionStatus::Completed => {
tasks.sort_by_key(|task| Reverse(task.task.completed_date));
}
}
Ok(tasks)
}
}