Skip to content

Commit

Permalink
feat: storage functions
Browse files Browse the repository at this point in the history
  • Loading branch information
syntaxbullet committed Jan 10, 2024
1 parent 65dcc9c commit a864a57
Show file tree
Hide file tree
Showing 9 changed files with 235 additions and 5 deletions.
Empty file removed src/cli.rs
Empty file.
Empty file removed src/commands/add.rs
Empty file.
Empty file removed src/commands/delete.rs
Empty file.
Empty file removed src/commands/list.rs
Empty file.
Empty file removed src/commands/update.rs
Empty file.
152 changes: 147 additions & 5 deletions src/main.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,149 @@
mod task;
fn main() {
let t = task::Task::from_string("1. [>] \"Task 1\"; due: 2020-01-21T00:00; priority: 1; tags: tag1, tag2".to_string()).unwrap();
let is_valid = t.validate();
println!("{}", is_valid);
println!("{}", t);
mod storage;
use clap::{Args, Parser, Subcommand};

#[derive(Parser)]
#[command(name = "citrine", version = "0.1.0")]
#[command(author = "syntaxbullet <syntaxbullet@protonmail.com>")]
#[command(about = "A simple task manager following the unix philosophy")]

pub struct Cli {
#[command(subcommand)]
command: Option<Commands>,
}

#[derive(Subcommand)]

enum Commands {
Add(Add),
Update(Update),
Delete(Delete),
List(List),
}

#[derive(Args)]
/// Add a new task to the task list
struct Add {
/// The title of the task
title: String,
/// The due date of the task in rfc3339 format (e.g. 2021-01-01T00:00:00+00:00)
#[arg(short = 'd', long = "due")]
due_date: Option<String>,
/// The priority of the task [0-9]
#[arg(short = 'p', long = "priority")]
priority: Option<u8>,
/// The tags of the task , must be a comma separated list
#[arg(short = 't', long = "tags")]
tags: Option<Vec<String>>,
}
#[derive(Args)]
/// Update an existing task in the task list
struct Update {
/// The id of the task to update
id: u8,
/// The due date of the task in rfc3339 format (e.g. 2021-01-01T00:00:00+00:00)
#[arg(short = 'd', long = "due")]
due_date: Option<String>,
/// The priority of the task (0-9)
#[arg(short = 'p', long = "priority")]
priority: Option<u8>,
/// The tags of the task, must be a comma separated list
#[arg(short = 't', long = "tags")]
tags: Option<Vec<String>>,
/// The status of the task
#[arg(short = 's', long = "status")]
status: Option<String>,
/// The title of the task
#[arg(short = 'm', long = "message")]
title: Option<String>,
/// remove tags from the task
#[arg(short = 'r', long = "remove-tags")]
remove_tag: Option<String>,
/// append tags to the task
#[arg(short = 'a', long = "append-tags")]
append_tag: Option<String>,
}

#[derive(Args)]
/// Delete an existing task from the task list
struct Delete {
/// The id of the task to remove
id: u8,
}

#[derive(Args)]
/// List all tasks in the task list filtered by the given options
struct List {}

fn main() {
let cli = Cli::parse();

match cli.command {
Some(Commands::Add(add)) => {
let last_id = storage::get_last_id().unwrap_or(0);
let task = task::Task {
id: last_id + 1,
title: add.title,
status: ' ',
due_date: add.due_date,
priority: add.priority,
tags: add.tags.unwrap_or(Vec::new()),
};
// validate the task
if !task.validate() {
println!("Invalid task");
return;
}
// append the task to the task list
storage::append_task(task).unwrap();


}
Some(Commands::Update(update)) => {
// get the task to update
let mut task = storage::read_task(update.id).unwrap();
// update the task
if let Some(due_date) = update.due_date {
task.due_date = Some(due_date);
}
if let Some(priority) = update.priority {
task.priority = Some(priority);
}
if let Some(status) = update.status {
task.status = status.chars().next().unwrap();
}
if let Some(title) = update.title {
task.title = title;
}
if let Some(append_tag) = update.append_tag {
task.tags.push(append_tag);
}
if let Some(remove_tag) = update.remove_tag {
task.tags.retain(|tag| tag != &remove_tag);
}
// validate the task
if !task.validate() {
println!("Invalid task");
return;
}
// 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
storage::delete_task(delete.id).unwrap();
}
Some(Commands::List(_list)) => {
// get all tasks from the task list
let tasks = storage::read_tasks().unwrap();
// print all tasks
for task in tasks {
println!("{}", task);
}
}
None => {
println!("No command given");
}
}
}
84 changes: 84 additions & 0 deletions src/storage.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@

use std::error::Error;
use std::fs::{File, OpenOptions};

use crate::task::Task;
use std::io::Write;
use std::io::Read;

pub fn append_task(task: Task) -> Result<(), Box<dyn Error>> {
let mut file = OpenOptions::new()
.append(true)
.create(true)
.open(".citrine")?;
// validate the task
if !task.validate() {
println!("Invalid task");
return Ok(());
}
writeln!(file, "{}", task)?;
Ok(())
}
pub fn read_task(id: u8) -> Result<Task, Box<dyn Error>> {
let mut file = File::open(".citrine")?;
let mut contents = String::new();
file.read_to_string(&mut contents)?;
for line in contents.lines() {
let task = Task::from_string(line.to_string()).unwrap();
if task.id == id {
return Ok(task);
}
}
Err("Task not found".into())
}
pub fn read_tasks() -> Result<Vec<Task>, Box<dyn Error>> {
let mut file = File::open(".citrine")?;
// if the file is empty, return an empty vector
if file.metadata()?.len() == 0 {
return Ok(Vec::new());
}
// if the file does not exist, return an empty vector
if !std::path::Path::new(".citrine").exists() {
return Ok(Vec::new());
}
let mut contents = String::new();
file.read_to_string(&mut contents)?;
let mut tasks = Vec::new();
for line in contents.lines() {
// skip unparseable lines
if let Some(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")?;
for task in tasks {
writeln!(file, "{}", task)?;
}
Ok(())
}
pub fn delete_task(id: u8) -> Result<(), Box<dyn Error>> {
let mut tasks = read_tasks()?;
tasks.retain(|task| task.id != id);
write_tasks(tasks)?;
Ok(())
}
pub fn update_task(id: u8, task: Task) -> Result<(), Box<dyn Error>> {
let mut tasks = read_tasks()?;
tasks.retain(|t| t.id != id);
tasks.push(task);
write_tasks(tasks)?;
Ok(())
}
pub fn get_last_id() -> Result<u8, Box<dyn Error>> {
let tasks = read_tasks()?;
let mut last_id = 0;
for task in tasks {
if task.id > last_id {
last_id = task.id;
}
}
Ok(last_id)
}
4 changes: 4 additions & 0 deletions src/task.rs
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,10 @@ impl Task {
_ => (),
}
}
// validate the task
if !result.validate() {
return None;
}
Some(result)
}
pub fn validate(&self) -> bool {
Expand Down
Empty file removed src/utils.rs
Empty file.

0 comments on commit a864a57

Please sign in to comment.