🎨 General code improvements and fixes
This commit is contained in:
parent
b53ce1de5f
commit
81d6fd91e5
@ -23,6 +23,8 @@ pub enum Commands {
|
||||
Start(StartTask),
|
||||
/// Marks a task as pending
|
||||
Stop(StopTask),
|
||||
/// Returns a task to the inbox
|
||||
Inbox(InboxTask),
|
||||
/// Edit a task with $EDITOR
|
||||
Edit(EditTask),
|
||||
/// Modify a task at the command line
|
||||
@ -92,6 +94,11 @@ pub struct StopTask {
|
||||
pub id: usize,
|
||||
}
|
||||
#[derive(Args, PartialEq, Eq, Debug)]
|
||||
pub struct InboxTask {
|
||||
/// ID of the task
|
||||
pub id: usize,
|
||||
}
|
||||
#[derive(Args, PartialEq, Eq, Debug)]
|
||||
pub struct EditTask {
|
||||
/// ID of the task
|
||||
pub id: usize,
|
||||
|
12
src/cli.rs
12
src/cli.rs
@ -6,8 +6,10 @@ mod tables;
|
||||
|
||||
use crate::args::{Commands, GitExecute, TasksArgs};
|
||||
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};
|
||||
|
||||
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)?;
|
||||
}
|
||||
|
||||
Commands::Inbox(InboxTask { id }) => {
|
||||
cmds::inbox(tasks, id)?;
|
||||
}
|
||||
|
||||
Commands::Clear => {
|
||||
cmds::clear(tasks)?;
|
||||
}
|
||||
@ -59,12 +65,12 @@ pub fn execute(tasks: &mut Tasks, arguments: TasksArgs) -> Result<(), TasksError
|
||||
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(..) => (),
|
||||
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(..) => (),
|
||||
Err(..) => panic!("failed"),
|
||||
},
|
||||
|
@ -4,6 +4,7 @@ use crate::cli::tables;
|
||||
use crate::tasks::{Task, Tasks, TasksError};
|
||||
|
||||
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())
|
||||
}
|
||||
|
||||
@ -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 let Some(id) = id {
|
||||
// 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
|
||||
let table = tables::task_table(task, id);
|
||||
@ -75,7 +76,7 @@ pub fn modify(
|
||||
let tags = parse_tags(tags);
|
||||
|
||||
// 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 title.is_some() {
|
||||
@ -93,7 +94,7 @@ pub fn modify(
|
||||
pub fn delete(tasks: &mut Tasks, id: usize) -> Result<(), TasksError> {
|
||||
// Get the task the user wants to delete for output later
|
||||
let mut binding = tasks.clone();
|
||||
let task = binding.get_task(id)?;
|
||||
let task = binding.task(id)?;
|
||||
|
||||
// Delete the task
|
||||
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> {
|
||||
// Get the task the user wants to stop
|
||||
let task = tasks.get_task(id)?;
|
||||
let task = tasks.task(id)?;
|
||||
// Stop the task
|
||||
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> {
|
||||
// Get the task the user wants to start
|
||||
let task = tasks.get_task(id)?;
|
||||
let task = tasks.task(id)?;
|
||||
// Start the task
|
||||
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> {
|
||||
// Get the task the user wants to complete
|
||||
let task = tasks.get_task(id)?;
|
||||
let task = tasks.task(id)?;
|
||||
// Complete the task
|
||||
task.complete();
|
||||
|
||||
@ -144,3 +145,14 @@ pub fn done(tasks: &mut Tasks, id: usize) -> Result<(), TasksError> {
|
||||
output::success(output::task_msg("completed", task, id));
|
||||
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(())
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
use chrono::{Local, NaiveDateTime};
|
||||
use colored::{ColoredString, Colorize};
|
||||
use chrono::NaiveDateTime;
|
||||
use colored::Colorize;
|
||||
|
||||
pub fn parse_fuzzy_date(date_string: Option<String>) -> Option<NaiveDateTime> {
|
||||
if let Some(date_string) = date_string {
|
||||
@ -11,24 +11,3 @@ pub fn parse_fuzzy_date(date_string: Option<String>) -> Option<NaiveDateTime> {
|
||||
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()
|
||||
}
|
||||
}
|
||||
|
@ -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(())
|
||||
}
|
||||
|
@ -26,7 +26,7 @@ pub fn task_msg(msg: &str, task: &Task, id: usize) -> String {
|
||||
format!(
|
||||
"{} task: {}({})",
|
||||
msg,
|
||||
task.title.blue(),
|
||||
task.title_string().blue(),
|
||||
id.to_string().cyan()
|
||||
)
|
||||
}
|
||||
|
@ -1,29 +1,26 @@
|
||||
use colored::Colorize;
|
||||
use prettytable::{format, row, Row, Table};
|
||||
|
||||
use crate::cli::dates;
|
||||
use crate::tasks::{Status, Task, Tasks};
|
||||
use crate::tasks::{Task, Tasks};
|
||||
|
||||
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
|
||||
Row::from([
|
||||
id.to_string().bright_black().italic(),
|
||||
task.status.as_string().bright_black().italic(),
|
||||
task.title.clone().bright_black().italic(),
|
||||
dates::date_as_string(&task.when).bright_black().italic(),
|
||||
dates::date_as_string(&task.deadline)
|
||||
.bright_black()
|
||||
.italic(),
|
||||
task.status_string().bright_black().italic(),
|
||||
task.title_string().bright_black().italic(),
|
||||
task.when_string().bright_black().italic(),
|
||||
task.deadline_string().bright_black().italic(),
|
||||
])
|
||||
} else {
|
||||
// Generate normal colored rows for uncompleted tasks
|
||||
Row::from([
|
||||
id.to_string().cyan(),
|
||||
task.status.as_string(),
|
||||
task.title.clone().white(),
|
||||
dates::date_as_string(&task.when),
|
||||
dates::date_as_string(&task.deadline),
|
||||
task.status_string(),
|
||||
task.title_string(),
|
||||
task.when_string(),
|
||||
task.deadline_string(),
|
||||
])
|
||||
}
|
||||
}
|
||||
@ -52,7 +49,16 @@ pub fn task_table(task: &Task, id: usize) -> Table {
|
||||
let mut table = Table::new();
|
||||
table.set_titles(row!["Item".magenta().bold(), "Value".magenta().bold()]);
|
||||
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
|
||||
}
|
||||
|
61
src/data.rs
61
src/data.rs
@ -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")
|
||||
}
|
16
src/main.rs
16
src/main.rs
@ -1,6 +1,6 @@
|
||||
mod args;
|
||||
mod cli;
|
||||
mod data;
|
||||
mod repo;
|
||||
mod tasks;
|
||||
|
||||
use clap::Parser;
|
||||
@ -10,29 +10,31 @@ use crate::args::TasksArgs;
|
||||
|
||||
fn main() {
|
||||
// Generate the file paths for tasks
|
||||
let repo_path = &data::tasks_repo_string();
|
||||
let tasks_file = "tasks";
|
||||
let repo_path = repo::tasks_repo_string();
|
||||
let tasks_file_path = repo::tasks_file_path();
|
||||
|
||||
// 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(..) => (),
|
||||
Err(error) => panic!("{} {:?}", "error:".red().bold(), error),
|
||||
};
|
||||
|
||||
// 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,
|
||||
Err(error) => panic!("{} {:?}", "error:".red().bold(), error),
|
||||
};
|
||||
|
||||
// Parse command line arguments
|
||||
let arguments = TasksArgs::parse();
|
||||
|
||||
// Execute the inputted command line arguments
|
||||
match cli::execute(&mut tasks, arguments) {
|
||||
Ok(..) => (),
|
||||
Err(error) => panic!("{} {:?}", "error:".red().bold(), error),
|
||||
};
|
||||
|
||||
// Save any changes
|
||||
cli::git::execute(repo_path, String::from("add --all")).unwrap();
|
||||
data::save_tasks(&repo_path, &tasks).unwrap();
|
||||
repo::save_tasks(&tasks_file_path, &tasks).unwrap();
|
||||
repo::execute(&repo_path, String::from("add --all")).unwrap();
|
||||
}
|
||||
|
97
src/repo.rs
Normal file
97
src/repo.rs
Normal 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(())
|
||||
}
|
213
src/tasks.rs
213
src/tasks.rs
@ -1,16 +1,37 @@
|
||||
use chrono::NaiveDateTime;
|
||||
use colored::*;
|
||||
use chrono::{Local, NaiveDateTime};
|
||||
use colored::{ColoredString, Colorize};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
#[derive(Debug)]
|
||||
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)]
|
||||
pub enum Status {
|
||||
Inbox,
|
||||
Pending,
|
||||
Active,
|
||||
Complete,
|
||||
Inbox, // When you create a new task without a when date
|
||||
Pending, // When you give a task a when date
|
||||
Active, // When you have started a task
|
||||
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)]
|
||||
@ -24,21 +45,6 @@ pub struct Task {
|
||||
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 {
|
||||
pub fn new(
|
||||
title: String,
|
||||
@ -88,6 +94,9 @@ impl Task {
|
||||
|
||||
if let Some(when) = when {
|
||||
self.when = Some(when);
|
||||
if self.is_inbox() {
|
||||
self.pend()
|
||||
};
|
||||
};
|
||||
|
||||
if let Some(deadline) = deadline {
|
||||
@ -98,22 +107,118 @@ impl Task {
|
||||
self.reminder = Some(reminder);
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
pub fn start(&mut self) {
|
||||
self.status = Status::Active;
|
||||
}
|
||||
|
||||
pub fn stop(&mut self) {
|
||||
if self.when.is_some() {
|
||||
impl Task {
|
||||
pub fn inbox(&mut self) {
|
||||
self.status = Status::Inbox;
|
||||
} else {
|
||||
self.status = Status::Pending;
|
||||
}
|
||||
self.when = None;
|
||||
}
|
||||
|
||||
pub fn complete(&mut self) {
|
||||
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 {
|
||||
@ -124,25 +229,32 @@ impl Tasks {
|
||||
tasks: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn task_exists(&self, id: usize) -> bool {
|
||||
id < self.len()
|
||||
}
|
||||
|
||||
impl Tasks {
|
||||
/// Checks if tasks are empty
|
||||
pub fn is_empty(&self) -> bool {
|
||||
self.len() == 0
|
||||
}
|
||||
|
||||
pub fn get_task(&mut self, id: usize) -> Result<&mut Task, TasksError> {
|
||||
if self.is_empty() {
|
||||
Err(no_tasks_available())
|
||||
} else if self.task_exists(id) {
|
||||
Ok(&mut self.tasks.as_mut().unwrap()[id])
|
||||
} else {
|
||||
Err(task_not_found(id))
|
||||
}
|
||||
/// Checks if a task exists from an id
|
||||
pub fn exists(&self, id: usize) -> bool {
|
||||
id < self.len()
|
||||
}
|
||||
|
||||
/// 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) {
|
||||
if self.is_empty() {
|
||||
self.tasks = Some(vec![task]);
|
||||
@ -152,11 +264,11 @@ impl Tasks {
|
||||
}
|
||||
|
||||
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);
|
||||
Ok(())
|
||||
} else {
|
||||
Err(task_not_found(id))
|
||||
Err(TasksError::no_task(id))
|
||||
}
|
||||
}
|
||||
|
||||
@ -170,21 +282,10 @@ impl Tasks {
|
||||
|
||||
pub fn clear(&mut self) -> Result<(), TasksError> {
|
||||
if self.is_empty() {
|
||||
Err(no_tasks_available())
|
||||
Err(TasksError::no_tasks())
|
||||
} 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(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user