🧰 Improvements to code and dates

This commit is contained in:
Maddie H 2023-02-24 15:37:29 +00:00
parent 19e5011f27
commit 102123b688
No known key found for this signature in database
GPG Key ID: 64FAA9959751687D
3 changed files with 136 additions and 86 deletions

View File

@ -1,12 +1,14 @@
use chrono::NaiveDateTime; use chrono::{NaiveDateTime, Local};
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, TasksError};
use prettytable::{Table, Row, row, format}; use prettytable::{Table, Row, row, format};
use colored::*; use colored::*;
use fuzzydate; use fuzzydate;
use std::panic;
pub fn success(msg: String) {
fn success(msg: String) {
println!("{} {}", "success:".green().bold(), msg); println!("{} {}", "success:".green().bold(), msg);
} }
@ -14,26 +16,20 @@ pub fn warning(msg: &str) {
println!("{} {}", "warning:".yellow().bold(), msg); println!("{} {}", "warning:".yellow().bold(), msg);
} }
#[allow(dead_code)] pub fn info(msg: &str) {
pub fn error(msg: String) { println!("{} {}", "info:".blue().bold(), msg);
println!("{} {}", "error:".red().bold(), msg);
panic!();
} }
fn task_msg(msg: &str, task: &Task, id: usize) -> String { fn task_msg(msg: &str, task: &Task, id: usize) -> String {
format!("{} task: {}({})", msg, task.title.blue(), id.to_string().cyan()) format!("{} task: {}({})", msg, task.title.blue(), id.to_string().cyan())
} }
fn get_task(tasks: &mut Tasks, id: usize) -> Task {
match tasks.get_task(id) {
Ok(task) => task.clone(),
Err(error) => panic!("error: {}", error),
}
}
fn parse_date(date_string: Option<String>) -> Option<NaiveDateTime> { fn parse_date(date_string: Option<String>) -> Option<NaiveDateTime> {
if date_string.is_some() { if date_string.is_some() {
Some(fuzzydate::parse(date_string.unwrap()).unwrap()) match fuzzydate::parse(date_string.unwrap()) {
Ok(date) => Some(date),
Err(err) => panic!("{:?}", err),
}
} else { } else {
None None
} }
@ -44,12 +40,23 @@ fn calc_row(task: &Task, id: usize) -> Row {
// Generate greyed out rows for complete tasks // Generate greyed out rows for complete tasks
Row::from([id.to_string().bright_black().italic(), Row::from([id.to_string().bright_black().italic(),
task.status.as_string().bright_black().italic(), task.status.as_string().bright_black().italic(),
"N/A".bright_black(),
task.title.clone().bright_black().italic()]) task.title.clone().bright_black().italic()])
} else { } else {
let when = if task.when.is_some() { let when = if task.when.is_some() {
format!("{}", task.when.unwrap().format("%Y-%m-%d")).bright_black() let date = format!("{}", task.when.unwrap().format("%Y-%m-%d"));
let now = Local::now().date_naive();
if now == task.when.unwrap().date() {
date.bright_red()
} else if now.succ_opt().unwrap() == task.when.unwrap().date() {
date.yellow()
} else {
date.white()
}
} else { } else {
String::from("N/A").bright_black() "N/A".bright_black()
}; };
// Generate normal colored rows for uncompleted tasks // Generate normal colored rows for uncompleted tasks
@ -57,7 +64,7 @@ fn calc_row(task: &Task, id: usize) -> Row {
} }
} }
pub fn execute(tasks: &mut Tasks, arguments: TasksArgs) -> &mut Tasks { pub fn execute(tasks: &mut Tasks, arguments: TasksArgs) -> Result<&mut Tasks, TasksError> {
match arguments.command { match arguments.command {
Commands::Add(CreateTask { title, notes, tags, when, deadline, reminder, ..}) => { Commands::Add(CreateTask { title, notes, tags, when, deadline, reminder, ..}) => {
let when = parse_date(when); let when = parse_date(when);
@ -70,53 +77,58 @@ pub fn execute(tasks: &mut Tasks, arguments: TasksArgs) -> &mut Tasks {
}; };
let task = Task::new(title, notes, tags, when, deadline, reminder); let task = Task::new(title, notes, tags, when, deadline, reminder);
tasks.add(task.clone()); tasks.push(task.clone());
let id = tasks.len() - 1; let id = tasks.len() - 1;
success(task_msg("created", &task, id)); success(task_msg("created", &task, id));
Ok(tasks)
} }
Commands::Del(DeleteTask { id }) => { Commands::Del(DeleteTask { id }) => {
let task = get_task(tasks, id); let mut binding = tasks.clone();
let task = binding.get_task(id)?;
tasks.remove(id)?;
tasks.del(id);
success(task_msg("deleted", &task, id)); success(task_msg("deleted", &task, id));
Ok(tasks)
} }
Commands::Done(CompleteTask { id }) => { Commands::Done(CompleteTask { id }) => {
let task = get_task(&mut tasks.clone(), id); let task = tasks.get_task(id)?;
task.complete();
tasks.set_status(id, Status::Complete);
success(task_msg("completed", &task, id)); success(task_msg("completed", &task, id));
Ok(tasks)
} }
Commands::Start(StartTask { id }) => { Commands::Start(StartTask { id }) => {
let task = get_task(&mut tasks.clone(), id); let task = tasks.get_task(id)?;
task.start();
tasks.set_status(id, Status::Active);
success(task_msg("started", &task, id)); success(task_msg("started", &task, id));
Ok(tasks)
} }
Commands::Stop(StopTask { id }) => { Commands::Stop(StopTask { id }) => {
let task = get_task(&mut tasks.clone(), id); let task = tasks.get_task(id)?;
task.stop();
if task.when.is_none() {
tasks.set_status(id, Status::Inbox);
} else {
tasks.set_status(id, Status::Pending);
};
success(task_msg("stopped", &task, id)); success(task_msg("stopped", &task, id));
Ok(tasks)
} }
Commands::Clear => { Commands::Clear => {
tasks.clear(); tasks.clear()?;
success(String::from("cleared all tasks")); success(String::from("cleared all tasks"));
Ok(tasks)
} }
Commands::Show(ShowTask { id }) => { Commands::Show(ShowTask { id }) => {
if id.is_none() { if id.is_none() {
if tasks.tasks.is_none() { if tasks.is_empty() {
warning("no tasks available to show") info("no tasks found")
} else { } else {
// Create the table for printing // Create the table for printing
let mut table = Table::new(); let mut table = Table::new();
@ -134,8 +146,9 @@ pub fn execute(tasks: &mut Tasks, arguments: TasksArgs) -> &mut Tasks {
println!("{}", table); println!("{}", table);
}; };
} else { } else {
// Get the task
let id = id.unwrap(); let id = id.unwrap();
let task = get_task(&mut tasks.clone(), id); let task = tasks.get_task(id)?;
// Generate and print the table // Generate and print the table
let mut table = Table::new(); let mut table = Table::new();
@ -144,9 +157,10 @@ pub fn execute(tasks: &mut Tasks, arguments: TasksArgs) -> &mut Tasks {
table.add_row(calc_row(&task, id)); table.add_row(calc_row(&task, id));
println!("{}", table) println!("{}", table)
}; };
Ok(tasks)
} }
_ => todo!() _ => todo!()
}; }
tasks }
}

View File

@ -7,6 +7,7 @@ use crate::tasks::Tasks;
use crate::args::TasksArgs; use crate::args::TasksArgs;
use std::path::Path; use std::path::Path;
use clap::Parser; use clap::Parser;
use colored::*;
fn main() { fn main() {
// Generate the file path for tasks // Generate the file path for tasks
@ -22,12 +23,15 @@ fn main() {
// Load tasks and check for any errors when loading the tasks // Load tasks and check for any errors when loading the tasks
let mut tasks = match data::load_tasks(&tasks_file_path) { let mut tasks = match data::load_tasks(&tasks_file_path) {
Ok(tasks) => tasks, Ok(tasks) => tasks,
Err(_error) => panic!("error: couldn't open file {} - likely corrupted", &tasks_file_path), Err(error) => panic!("{} {:?}", "error:".red().bold(), error),
}; };
// Parse command line arguments // Parse command line arguments
let arguments = TasksArgs::parse(); let arguments = TasksArgs::parse();
let tasks = cli::execute(&mut tasks, arguments); let tasks = match cli::execute(&mut tasks, arguments) {
Ok(tasks) => tasks,
Err(error) => panic!("{} {:?}", "error:".red().bold(), error),
};
// Save any changes // Save any changes
data::save_tasks(tasks_file_path, &tasks).unwrap() data::save_tasks(tasks_file_path, &tasks).unwrap()

View File

@ -1,8 +1,11 @@
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use chrono::{NaiveDateTime, Utc}; use chrono::NaiveDateTime;
use colored::*; use colored::*;
#[derive(Debug)]
pub struct TasksError(String);
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)] #[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
pub enum Status { pub enum Status {
Inbox, Inbox,
@ -11,17 +14,6 @@ pub enum Status {
Complete, Complete,
} }
impl Status {
pub fn as_string(&self) -> ColoredString {
match self {
Status::Inbox => "📮 Inbox".blue(),
Status::Pending => "📅 Pending".yellow(),
Status::Active => "✍️ Active".red(),
Status::Complete => "📗 Complete".green(),
}
}
}
#[derive(Serialize, Deserialize, Debug, Clone)] #[derive(Serialize, Deserialize, Debug, Clone)]
pub struct Task { pub struct Task {
pub title: String, // The required title of the task pub title: String, // The required title of the task
@ -41,24 +33,29 @@ pub struct Tasks {
} }
impl Task { impl Task {
pub fn new(title: String, notes: Option<String>, tags: Option<Vec<String>>, when: Option<NaiveDateTime>, deadline: Option<NaiveDateTime>, reminder: Option<NaiveDateTime>) -> Self { pub fn new(title: String, notes: Option<String>, tags: Option<Vec<String>>,
let status = if when.is_some() { when: Option<NaiveDateTime>, deadline: Option<NaiveDateTime>,
Status::Pending reminder: Option<NaiveDateTime>) -> Self {
} else { let status = if when.is_some() { Status::Pending } else { Status::Inbox };
Status::Inbox
};
Self { Self { title, status, notes, tags, subtasks: None, when, deadline, reminder, }
title, }
status,
notes, pub fn start(&mut self) {
tags, self.status = Status::Active;
subtasks: None, }
when,
deadline, pub fn stop(&mut self) {
reminder, if self.when.is_none() {
self.status = Status::Inbox;
} else {
self.status = Status::Pending;
} }
} }
pub fn complete(&mut self) {
self.status = Status::Complete;
}
} }
impl Tasks { impl Tasks {
@ -69,43 +66,78 @@ impl Tasks {
} }
} }
pub fn get_task(&mut self, id: usize) -> Result<&mut Task, &str> { fn task_not_found(&self, id: usize) -> TasksError {
if self.tasks.is_none() { TasksError(format!("couldn't find task with id {}", id))
Err("there are no tasks") }
fn task_exists(&self, id: usize) -> bool{
if id >= self.len() { false } else { true }
}
pub fn is_empty(&self) -> bool {
if self.len() == 0 {
true
} else { } else {
if id >= self.tasks.as_ref().unwrap().len() { false
Err("couldn't find task") }
} else { }
pub fn get_task(&mut self, id: usize) -> Result<&mut Task, TasksError> {
if self.is_empty() {
Err(TasksError(format!("no tasks available")))
} else {
if self.task_exists(id) {
let task = &mut self.tasks.as_mut().unwrap()[id]; let task = &mut self.tasks.as_mut().unwrap()[id];
Ok(task) Ok(task)
} else {
Err(TasksError(format!("couldn't find task with id {}", id)))
} }
} }
} }
#[allow(dead_code)] pub fn push(&mut self, task: Task) {
pub fn set_status(&mut self, id: usize, status: Status) { if self.is_empty() {
let mut task: &mut Task = self.get_task(id).unwrap();
task.status = status;
}
pub fn add(&mut self, task: Task) {
if self.tasks.is_none() {
self.tasks = Some(vec![task]); self.tasks = Some(vec![task]);
} else { } else {
self.tasks.as_mut().unwrap().push(task); self.tasks.as_mut().unwrap().push(task);
}; };
} }
pub fn del(&mut self, id: usize) { pub fn remove(&mut self, id: usize) -> Result<(), TasksError> {
self.tasks.as_mut().unwrap().remove(id); if self.task_exists(id) {
self.tasks.as_mut().unwrap().remove(id);
Ok(())
} else {
Err(self.task_not_found(id))
}
} }
pub fn len(&self) -> usize { pub fn len(&self) -> usize {
self.tasks.as_ref().unwrap().len() if self.tasks.is_none() {
0
} else {
self.tasks.as_ref().unwrap().len()
}
} }
pub fn clear(&mut self) { pub fn clear(&mut self) -> Result<(), TasksError> {
self.tasks = None; if self.is_empty() {
Err(TasksError(String::from("no tasks available")))
} else {
self.tasks = None;
Ok(())
}
}
}
impl Status {
pub fn as_string(&self) -> ColoredString {
match self {
Status::Inbox => "📮 Inbox".blue(),
Status::Pending => "📅 Pending".yellow(),
Status::Active => "✍️ Active".red(),
Status::Complete => "📗 Complete".green(),
}
} }
} }