From c9a73b462dbe272dc879b764c6ad38bd6fdb19cd Mon Sep 17 00:00:00 2001 From: NotAShelf Date: Wed, 20 Aug 2025 11:14:51 +0300 Subject: [PATCH] commands: more consistent error propagation Signed-off-by: NotAShelf Change-Id: I6a6a69647a0eb8de028e4251465fbb94f0a14cef --- src/commands/decode.rs | 52 ++++++++++++++++++++++---------------- src/commands/delete.rs | 13 +++------- src/commands/import.rs | 57 +++++++++++++++++++----------------------- src/commands/list.rs | 15 +++++------ 4 files changed, 66 insertions(+), 71 deletions(-) diff --git a/src/commands/decode.rs b/src/commands/decode.rs index e6df237..9dc9116 100644 --- a/src/commands/decode.rs +++ b/src/commands/decode.rs @@ -24,26 +24,33 @@ impl DecodeCommand for SqliteClipboardDb { s } else { let mut buf = String::new(); - if let Err(e) = in_.read_to_string(&mut buf) { - log::error!("Failed to read stdin for decode: {e}"); - } + in_ + .read_to_string(&mut buf) + .map_err(|e| StashError::DecodeRead(e.to_string()))?; buf }; // If input is empty or whitespace, treat as error and trigger fallback if input_str.trim().is_empty() { - log::info!("No input provided to decode; relaying clipboard to stdout"); + log::debug!("No input provided to decode; relaying clipboard to stdout"); if let Ok((mut reader, _mime)) = get_contents(ClipboardType::Regular, Seat::Unspecified, MimeType::Any) { let mut buf = Vec::new(); - if let Err(err) = reader.read_to_end(&mut buf) { - log::error!("Failed to read clipboard for relay: {err}"); - } else { - let _ = out.write_all(&buf); - } + reader.read_to_end(&mut buf).map_err(|e| { + StashError::DecodeRead(format!( + "Failed to read clipboard for relay: {e}" + )) + })?; + out.write_all(&buf).map_err(|e| { + StashError::DecodeWrite(format!( + "Failed to write clipboard relay: {e}" + )) + })?; } else { - log::error!("Failed to get clipboard contents for relay"); + return Err(StashError::DecodeGet( + "Failed to get clipboard contents for relay".to_string(), + )); } return Ok(()); } @@ -54,25 +61,28 @@ impl DecodeCommand for SqliteClipboardDb { &mut out, Some(input_str.clone()), ) { - Ok(()) => { - log::info!("Entry decoded"); - }, + Ok(()) => Ok(()), Err(e) => { - log::error!("Failed to decode entry: {e}"); + // On decode failure, relay clipboard as fallback if let Ok((mut reader, _mime)) = get_contents(ClipboardType::Regular, Seat::Unspecified, MimeType::Any) { let mut buf = Vec::new(); - if let Err(err) = reader.read_to_end(&mut buf) { - log::error!("Failed to read clipboard for relay: {err}"); - } else { - let _ = out.write_all(&buf); - } + reader.read_to_end(&mut buf).map_err(|err| { + StashError::DecodeRead(format!( + "Failed to read clipboard for relay: {err}" + )) + })?; + out.write_all(&buf).map_err(|err| { + StashError::DecodeWrite(format!( + "Failed to write clipboard relay: {err}" + )) + })?; + Ok(()) } else { - log::error!("Failed to get clipboard contents for relay"); + Err(e) } }, } - Ok(()) } } diff --git a/src/commands/delete.rs b/src/commands/delete.rs index e7e2c92..dd84989 100644 --- a/src/commands/delete.rs +++ b/src/commands/delete.rs @@ -8,15 +8,8 @@ pub trait DeleteCommand { impl DeleteCommand for SqliteClipboardDb { fn delete(&self, input: impl Read) -> Result { - match self.delete_entries(input) { - Ok(deleted) => { - log::info!("Deleted {deleted} entries"); - Ok(deleted) - }, - Err(e) => { - log::error!("Failed to delete entries: {e}"); - Err(e) - }, - } + let deleted = self.delete_entries(input)?; + log::info!("Deleted {deleted} entries"); + Ok(deleted) } } diff --git a/src/commands/import.rs b/src/commands/import.rs index 95cb06e..05833d7 100644 --- a/src/commands/import.rs +++ b/src/commands/import.rs @@ -1,7 +1,5 @@ use std::io::{self, BufRead}; -use log::{error, info}; - use crate::db::{ ClipboardDb, Entry, @@ -12,18 +10,6 @@ use crate::db::{ pub trait ImportCommand { /// Import clipboard entries from TSV format. - /// - /// # Arguments - /// - /// * `input` - A readable stream containing TSV lines, each of the form - /// `\t`. - /// * `max_items` - The maximum number of clipboard entries to keep after - /// import. If set to `u64::MAX`, no trimming occurs. - /// - /// # Returns - /// - /// * `Ok(())` if all entries are imported and trimming succeeds. - /// * `Err(StashError)` if any error occurs during import or trimming. fn import_tsv( &self, input: impl io::Read, @@ -39,16 +25,21 @@ impl ImportCommand for SqliteClipboardDb { ) -> Result<(), StashError> { let reader = io::BufReader::new(input); let mut imported = 0; - for line in reader.lines().map_while(Result::ok) { + for (lineno, line) in reader.lines().enumerate() { + let line = line.map_err(|e| { + StashError::Store(format!("Failed to read line {lineno}: {e}")) + })?; let mut parts = line.splitn(2, '\t'); let (Some(id_str), Some(val)) = (parts.next(), parts.next()) else { - error!("Malformed TSV line: {line:?}"); - continue; + return Err(StashError::Store(format!( + "Malformed TSV line {lineno}: {line:?}" + ))); }; let Ok(_id) = id_str.parse::() else { - error!("Failed to parse id from line: {id_str}"); - continue; + return Err(StashError::Store(format!( + "Failed to parse id from line {lineno}: {id_str}" + ))); }; let entry = Entry { @@ -56,22 +47,26 @@ impl ImportCommand for SqliteClipboardDb { mime: detect_mime(val.as_bytes()), }; - match self.conn.execute( - "INSERT INTO clipboard (contents, mime) VALUES (?1, ?2)", - rusqlite::params![entry.contents, entry.mime], - ) { - Ok(_) => { - imported += 1; - info!("Imported entry from TSV"); - }, - Err(e) => error!("Failed to insert entry: {e}"), - } + self + .conn + .execute( + "INSERT INTO clipboard (contents, mime) VALUES (?1, ?2)", + rusqlite::params![entry.contents, entry.mime], + ) + .map_err(|e| { + StashError::Store(format!( + "Failed to insert entry at line {lineno}: {e}" + )) + })?; + imported += 1; } - info!("Imported {imported} records from TSV into SQLite database."); + + log::info!("Imported {imported} records from TSV into SQLite database."); // Trim database to max_items after import self.trim_db(max_items)?; - info!("Trimmed clipboard database to max_items = {max_items}"); + log::info!("Trimmed clipboard database to max_items = {max_items}"); + Ok(()) } } diff --git a/src/commands/list.rs b/src/commands/list.rs index 1464956..75c1ce5 100644 --- a/src/commands/list.rs +++ b/src/commands/list.rs @@ -16,14 +16,11 @@ impl ListCommand for SqliteClipboardDb { out: impl Write, preview_width: u32, ) -> Result<(), StashError> { - self.list_entries(out, preview_width)?; - log::info!("Listed clipboard entries"); - Ok(()) + self.list_entries(out, preview_width).map(|_| ()) } } impl SqliteClipboardDb { - /// Public TUI listing function for use in main.rs #[allow(clippy::too_many_lines)] pub fn list_tui(&self, preview_width: u32) -> Result<(), StashError> { use std::io::stdout; @@ -272,14 +269,14 @@ impl SqliteClipboardDb { Ok(()) })(); - disable_raw_mode().ok(); - execute!( + // Ignore errors during terminal restore, as we can't recover here. + let _ = disable_raw_mode(); + let _ = execute!( terminal.backend_mut(), LeaveAlternateScreen, DisableMouseCapture - ) - .ok(); - terminal.show_cursor().ok(); + ); + let _ = terminal.show_cursor(); res }