📅 Initial task dates

This commit is contained in:
Maddie H 2023-02-22 21:51:19 +00:00
parent 9595df093e
commit 19e5011f27
No known key found for this signature in database
GPG Key ID: 64FAA9959751687D
5 changed files with 114 additions and 72 deletions

38
Cargo.lock generated
View File

@ -2,6 +2,20 @@
# It is not intended for manual editing. # It is not intended for manual editing.
version = 3 version = 3
[[package]]
name = "agenda"
version = "0.1.0"
dependencies = [
"chrono",
"clap",
"colored",
"dirs",
"fuzzydate",
"prettytable-rs",
"serde",
"serde_json",
]
[[package]] [[package]]
name = "android_system_properties" name = "android_system_properties"
version = "0.1.5" version = "0.1.5"
@ -265,6 +279,17 @@ dependencies = [
"libc", "libc",
] ]
[[package]]
name = "fuzzydate"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "10cc638ce39f54e307c2648a5cf89fb549a0b66bc59f9de933b51b1dfe53c813"
dependencies = [
"chrono",
"lazy_static",
"thiserror",
]
[[package]] [[package]]
name = "getrandom" name = "getrandom"
version = "0.2.8" version = "0.2.8"
@ -587,19 +612,6 @@ dependencies = [
"unicode-ident", "unicode-ident",
] ]
[[package]]
name = "tasks"
version = "0.1.0"
dependencies = [
"chrono",
"clap",
"colored",
"dirs",
"prettytable-rs",
"serde",
"serde_json",
]
[[package]] [[package]]
name = "term" name = "term"
version = "0.7.0" version = "0.7.0"

View File

@ -14,3 +14,4 @@ chrono = { version = "0.4.23", features = ["serde"] }
dirs = "4.0.0" dirs = "4.0.0"
colored = "2.0.0" colored = "2.0.0"
prettytable-rs = "0.10.0" prettytable-rs = "0.10.0"
fuzzydate = "0.2.1"

View File

