🎨 General code improvements and fixes

This commit is contained in:
Maddie H 2023-02-26 20:21:39 +00:00
parent b53ce1de5f
commit 81d6fd91e5
No known key found for this signature in database
GPG Key ID: 64FAA9959751687D
11 changed files with 321 additions and 201 deletions

View File

@ -23,6 +23,8 @@ pub enum Commands {
Start(StartTask), Start(StartTask),
/// Marks a task as pending /// Marks a task as pending
Stop(StopTask), Stop(StopTask),
/// Returns a task to the inbox
Inbox(InboxTask),
/// Edit a task with $EDITOR /// Edit a task with $EDITOR
Edit(EditTask), Edit(EditTask),
/// Modify a task at the command line /// Modify a task at the command line
@ -92,6 +94,11 @@ pub struct StopTask {
pub id: usize, pub id: usize,
} }
#[derive(Args, PartialEq, Eq, Debug)] #[derive(Args, PartialEq, Eq, Debug)]
pub struct InboxTask {
/// ID of the task
pub id: usize,
}
#[derive(Args, PartialEq, Eq, Debug)]
pub struct EditTask { pub struct EditTask {
/// ID of the task /// ID of the task
pub id: usize, pub id: usize,

View File

@ -6,8 +6,10 @@ mod tables;
use crate::args::{Commands, GitExecute, TasksArgs}; use crate::args::{Commands, GitExecute, TasksArgs};
use crate::args::{ use crate::args::{
CompleteTask, CreateTask, DeleteTask, ModifyTask, ShowTask, StartTask, StopTask, SyncTasks, CompleteTask, CreateTask, DeleteTask, InboxTask, ModifyTask, ShowTask, StartTask, StopTask,
SyncTasks,
}; };
use crate::repo;
use crate::tasks::{Tasks, TasksError}; use crate::tasks::{Tasks, TasksError};
pub fn execute(tasks: &mut Tasks, arguments: TasksArgs) -> Result<(), TasksError> { pub fn execute(tasks: &mut Tasks, arguments: TasksArgs) -> Result<(), TasksError> {
@ -51,6 +53,10 @@ pub fn execute(tasks: &mut Tasks, arguments: TasksArgs) -> Result<(), TasksError
cmds::stop(tasks, id)?; cmds::stop(tasks, id)?;
} }
Commands::Inbox(InboxTask { id }) => {
cmds::inbox(tasks, id)?;
}
Commands::Clear => { Commands::Clear => {
cmds::clear(tasks)?; cmds::clear(tasks)?;
} }
@ -59,12 +65,12 @@ pub fn execute(tasks: &mut Tasks, arguments: TasksArgs) -> Result<(), TasksError
cmds::show(tasks, id)?; cmds::show(tasks, id)?;
} }
Commands::Git(GitExecute { command }) => match git::execute(&tasks.path, command) { Commands::Git(GitExecute { command }) => match repo::execute(&tasks.path, command) {
Ok(..) => (), Ok(..) => (),
Err(..) => panic!("failed to execute git cmd"), Err(..) => panic!("failed to execute git cmd"),
}, },
Commands::Sync(SyncTasks { remote }) => match git::sync(&tasks.path, remote) { Commands::Sync(SyncTasks { remote }) => match repo::sync(&tasks.path, remote) {
Ok(..) => (), Ok(..) => (),
Err(..) => panic!("failed"), Err(..) => panic!("failed"),
}, },

View File

