From 6a323d06c11acdcda8fc17e37e7d8789f4acbc8f Mon Sep 17 00:00:00 2001 From: Joseph Ferano Date: Wed, 7 Jun 2023 23:34:40 +0700 Subject: [PATCH] Create or open file. Save on add/remove/edit task --- .gitignore | 1 + src/app.rs | 44 +++++++++++++++++++++++++++----------------- src/input.rs | 8 ++++++-- src/main.rs | 2 +- 4 files changed, 35 insertions(+), 20 deletions(-) diff --git a/.gitignore b/.gitignore index 2037310..bf8c5c1 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ /target /.idea/ /kanban-tui.json +/kanban.json diff --git a/src/app.rs b/src/app.rs index 221b8e2..6f8f979 100644 --- a/src/app.rs +++ b/src/app.rs @@ -2,6 +2,8 @@ // use int_enum::IntEnum; use serde::{Deserialize, Serialize}; use std::cmp::min; +use std::fs::OpenOptions; +use std::io::Read; use tui_textarea::TextArea; #[cfg(test)] @@ -34,6 +36,7 @@ impl Default for Task { #[derive(Deserialize, Serialize, Debug)] pub struct Project { pub name: String, + pub filepath: String, pub selected_column_idx: usize, pub columns: Vec } @@ -46,16 +49,6 @@ pub enum KanbanError { Io(#[from] std::io::Error), } -impl Default for Project { - fn default() -> Self { - Project { - name: String::new(), - columns: vec![], - selected_column_idx: 0, - } - } -} - #[derive(Debug)] pub enum TaskEditFocus { Title, @@ -135,14 +128,14 @@ impl<'a> Column { pub fn select_next_task(&mut self) { let task_idx = &mut self.selected_task_idx; - *task_idx = min(*task_idx + 1, self.tasks.len() - 1) + *task_idx = min(*task_idx + 1, self.tasks.len().saturating_sub(1)) } pub fn select_last_task(&mut self) { self.selected_task_idx = self.tasks.len() - 1; } - pub fn get_task_state_from_curr_selected_task(&self) -> Option> { + pub fn get_task_state_from_curr_selected_task(&self) -> Option> { self.get_selected_task().map(|t| { TaskState { title: TextArea::from(t.title.lines()), @@ -155,10 +148,16 @@ impl<'a> Column { } impl Project { - pub fn new(name: &str) -> Self { + pub fn new(name: &str, filepath: String) -> Self { Project { name: name.to_owned(), - columns: vec![], + filepath, + columns: vec![ + Column::new("Todo"), + Column::new("InProgress"), + Column::new("Done"), + Column::new("Ideas"), + ], selected_column_idx: 0, } } @@ -168,13 +167,24 @@ impl Project { } pub fn load(filepath: String) -> Result { - let json = std::fs::read_to_string(filepath)?; - Self::load_from_json(&json) + let mut file = OpenOptions::new() + .write(true) + .read(true) + .create(true) + .open(&filepath)?; + + let mut json = String::new(); + file.read_to_string(&mut json)?; + if json.is_empty() { + Ok(Project::new("", filepath)) + } else { + Self::load_from_json(&json) + } } pub fn save(&self) { let json = serde_json::to_string_pretty(&self).unwrap(); - std::fs::write("kanban-tui.json", json).unwrap(); + std::fs::write(&self.filepath, json).unwrap(); } pub fn get_selected_column(&self) -> &Column { diff --git a/src/input.rs b/src/input.rs index 7f403f5..20e03d1 100644 --- a/src/input.rs +++ b/src/input.rs @@ -44,7 +44,8 @@ pub fn handle_input(state: &mut AppState) -> Result<(), std::io::Error> { } else { column.add_task(title, description); } - state.task_edit_state = None + state.task_edit_state = None; + project.save(); } _ => (), } @@ -83,7 +84,10 @@ pub fn handle_input(state: &mut AppState) -> Result<(), std::io::Error> { KeyCode::Char('n') => state.task_edit_state = Some(TaskState::default()), KeyCode::Char('e') => state.task_edit_state = column.get_task_state_from_curr_selected_task(), - KeyCode::Char('D') => column.remove_task(), + KeyCode::Char('D') => { + column.remove_task(); + project.save(); + }, _ => {} } } diff --git a/src/main.rs b/src/main.rs index efcf035..8a31fc4 100644 --- a/src/main.rs +++ b/src/main.rs @@ -11,7 +11,7 @@ use tui::backend::CrosstermBackend; use tui::Terminal; fn main() -> anyhow::Result<()> { - let pattern = env::args().nth(1).expect("Path to task database not provided"); + let pattern = env::args().nth(1).unwrap_or("kanban.json".to_string()); let mut state = AppState::new(Project::load(pattern)?); enable_raw_mode()?;