From fd214c48d6fe218c2665f790d958b1f671768c3f Mon Sep 17 00:00:00 2001 From: ThomasNels Date: Sat, 7 Mar 2026 08:10:30 -0800 Subject: [PATCH] feat: added a delete feature to the program --- src/app.rs | 19 +++++++++++++++++++ src/main.rs | 23 ++++++++++++++++++++++- src/provider.rs | 6 ++++++ src/provider_local.rs | 8 ++++++++ src/store_fs.rs | 12 ++++++++++++ 5 files changed, 67 insertions(+), 1 deletion(-) diff --git a/src/app.rs b/src/app.rs index 28ff136..909542f 100644 --- a/src/app.rs +++ b/src/app.rs @@ -12,6 +12,7 @@ pub enum Action { MoveRight, ToggleDetail, Refresh, + Delete, } pub struct App { @@ -136,6 +137,7 @@ impl App { Action::SelectDown => self.select(1), Action::ToggleDetail => self.detail_open = !self.detail_open, Action::Refresh | Action::MoveLeft | Action::MoveRight => {} + Action::Delete => { self.delete_current_card(); }, } false } @@ -168,6 +170,23 @@ impl App { Some((card_id, to_col_id)) } + + pub fn delete_current_card(&mut self) -> Option { + if self.board.columns.is_empty() { + return None; + } + + self.clamp(); + + if self.board.columns[self.col].cards.is_empty() { + return None; + } + + let card = self.board.columns[self.col].cards.remove(self.row); + self.clamp_row(); + + Some(card.id) + } } fn first_non_empty_column(board: &Board) -> Option { diff --git a/src/main.rs b/src/main.rs index fd7ed7b..805d49f 100644 --- a/src/main.rs +++ b/src/main.rs @@ -32,7 +32,7 @@ mod store_fs; use app::{Action, App}; fn help_text() -> &'static str { - "h/l or ←/→ focus j/k or ↑/↓ select H/L move n new e edit Enter detail r refresh Esc close/quit q quit" + "h/l or ←/→ focus j/k or ↑/↓ select H/L move n new e edit d delete Enter detail r refresh Esc close/quit q quit" } fn action_from_key(code: KeyCode) -> Option { @@ -51,6 +51,7 @@ fn action_from_key(code: KeyCode) -> Option { KeyCode::Enter => Action::ToggleDetail, KeyCode::Char('r') => Action::Refresh, + KeyCode::Char('d') => Action::Delete, _ => return None, }) @@ -253,6 +254,26 @@ fn run(terminal: &mut Terminal>) -> io::Result<()> Err(e) => app.banner = Some(format!("Refresh failed: {e}")), } } + Action::Delete => { + if quitting { + continue; + } + if let Some(card_id) = selected_card_id(&app) { + app.apply(a); + if let Err(e) = provider.delete_card(&card_id) { + app.banner = Some(format!("Delete failed: {e}")); + if let Ok(board) = provider.load_board() { + app.board = board; + app.clamp(); + } + } else { + app.banner = Some("Card deleted".to_string()); + } + } else { + app.apply(a); + app.banner = Some("No card selected to delete".to_string()); + } + } _ => { if app.apply(a) { if move_rx.is_some() || !move_queue.is_empty() { diff --git a/src/provider.rs b/src/provider.rs index c99f7bd..dfeaa24 100644 --- a/src/provider.rs +++ b/src/provider.rs @@ -53,6 +53,12 @@ pub trait Provider { msg: "edit_card not supported by current provider".to_string(), }) } + + fn delete_card(&mut self, _card_id: &str) -> Result<(), ProviderError> { + Err(ProviderError::Parse { + msg: "delete_card not supported by current provider".to_string(), + }) + } } pub fn from_env() -> Box { diff --git a/src/provider_local.rs b/src/provider_local.rs index 67b6a5d..dc824e8 100644 --- a/src/provider_local.rs +++ b/src/provider_local.rs @@ -72,6 +72,14 @@ impl Provider for LocalProvider { }, }) } + fn delete_card(&mut self, card_id: &str) -> Result<(), ProviderError> { + store_fs::delete_card(&self.root, card_id) + .map_err(|err| ProviderError::Io { + op: "delete_card".to_string(), + path: self.root.clone(), + source: err, + }) + } } fn map_load_err(op: &str, root: &Path, err: io::Error) -> ProviderError { diff --git a/src/store_fs.rs b/src/store_fs.rs index 1deab85..ff75386 100644 --- a/src/store_fs.rs +++ b/src/store_fs.rs @@ -91,6 +91,18 @@ pub fn move_card(root: &Path, card_id: &str, to_col_id: &str) -> io::Result<()> Ok(()) } +pub fn delete_card(root: &Path, card_id: &str) -> io::Result<()> { + let col_ids = list_columns(root)?; + let src = find_card_column(root, &col_ids, card_id)? + .ok_or_else(|| io::Error::new(io::ErrorKind::NotFound, "card not found"))?; + + let dir = root.join("cols").join(&src); + fs::remove_file(dir.join(format!("{card_id}.md")))?; + order_remove(&dir.join("order.txt"), card_id)?; + + Ok(()) +} + pub fn create_card(root: &Path, to_col_id: &str) -> io::Result { let id = format!("CARD-{}", now_millis()); let dir = root.join("cols").join(to_col_id);