@ -4,6 +4,7 @@ use crate::cli::tables;
use crate::tasks::{Task, Tasks, TasksError}; use crate::tasks::{Task, Tasks, TasksError};
fn parse_tags(tags: Option<String>) -> Option<Vec<String>> { fn parse_tags(tags: Option<String>) -> Option<Vec<String>> {
// Split tags into a vector by commas
tags.map(|tags| tags.split(',').map(str::to_string).collect()) tags.map(|tags| tags.split(',').map(str::to_string).collect())
} }
@ -11,7 +12,7 @@ pub fn show(tasks: &mut Tasks, id: Option<usize>) -> Result<(), TasksError> {
// If no id is given, print out all tasks // If no id is given, print out all tasks
if let Some(id) = id { if let Some(id) = id {
// Get the task the user wants to see // Get the task the user wants to see
let task = tasks.get_task(id)?; let task = tasks.task(id)?;
// Generate the table for the singular task // Generate the table for the singular task
let table = tables::task_table(task, id); let table = tables::task_table(task, id);
@ -75,7 +76,7 @@ pub fn modify(
let tags = parse_tags(tags); let tags = parse_tags(tags);
// Get the task the user wants // Get the task the user wants
let task = tasks.get_task(id)?; let task = tasks.task(id)?;
// If the the user changes the title, show that here // If the the user changes the title, show that here
if title.is_some() { if title.is_some() {
@ -93,7 +94,7 @@ pub fn modify(
pub fn delete(tasks: &mut Tasks, id: usize) -> Result<(), TasksError> { pub fn delete(tasks: &mut Tasks, id: usize) -> Result<(), TasksError> {
// Get the task the user wants to delete for output later // Get the task the user wants to delete for output later
let mut binding = tasks.clone(); let mut binding = tasks.clone();
let task = binding.get_task(id)?; let task = binding.task(id)?;
// Delete the task // Delete the task
tasks.remove(id)?; tasks.remove(id)?;
@ -114,7 +115,7 @@ pub fn clear(tasks: &mut Tasks) -> Result<(), TasksError> {
pub fn stop(tasks: &mut Tasks, id: usize) -> Result<(), TasksError> { pub fn stop(tasks: &mut Tasks, id: usize) -> Result<(), TasksError> {
// Get the task the user wants to stop // Get the task the user wants to stop
let task = tasks.get_task(id)?; let task = tasks.task(id)?;
// Stop the task // Stop the task
task.stop(); task.stop();
@ -125,7 +126,7 @@ pub fn stop(tasks: &mut Tasks, id: usize) -> Result<(), TasksError> {
pub fn start(tasks: &mut Tasks, id: usize) -> Result<(), TasksError> { pub fn start(tasks: &mut Tasks, id: usize) -> Result<(), TasksError> {
// Get the task the user wants to start // Get the task the user wants to start
let task = tasks.get_task(id)?; let task = tasks.task(id)?;
// Start the task // Start the task
task.start(); task.start();
@ -136,7 +137,7 @@ pub fn start(tasks: &mut Tasks, id: usize) -> Result<(), TasksError> {
pub fn done(tasks: &mut Tasks, id: usize) -> Result<(), TasksError> { pub fn done(tasks: &mut Tasks, id: usize) -> Result<(), TasksError> {
// Get the task the user wants to complete // Get the task the user wants to complete
let task = tasks.get_task(id)?; let task = tasks.task(id)?;
// Complete the task // Complete the task
task.complete(); task.complete();
@ -144,3 +145,14 @@ pub fn done(tasks: &mut Tasks, id: usize) -> Result<(), TasksError> {
output::success(output::task_msg("completed", task, id)); output::success(output::task_msg("completed", task, id));
Ok(()) Ok(())
} }
pub fn inbox(tasks: &mut Tasks, id: usize) -> Result<(), TasksError> {
// Get the task the user wants to return to the inbox
let task = tasks.task(id)?;
// Inbox the task
task.inbox();
// Success
output::success(output::task_msg("inboxed", task, id));
Ok(())
}

View File

@ -1,5 +1,5 @@
use chrono::{Local, NaiveDateTime}; use chrono::NaiveDateTime;
use colored::{ColoredString, Colorize}; use colored::Colorize;
pub fn parse_fuzzy_date(date_string: Option<String>) -> Option<NaiveDateTime> { pub fn parse_fuzzy_date(date_string: Option<String>) -> Option<NaiveDateTime> {
if let Some(date_string) = date_string { if let Some(date_string) = date_string {
@ -11,24 +11,3 @@ pub fn parse_fuzzy_date(date_string: Option<String>) -> Option<NaiveDateTime> {
None None
} }
} }
pub fn date_as_string(date: &Option<NaiveDateTime>) -> ColoredString {
if date.is_some() {
let date = date.unwrap().date();
let date_string = format!("{}", date.format("%Y-%m-%d"));
let now = Local::now().date_naive();
if date <= now {
// If the date is today or past today
date_string.bright_red()
} else if now.succ_opt().unwrap() == date {
// If the date is tomorrow
date_string.yellow()
} else {
// Otherwise the date is too far in the past
date_string.white()
}
} else {
"N/A".bright_black()
}
}

View File

@ -1,30 +1 @@
use std::error::Error;
use std::process::Command;
use crate::cli::output;
pub fn execute(path: &str, command: String) -> Result<(), Box<dyn Error>> {
let output = Command::new("git")
.args(["-C", path])
.args(command.split(' '))
.output()?;
if !output.stdout.is_empty() {
output::git(String::from_utf8(output.stdout).unwrap());
};
if !output.stderr.is_empty() {
output::error(String::from_utf8(output.stderr).unwrap());
};
Ok(())
}
pub fn sync(repo_path: &str, remote: String) -> Result<(), Box<dyn Error>> {
execute(
repo_path,
format!("pull --ff --no-rebase --no-edit --commit {remote}"),
)?;
execute(repo_path, format!("push {remote}"))?;
Ok(())
}

View File

@ -26,7 +26,7 @@ pub fn task_msg(msg: &str, task: &Task, id: usize) -> String {
format!( format!(
"{} task: {}({})", "{} task: {}({})",
msg, msg,
task.title.blue(), task.title_string().blue(),
id.to_string().cyan() id.to_string().cyan()
) )
} }

View File

@ -1,29 +1,26 @@
use colored::Colorize; use colored::Colorize;
use prettytable::{format, row, Row, Table}; use prettytable::{format, row, Row, Table};
use crate::cli::dates; use crate::tasks::{Task, Tasks};
use crate::tasks::{Status, Task, Tasks};
pub fn calc_row(task: &Task, id: usize) -> Row { pub fn calc_row(task: &Task, id: usize) -> Row {
if task.status == Status::Complete { if task.is_complete() {
// Generate greyed out rows for complete tasks // Generate greyed out rows for complete tasks
Row::from([ Row::from([
id.to_string().bright_black().italic(), id.to_string().bright_black().italic(),
task.status.as_string().bright_black().italic(), task.status_string().bright_black().italic(),
task.title.clone().bright_black().italic(), task.title_string().bright_black().italic(),
dates::date_as_string(&task.when).bright_black().italic(), task.when_string().bright_black().italic(),
dates::date_as_string(&task.deadline) task.deadline_string().bright_black().italic(),
.bright_black()
.italic(),
]) ])
} else { } else {
// Generate normal colored rows for uncompleted tasks // Generate normal colored rows for uncompleted tasks
Row::from([ Row::from([
id.to_string().cyan(), id.to_string().cyan(),
task.status.as_string(), task.status_string(),
task.title.clone().white(), task.title_string(),
dates::date_as_string(&task.when), task.when_string(),
dates::date_as_string(&task.deadline), task.deadline_string(),
]) ])
} }
} }
@ -52,7 +49,16 @@ pub fn task_table(task: &Task, id: usize) -> Table {
let mut table = Table::new(); let mut table = Table::new();
table.set_titles(row!["Item".magenta().bold(), "Value".magenta().bold()]); table.set_titles(row!["Item".magenta().bold(), "Value".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));
// Add rows
table.add_row(row!["ID".white().bold(), id.to_string().cyan()]);
table.add_row(row!["Status".white().bold(), task.status_string()]);
table.add_row(row!["Title".white().bold(), task.title_string()]);
table.add_row(row!["When".white().bold(), task.when_string(),]);
table.add_row(row!["Deadline".white().bold(), task.deadline_string(),]);
table.add_row(row!["Reminder".white().bold(), task.reminder_string(),]);
table.add_row(row!["Tags".white().bold(), &task.tags_string()]);
table.add_row(row!["Notes".white().bold(), &task.notes_string()]);
table table
} }

View File

@ -1,61 +0,0 @@
use dirs::home_dir;
use std::error::Error;
use std::fs;
use std::path::Path;
use std::string::ToString;
use crate::cli::git;
use crate::cli::output;
use crate::tasks::Tasks;
pub fn save_tasks<P: AsRef<Path>>(path: P, tasks: &Tasks) -> Result<(), Box<dyn Error>> {
// Convert the tasks to TOML format
let data = toml::to_string_pretty(&tasks)?;
// Write the TOML to the file
fs::write(path, data)?;
Ok(())
}
pub fn load_tasks<P: AsRef<Path> + ToString>(
path: P,
tasks_file: &str,
) -> Result<Tasks, Box<dyn Error>> {
let tasks_file_path = &format!("{}/{}", path.to_string(), tasks_file);
// Read TOML from the file
let data = fs::read_to_string(tasks_file_path)?;
// Load the tasks from TOML form
let tasks: Tasks = toml::from_str(&data)?;
Ok(tasks)
}
pub fn ensure_repo(path: &str, tasks_file: &str) -> Result<(), Box<dyn Error>> {
// Generate the path of the tasks file
let tasks_file_path = &format!("{}/{}", path, tasks_file);
// Check if the path exists
if !Path::new(path).exists() {
output::warning(format!(
"tasks repository {path} does not exist. creating..."
));
fs::create_dir_all(path).unwrap();
let tasks = Tasks::new(path, tasks_file);
save_tasks(tasks_file_path, &tasks).unwrap();
git::execute(path, String::from("init"))?;
git::execute(path, String::from("add ."))?;
output::success(format!("created tasks repo {path}"));
}
Ok(())
}
pub fn tasks_repo_string() -> String {
// Generate the path for the location of tasks
let home_dir = home_dir().unwrap();
let home_dir = home_dir.to_str().unwrap();
format!("{home_dir}/.local/share/inertia")
}

View File

@ -1,6 +1,6 @@
mod args; mod args;
mod cli; mod cli;
mod data; mod repo;
mod tasks; mod tasks;
use clap::Parser; use clap::Parser;
@ -10,29 +10,31 @@ use crate::args::TasksArgs;
fn main() { fn main() {
// Generate the file paths for tasks // Generate the file paths for tasks
let repo_path = &data::tasks_repo_string(); let repo_path = repo::tasks_repo_string();
let tasks_file = "tasks"; let tasks_file_path = repo::tasks_file_path();
// If the tasks file doesn't exist, create it first // If the tasks file doesn't exist, create it first
match data::ensure_repo(repo_path, tasks_file) { match repo::ensure_repo(&repo_path) {
Ok(..) => (), Ok(..) => (),
Err(error) => panic!("{} {:?}", "error:".red().bold(), error), Err(error) => panic!("{} {:?}", "error:".red().bold(), error),
}; };
// 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(repo_path, tasks_file) { let mut tasks = match repo::load_tasks(&tasks_file_path) {
Ok(tasks) => tasks, Ok(tasks) => tasks,
Err(error) => panic!("{} {:?}", "error:".red().bold(), error), Err(error) => panic!("{} {:?}", "error:".red().bold(), error),
}; };
// Parse command line arguments // Parse command line arguments
let arguments = TasksArgs::parse(); let arguments = TasksArgs::parse();
// Execute the inputted command line arguments
match cli::execute(&mut tasks, arguments) { match cli::execute(&mut tasks, arguments) {
Ok(..) => (), Ok(..) => (),
Err(error) => panic!("{} {:?}", "error:".red().bold(), error), Err(error) => panic!("{} {:?}", "error:".red().bold(), error),
}; };
// Save any changes // Save any changes
cli::git::execute(repo_path, String::from("add --all")).unwrap(); repo::save_tasks(&tasks_file_path, &tasks).unwrap();
data::save_tasks(&repo_path, &tasks).unwrap(); repo::execute(&repo_path, String::from("add --all")).unwrap();
} }

97
src/repo.rs Normal file
View File

@ -0,0 +1,97 @@
use dirs::home_dir;
use std::error::Error;
use std::fs;
use std::path::Path;
use std::process::Command;
use std::string::ToString;
use crate::cli::output;
use crate::tasks::Tasks;
const TASKS_FILE: &str = "tasks.toml";
pub fn execute(path: &str, command: String) -> Result<(), Box<dyn Error>> {
let output = Command::new("git")
.args(["-C", path])
.args(command.split(' '))
.output()?;
if !output.stdout.is_empty() {
output::git(String::from_utf8(output.stdout).unwrap());
};
if !output.stderr.is_empty() {
output::error(String::from_utf8(output.stderr).unwrap());
};
Ok(())
}
pub fn save_tasks<P: AsRef<Path>>(path: P, tasks: &Tasks) -> Result<(), Box<dyn Error>> {
// Convert the tasks to TOML format
let data = toml::to_string_pretty(&tasks)?;
// Write the TOML to the file
fs::write(path, data)?;
Ok(())
}
pub fn load_tasks<P: AsRef<Path> + ToString>(path: P) -> Result<Tasks, Box<dyn Error>> {
// Read TOML from the file
let data = fs::read_to_string(path)?;
// Load the tasks from TOML form
let tasks: Tasks = toml::from_str(&data)?;
Ok(tasks)
}
pub fn ensure_repo(path: &str) -> Result<(), Box<dyn Error>> {
// Generate the path of the tasks file
let tasks_file_path = tasks_file_path();
// Check if the path exists
if !Path::new(path).exists() {
output::warning(format!(
"tasks repository {path} does not exist. creating..."
));
// Create the directory
fs::create_dir_all(path).unwrap();
// Generate a new empty tasks structure
let tasks = Tasks::new(path, TASKS_FILE);
// Save the tasks
save_tasks(tasks_file_path, &tasks).unwrap();
// Create the git repository
execute(path, String::from("init"))?;
execute(path, String::from("add --all"))?;
// Success
output::success(format!("created tasks repo {path}"));
}
Ok(())
}
pub fn tasks_repo_string() -> String {
// Generate the path for the location of tasks
let home_dir = home_dir().unwrap();
let home_dir = home_dir.to_str().unwrap();
format!("{home_dir}/.local/share/inertia")
}
pub fn tasks_file_path() -> String {
format!("{}/{}", tasks_repo_string(), TASKS_FILE)
}
pub fn sync(repo_path: &str, remote: String) -> Result<(), Box<dyn Error>> {
execute(
repo_path,
format!("pull --ff --no-rebase --no-edit --commit {remote}"),
)?;
execute(repo_path, format!("push {remote}"))?;
Ok(())
}

View File

@ -1,16 +1,37 @@
use chrono::NaiveDateTime; use chrono::{Local, NaiveDateTime};
use colored::*; use colored::{ColoredString, Colorize};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
#[derive(Debug)] #[derive(Debug)]
pub struct TasksError(String); pub struct TasksError(String);
impl TasksError {
pub fn no_task(id: usize) -> TasksError {
TasksError(format!("couldn't find task with id {}", id))
}
pub fn no_tasks() -> TasksError {
TasksError(String::from("no tasks available"))
}
}
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)] #[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)]
pub enum Status { pub enum Status {
Inbox, Inbox, // When you create a new task without a when date
Pending, Pending, // When you give a task a when date
Active, Active, // When you have started a task
Complete, Complete, // When a task is completed
}
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)]
@ -24,21 +45,6 @@ pub struct Task {
pub reminder: Option<NaiveDateTime>, // The datetime a reminder will alert you pub reminder: Option<NaiveDateTime>, // The datetime a reminder will alert you
} }
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct Tasks {
pub path: String, // Path to the tasks repository
pub file: String, // Path to the tasks file in the repository
pub tasks: Option<Vec<Task>>, // All the tasks in one vector
}
fn task_not_found(id: usize) -> TasksError {
TasksError(format!("couldn't find task with id {}", id))
}
fn no_tasks_available() -> TasksError {
TasksError(String::from("no tasks available"))
}
impl Task { impl Task {
pub fn new( pub fn new(
title: String, title: String,
@ -88,6 +94,9 @@ impl Task {
if let Some(when) = when { if let Some(when) = when {
self.when = Some(when); self.when = Some(when);
if self.is_inbox() {
self.pend()
};
}; };
if let Some(deadline) = deadline { if let Some(deadline) = deadline {
@ -98,22 +107,118 @@ impl Task {
self.reminder = Some(reminder); self.reminder = Some(reminder);
}; };
} }
}
pub fn start(&mut self) { impl Task {
self.status = Status::Active; pub fn inbox(&mut self) {
} self.status = Status::Inbox;
self.when = None;
pub fn stop(&mut self) {
if self.when.is_some() {
self.status = Status::Inbox;
} else {
self.status = Status::Pending;
}
} }
pub fn complete(&mut self) { pub fn complete(&mut self) {
self.status = Status::Complete; self.status = Status::Complete;
} }
pub fn start(&mut self) {
self.status = Status::Active;
}
pub fn pend(&mut self) {
self.status = Status::Pending;
}
pub fn stop(&mut self) {
if self.when.is_some() {
self.status = Status::Pending;
} else {
self.status = Status::Inbox;
}
}
}
impl Task {
pub fn is_complete(&self) -> bool {
self.status == Status::Complete
}
pub fn is_active(&self) -> bool {
self.status == Status::Active
}
pub fn is_pending(&self) -> bool {
self.status == Status::Pending
}
pub fn is_inbox(&self) -> bool {
self.status == Status::Inbox
}
}
impl Task {
fn date_string(&self, date: &Option<NaiveDateTime>) -> ColoredString {
if let Some(date) = date {
let date = date.date();
let date_string = format!("{}", date.format("%Y-%m-%d"));
let now = Local::now().date_naive();
if date <= now {
// If the date is today or past today
date_string.bright_red()
} else if now.succ_opt().unwrap() == date {
// If the date is tomorrow
date_string.yellow()
} else {
// Otherwise the date is too far in the past
date_string.white()
}
} else {
// No date available
"N/A".bright_black()
}
}
pub fn when_string(&self) -> ColoredString {
self.date_string(&self.when)
}
pub fn deadline_string(&self) -> ColoredString {
self.date_string(&self.deadline)
}
pub fn reminder_string(&self) -> ColoredString {
self.date_string(&self.reminder)
}
pub fn title_string(&self) -> ColoredString {
self.title.white()
}
pub fn status_string(&self) -> ColoredString {
self.status.as_string()
}
pub fn tags_string(&self) -> ColoredString {
if let Some(tags) = &self.tags {
tags.join(", ").white()
} else {
"N/A".bright_black()
}
}
pub fn notes_string(&self) -> ColoredString {
if let Some(notes) = &self.notes {
notes.white()
} else {
"N/A".bright_black()
}
}
}
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct Tasks {
pub path: String, // Path to the tasks repository
pub file: String, // Path to the tasks file in the repository
pub tasks: Option<Vec<Task>>, // All the tasks in one vector
} }
impl Tasks { impl Tasks {
@ -124,25 +229,32 @@ impl Tasks {
tasks: None, tasks: None,
} }
} }
}
pub fn task_exists(&self, id: usize) -> bool { impl Tasks {
id < self.len() /// Checks if tasks are empty
}
pub fn is_empty(&self) -> bool { pub fn is_empty(&self) -> bool {
self.len() == 0 self.len() == 0
} }
pub fn get_task(&mut self, id: usize) -> Result<&mut Task, TasksError> { /// Checks if a task exists from an id
if self.is_empty() { pub fn exists(&self, id: usize) -> bool {
Err(no_tasks_available()) id < self.len()
} else if self.task_exists(id) {
Ok(&mut self.tasks.as_mut().unwrap()[id])
} else {
Err(task_not_found(id))
}
} }
/// Returns a task from an id
pub fn task(&mut self, id: usize) -> Result<&mut Task, TasksError> {
if self.is_empty() {
Err(TasksError::no_tasks())
} else if self.exists(id) {
Ok(&mut self.tasks.as_mut().unwrap()[id])
} else {
Err(TasksError::no_task(id))
}
}
}
impl Tasks {
pub fn push(&mut self, task: Task) { pub fn push(&mut self, task: Task) {
if self.is_empty() { if self.is_empty() {
self.tasks = Some(vec![task]); self.tasks = Some(vec![task]);
@ -152,11 +264,11 @@ impl Tasks {
} }
pub fn remove(&mut self, id: usize) -> Result<(), TasksError> { pub fn remove(&mut self, id: usize) -> Result<(), TasksError> {
if self.task_exists(id) { if self.exists(id) {
self.tasks.as_mut().unwrap().remove(id); self.tasks.as_mut().unwrap().remove(id);
Ok(()) Ok(())
} else { } else {
Err(task_not_found(id)) Err(TasksError::no_task(id))
} }
} }
@ -170,21 +282,10 @@ impl Tasks {
pub fn clear(&mut self) -> Result<(), TasksError> { pub fn clear(&mut self) -> Result<(), TasksError> {
if self.is_empty() { if self.is_empty() {
Err(no_tasks_available()) Err(TasksError::no_tasks())
} else { } else {
self.tasks = None; self.tasks = None;
Ok(()) 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(),
}
}
}