Skip to content

Commit

Permalink
feat: add error handling to refactored commands
Browse files Browse the repository at this point in the history
  • Loading branch information
syntaxbullet committed Jan 10, 2024
1 parent a864a57 commit 6db7cbb
Show file tree
Hide file tree
Showing 3 changed files with 59 additions and 38 deletions.
47 changes: 33 additions & 14 deletions src/main.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
mod task;
mod storage;
mod task;
use clap::{Args, Parser, Subcommand};

#[derive(Parser)]
Expand Down Expand Up @@ -90,16 +90,21 @@ fn main() {
tags: add.tags.unwrap_or(Vec::new()),
};
// validate the task
if !task.validate() {
println!("Invalid task");
return;
if task.validate().is_err() {
// if the task is invalid, print the error and exit
let error = task.validate().unwrap_err();
println!("{}", error);
std::process::exit(1);
}
// append the task to the task list
storage::append_task(task).unwrap();


}
Some(Commands::Update(update)) => {
// check if the id of the task exists
if storage::read_task(update.id).is_err() {
println!("Task with id {} does not exist", update.id);
std::process::exit(1);
}
// get the task to update
let mut task = storage::read_task(update.id).unwrap();
// update the task
Expand All @@ -116,19 +121,33 @@ fn main() {
task.title = title;
}
if let Some(append_tag) = update.append_tag {
task.tags.push(append_tag);
// append the tag list to the task, deduplicating the tags
task.tags.append(
&mut append_tag
.split(',')
.map(|s| s.trim().to_string())
.collect::<Vec<String>>(),
);
}
if let Some(remove_tag) = update.remove_tag {
task.tags.retain(|tag| tag != &remove_tag);
// remove the tag list from the task, deduplicating the tags
task.tags.retain(|tag| {
!remove_tag
.split(',')
.map(|s| s.trim().to_string())
.collect::<Vec<String>>()
.contains(tag)
});
}
// update the task in the task list
// validate the task
if !task.validate() {
println!("Invalid task");
return;
if task.validate().is_err() {
// if the task is invalid, print the error and exit
let error = task.validate().unwrap_err();
println!("{}", error);
std::process::exit(1);
}
// update the task in the task list
storage::update_task(update.id, task).unwrap();

}
Some(Commands::Delete(delete)) => {
// delete the task from the task list
Expand All @@ -146,4 +165,4 @@ fn main() {
println!("No command given");
}
}
}
}
11 changes: 7 additions & 4 deletions src/storage.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,8 @@ pub fn append_task(task: Task) -> Result<(), Box<dyn Error>> {
.create(true)
.open(".citrine")?;
// validate the task
if !task.validate() {
println!("Invalid task");
return Ok(());
if task.validate().is_err() {
return Err("Invalid task".into());
}
writeln!(file, "{}", task)?;
Ok(())
Expand Down Expand Up @@ -46,14 +45,18 @@ pub fn read_tasks() -> Result<Vec<Task>, Box<dyn Error>> {
let mut tasks = Vec::new();
for line in contents.lines() {
// skip unparseable lines
if let Some(task) = Task::from_string(line.to_string()) {
if let Ok(task) = Task::from_string(line.to_string()) {
tasks.push(task);
}
}
Ok(tasks)
}
pub fn write_tasks(tasks: Vec<Task>) -> Result<(), Box<dyn Error>> {
let mut file = File::create(".citrine")?;
// sort the tasks by id
let mut tasks = tasks;
tasks.sort_by(|a, b| a.id.cmp(&b.id));

for task in tasks {
writeln!(file, "{}", task)?;
}
Expand Down
39 changes: 19 additions & 20 deletions src/task.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
use std::error::Error;

pub struct Task {
pub id: u8,
pub title: String,
Expand Down Expand Up @@ -45,7 +47,7 @@ impl std::fmt::Display for Task {
}

impl Task {
pub fn from_string(input: String) -> Option<Task> {
pub fn from_string(input: String) -> Result<Task, Box<dyn Error>>{
let mut result = Task {
id: 0,
title: String::new(),
Expand All @@ -54,18 +56,17 @@ impl Task {
priority: None,
tags: Vec::new(),
};

result.id = input.split('.').next()?.parse::<u8>().unwrap();
result.status = input.split('[').nth(1)?.chars().next()?;
result.title = input.split('"').nth(1)?.to_string();
result.id = input.split('.').next().unwrap().parse::<u8>().unwrap_or(0);
result.status = input.split('[').nth(1).unwrap().chars().next().unwrap_or('-');
result.title = input.split('"').nth(1).unwrap_or("").to_string();

let mut attributes = input.split(';');
attributes.next();

for attribute in attributes {
let mut attribute = attribute.split(':');
let key = attribute.next()?.trim();
let value = attribute.next()?.trim();
let key = attribute.next().unwrap().trim();
let value = attribute.next().unwrap_or("").trim();

match key {
"due" => result.due_date = Some(value.to_string()),
Expand All @@ -74,41 +75,39 @@ impl Task {
_ => (),
}
}
// validate the task
if !result.validate() {
return None;
}
Some(result)
// validate the task result and error if it is invalid
result.validate()?;
Ok(result)
}
pub fn validate(&self) -> bool {
pub fn validate(&self) -> Result<bool, Box<dyn Error>> {
if self.id == 0 {
return false;
return Err("Invalid ID 0".into());
}
if self.title.is_empty() {
return false;
return Err("Invalid title".into());
}
if self.status != ' ' && self.status != 'x' && self.status != '>' && self.status != '!' {
return false;
return Err("Invalid status".into());
}
if let Some(priority) = self.priority {
if priority < 1 || priority > 9 {
return false;
return Err("Invalid priority".into());
}
}
if let Some(due_date) = &self.due_date {
// if the date can't be parsed as rfc3339, or naive, then return false
if chrono::DateTime::parse_from_rfc3339(due_date).is_err()
&& chrono::NaiveDate::parse_from_str(due_date, "%Y-%m-%d").is_err()
{
return false;
return Err("Invalid date".into());
}
if chrono::DateTime::parse_from_rfc3339(due_date).is_ok() {
let date = chrono::DateTime::parse_from_rfc3339(due_date).unwrap();
if date < chrono::Utc::now() {
return false;
return Err("Invalid date".into());
}
}
}
true
Ok(true)
}
}

0 comments on commit 6db7cbb

Please sign in to comment.