@ -1,19 +1,21 @@
use chrono::NaiveDateTime;
use crate::args::{TasksArgs, Commands}; use crate::args::{TasksArgs, Commands};
use crate::args::{CreateTask, DeleteTask, ShowTask, StartTask, StopTask, CompleteTask}; use crate::args::{CreateTask, DeleteTask, ShowTask, StartTask, StopTask, CompleteTask};
use crate::tasks::{Tasks, Task, Status}; use crate::tasks::{Tasks, Task, Status};
use prettytable::{Table, Row, row, format}; use prettytable::{Table, Row, row, format};
use colored::*; use colored::*;
use fuzzydate;
fn success(msg: String) { pub fn success(msg: String) {
println!("{} {}", "success:".green().bold(), msg); println!("{} {}", "success:".green().bold(), msg);
} }
fn warning(msg: &str) { pub fn warning(msg: &str) {
println!("{} {}", "warning:".yellow().bold(), msg); println!("{} {}", "warning:".yellow().bold(), msg);
} }
#[allow(dead_code)] #[allow(dead_code)]
fn error(msg: String) { pub fn error(msg: String) {
println!("{} {}", "error:".red().bold(), msg); println!("{} {}", "error:".red().bold(), msg);
panic!(); panic!();
} }
@ -29,6 +31,14 @@ fn get_task(tasks: &mut Tasks, id: usize) -> Task {
} }
} }
fn parse_date(date_string: Option<String>) -> Option<NaiveDateTime> {
if date_string.is_some() {
Some(fuzzydate::parse(date_string.unwrap()).unwrap())
} else {
None
}
}
fn calc_row(task: &Task, id: usize) -> Row { fn calc_row(task: &Task, id: usize) -> Row {
if task.status == Status::Complete { if task.status == Status::Complete {
// Generate greyed out rows for complete tasks // Generate greyed out rows for complete tasks
@ -36,30 +46,73 @@ fn calc_row(task: &Task, id: usize) -> Row {
task.status.as_string().bright_black().italic(), task.status.as_string().bright_black().italic(),
task.title.clone().bright_black().italic()]) task.title.clone().bright_black().italic()])
} else { } else {
let when = if task.when.is_some() {
format!("{}", task.when.unwrap().format("%Y-%m-%d")).bright_black()
} else {
String::from("N/A").bright_black()
};
// Generate normal colored rows for uncompleted tasks // Generate normal colored rows for uncompleted tasks
Row::from([id.to_string().cyan(), task.status.as_string(), task.title.clone().white()]) Row::from([id.to_string().cyan(), task.status.as_string(), when, task.title.clone().white()])
} }
} }
pub fn execute(tasks: &mut Tasks, arguments: TasksArgs) -> &mut Tasks { pub fn execute(tasks: &mut Tasks, arguments: TasksArgs) -> &mut Tasks {
match arguments.command { match arguments.command {
Commands::Add(CreateTask { title, .. }) => { Commands::Add(CreateTask { title, notes, tags, when, deadline, reminder, ..}) => {
let task = Task::new(title); let when = parse_date(when);
let deadline = parse_date(deadline);
let reminder = parse_date(reminder);
let tags: Option<Vec<String>> = if tags.is_some() {
Some(tags.unwrap().split(",").map(str::to_string).collect())
} else {
None
};
let task = Task::new(title, notes, tags, when, deadline, reminder);
tasks.add(task.clone()); tasks.add(task.clone());
let id = tasks.len() - 1; let id = tasks.len() - 1;
success(task_msg("created", &task, id)); success(task_msg("created", &task, id));
} }
Commands::Del(DeleteTask { id }) => { Commands::Del(DeleteTask { id }) => {
let mut binding = tasks.clone(); let task = get_task(tasks, id);
let task = match binding.get_task(id) {
Ok(task) => task,
Err(error) => panic!("error: {}", error),
};
tasks.del(id); tasks.del(id);
success(task_msg("deleted", task, id)); success(task_msg("deleted", &task, id));
} }
Commands::Done(CompleteTask { id }) => {
let task = get_task(&mut tasks.clone(), id);
tasks.set_status(id, Status::Complete);
success(task_msg("completed", &task, id));
}
Commands::Start(StartTask { id }) => {
let task = get_task(&mut tasks.clone(), id);
tasks.set_status(id, Status::Active);
success(task_msg("started", &task, id));
}
Commands::Stop(StopTask { id }) => {
let task = get_task(&mut tasks.clone(), id);
if task.when.is_none() {
tasks.set_status(id, Status::Inbox);
} else {
tasks.set_status(id, Status::Pending);
};
success(task_msg("stopped", &task, id));
}
Commands::Clear => {
tasks.clear();
success(String::from("cleared all tasks"));
}
Commands::Show(ShowTask { id }) => { Commands::Show(ShowTask { id }) => {
if id.is_none() { if id.is_none() {
if tasks.tasks.is_none() { if tasks.tasks.is_none() {
@ -67,7 +120,7 @@ pub fn execute(tasks: &mut Tasks, arguments: TasksArgs) -> &mut Tasks {
} else { } else {
// Create the table for printing // Create the table for printing
let mut table = Table::new(); let mut table = Table::new();
table.set_titles(row!["ID".magenta().bold(), "Status".magenta().bold(), "Title".magenta().bold()]); table.set_titles(row!["ID".magenta().bold(), "Status".magenta().bold(), "When".magenta().bold(), "Title".magenta().bold()]);
table.set_format(*format::consts::FORMAT_NO_BORDER_LINE_SEPARATOR); table.set_format(*format::consts::FORMAT_NO_BORDER_LINE_SEPARATOR);
// Iterate through each task // Iterate through each task
@ -86,38 +139,13 @@ pub fn execute(tasks: &mut Tasks, arguments: TasksArgs) -> &mut Tasks {
// Generate and print the table // Generate and print the table
let mut table = Table::new(); let mut table = Table::new();
table.set_titles(row!["ID".magenta().bold(), "Status".magenta().bold(), "Title".magenta().bold()]); table.set_titles(row!["ID".magenta().bold(), "Status".magenta().bold(), "When".magenta().bold(), "Title".magenta().bold()]);
table.set_format(*format::consts::FORMAT_NO_BORDER_LINE_SEPARATOR); table.set_format(*format::consts::FORMAT_NO_BORDER_LINE_SEPARATOR);
table.add_row(calc_row(&task, id)); table.add_row(calc_row(&task, id));
println!("{}", table) println!("{}", table)
}; };
} }
Commands::Done(CompleteTask { id }) => {
let task = get_task(&mut tasks.clone(), id);
tasks.set_status(id, Status::Complete);
success(task_msg("completed", &task, id));
}
Commands::Start(StartTask { id }) => {
let task = get_task(&mut tasks.clone(), id);
tasks.set_status(id, Status::Active);
success(task_msg("started", &task, id));
}
Commands::Stop(StopTask { id }) => {
let task = get_task(&mut tasks.clone(), id);
if task.when.is_none() {
tasks.set_status(id, Status::Inbox);
} else {
tasks.set_status(id, Status::Pending);
};
success(task_msg("stopped", &task, id));
}
Commands::Clear => {
tasks.clear();
success(String::from("cleared all tasks"));
}
_ => todo!() _ => todo!()
}; };
tasks tasks

View File

@ -14,7 +14,7 @@ fn main() {
// If the tasks file doesn't exist, create it first // If the tasks file doesn't exist, create it first
if !Path::new(&tasks_file_path).exists() { if !Path::new(&tasks_file_path).exists() {
println!("warning: file '~/.local/share/tasks' does not exist. creating.."); cli::warning("file '~/.local/share/tasks' does not exist. creating..");
let tasks = Tasks::new(&tasks_file_path); let tasks = Tasks::new(&tasks_file_path);
data::save_tasks(&tasks_file_path, &tasks).unwrap(); data::save_tasks(&tasks_file_path, &tasks).unwrap();
}; };

View File

@ -1,13 +1,8 @@
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use chrono::{DateTime, Utc}; use chrono::{NaiveDateTime, Utc};
use colored::*; use colored::*;
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct Tag {
title: String, // The required title of the tag
}
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)] #[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
pub enum Status { pub enum Status {
Inbox, Inbox,
@ -20,7 +15,7 @@ impl Status {
pub fn as_string(&self) -> ColoredString { pub fn as_string(&self) -> ColoredString {
match self { match self {
Status::Inbox => "📮 Inbox".blue(), Status::Inbox => "📮 Inbox".blue(),
Status::Pending => "🗓️ Pending".yellow(), Status::Pending => "📅 Pending".yellow(),
Status::Active => "✍️ Active".red(), Status::Active => "✍️ Active".red(),
Status::Complete => "📗 Complete".green(), Status::Complete => "📗 Complete".green(),
} }
@ -32,11 +27,11 @@ pub struct Task {
pub title: String, // The required title of the task pub title: String, // The required title of the task
pub status: Status, // Current status of the task pub status: Status, // Current status of the task
pub notes: Option<String>, // Any notes to explain the task pub notes: Option<String>, // Any notes to explain the task
pub tags: Option<Vec<Tag>>, // Tasks can be tagged for organisation pub tags: Option<Vec<String>>, // Tasks can be tagged for organisation
pub subtasks: Option<Vec<Task>>, // Tasks can be hierarchically split into subtasks pub subtasks: Option<Vec<Task>>, // Tasks can be hierarchically split into subtasks
pub when: Option<DateTime<Utc>>, // The date you want to do the task pub when: Option<NaiveDateTime>, // The date you want to do the task
pub deadline: Option<DateTime<Utc>>, // The latest date the task should be done pub deadline: Option<NaiveDateTime>, // The latest date the task should be done
pub reminder: Option<DateTime<Utc>>, // The datetime a reminder will alert you pub reminder: Option<NaiveDateTime>, // The datetime a reminder will alert you
} }
#[derive(Serialize, Deserialize, Debug, Clone)] #[derive(Serialize, Deserialize, Debug, Clone)]
@ -46,16 +41,22 @@ pub struct Tasks {
} }
impl Task { impl Task {
pub fn new(title: String) -> Self { pub fn new(title: String, notes: Option<String>, tags: Option<Vec<String>>, when: Option<NaiveDateTime>, deadline: Option<NaiveDateTime>, reminder: Option<NaiveDateTime>) -> Self {
let status = if when.is_some() {
Status::Pending
} else {
Status::Inbox
};
Self { Self {
title, title,
status: Status::Inbox, status,
notes: None, notes,
tags: None, tags,
subtasks: None, subtasks: None,
when: None, when,
deadline: None, deadline,
reminder: None, reminder,
} }
} }
} }