treewide: format with rustfmt

Signed-off-by: NotAShelf <raf@notashelf.dev>
Change-Id: I6a6a69642c2865f41a4b141ddf39a198a3fc2e09
This commit is contained in:
raf 2025-08-20 09:40:20 +03:00
commit 6a5cd9b95d
Signed by: NotAShelf
GPG key ID: 29D95B64378DB4BF
10 changed files with 1191 additions and 1132 deletions

View file

@ -1,10 +1,9 @@
use crate::db::{ClipboardDb, SqliteClipboardDb};
use std::io::{Read, Write}; use std::io::{Read, Write};
use crate::db::StashError;
use wl_clipboard_rs::paste::{ClipboardType, MimeType, Seat, get_contents}; use wl_clipboard_rs::paste::{ClipboardType, MimeType, Seat, get_contents};
use crate::db::{ClipboardDb, SqliteClipboardDb, StashError};
pub trait DecodeCommand { pub trait DecodeCommand {
fn decode( fn decode(
&self, &self,
@ -50,10 +49,14 @@ impl DecodeCommand for SqliteClipboardDb {
} }
// Try decode as usual // Try decode as usual
match self.decode_entry(input_str.as_bytes(), &mut out, Some(input_str.clone())) { match self.decode_entry(
input_str.as_bytes(),
&mut out,
Some(input_str.clone()),
) {
Ok(()) => { Ok(()) => {
log::info!("Entry decoded"); log::info!("Entry decoded");
} },
Err(e) => { Err(e) => {
log::error!("Failed to decode entry: {e}"); log::error!("Failed to decode entry: {e}");
if let Ok((mut reader, _mime)) = if let Ok((mut reader, _mime)) =
@ -68,7 +71,7 @@ impl DecodeCommand for SqliteClipboardDb {
} else { } else {
log::error!("Failed to get clipboard contents for relay"); log::error!("Failed to get clipboard contents for relay");
} }
} },
} }
Ok(()) Ok(())
} }

View file

@ -1,7 +1,7 @@
use crate::db::{ClipboardDb, SqliteClipboardDb, StashError};
use std::io::Read; use std::io::Read;
use crate::db::{ClipboardDb, SqliteClipboardDb, StashError};
pub trait DeleteCommand { pub trait DeleteCommand {
fn delete(&self, input: impl Read) -> Result<usize, StashError>; fn delete(&self, input: impl Read) -> Result<usize, StashError>;
} }
@ -12,11 +12,11 @@ impl DeleteCommand for SqliteClipboardDb {
Ok(deleted) => { Ok(deleted) => {
log::info!("Deleted {deleted} entries"); log::info!("Deleted {deleted} entries");
Ok(deleted) Ok(deleted)
} },
Err(e) => { Err(e) => {
log::error!("Failed to delete entries: {e}"); log::error!("Failed to delete entries: {e}");
Err(e) Err(e)
} },
} }
} }
} }

View file

@ -1,15 +1,21 @@
use std::io::Write; use std::io::Write;
use crate::db::{ClipboardDb, SqliteClipboardDb, StashError};
use unicode_segmentation::UnicodeSegmentation; use unicode_segmentation::UnicodeSegmentation;
use unicode_width::UnicodeWidthStr; use unicode_width::UnicodeWidthStr;
use crate::db::{ClipboardDb, SqliteClipboardDb, StashError};
pub trait ListCommand { pub trait ListCommand {
fn list(&self, out: impl Write, preview_width: u32) -> Result<(), StashError>; fn list(&self, out: impl Write, preview_width: u32)
-> Result<(), StashError>;
} }
impl ListCommand for SqliteClipboardDb { impl ListCommand for SqliteClipboardDb {
fn list(&self, out: impl Write, preview_width: u32) -> Result<(), StashError> { fn list(
&self,
out: impl Write,
preview_width: u32,
) -> Result<(), StashError> {
self.list_entries(out, preview_width)?; self.list_entries(out, preview_width)?;
log::info!("Listed clipboard entries"); log::info!("Listed clipboard entries");
Ok(()) Ok(())
@ -20,11 +26,16 @@ impl SqliteClipboardDb {
/// Public TUI listing function for use in main.rs /// Public TUI listing function for use in main.rs
#[allow(clippy::too_many_lines)] #[allow(clippy::too_many_lines)]
pub fn list_tui(&self, preview_width: u32) -> Result<(), StashError> { pub fn list_tui(&self, preview_width: u32) -> Result<(), StashError> {
use std::io::stdout;
use crossterm::{ use crossterm::{
event::{self, DisableMouseCapture, EnableMouseCapture, Event, KeyCode}, event::{self, DisableMouseCapture, EnableMouseCapture, Event, KeyCode},
execute, execute,
terminal::{ terminal::{
EnterAlternateScreen, LeaveAlternateScreen, disable_raw_mode, enable_raw_mode, EnterAlternateScreen,
LeaveAlternateScreen,
disable_raw_mode,
enable_raw_mode,
}, },
}; };
use ratatui::{ use ratatui::{
@ -34,7 +45,6 @@ impl SqliteClipboardDb {
text::{Line, Span}, text::{Line, Span},
widgets::{Block, Borders, List, ListItem, ListState}, widgets::{Block, Borders, List, ListItem, ListState},
}; };
use std::io::stdout;
// Query entries from DB // Query entries from DB
let mut stmt = self let mut stmt = self
@ -61,7 +71,8 @@ impl SqliteClipboardDb {
let mime: Option<String> = row let mime: Option<String> = row
.get(2) .get(2)
.map_err(|e| StashError::ListDecode(e.to_string()))?; .map_err(|e| StashError::ListDecode(e.to_string()))?;
let preview = crate::db::preview_entry(&contents, mime.as_deref(), preview_width); let preview =
crate::db::preview_entry(&contents, mime.as_deref(), preview_width);
let mime_str = mime.as_deref().unwrap_or("").to_string(); let mime_str = mime.as_deref().unwrap_or("").to_string();
let id_str = id.to_string(); let id_str = id.to_string();
max_id_width = max_id_width.max(id_str.width()); max_id_width = max_id_width.max(id_str.width());
@ -74,8 +85,8 @@ impl SqliteClipboardDb {
execute!(stdout, EnterAlternateScreen, EnableMouseCapture) execute!(stdout, EnterAlternateScreen, EnableMouseCapture)
.map_err(|e| StashError::ListDecode(e.to_string()))?; .map_err(|e| StashError::ListDecode(e.to_string()))?;
let backend = CrosstermBackend::new(stdout); let backend = CrosstermBackend::new(stdout);
let mut terminal = let mut terminal = Terminal::new(backend)
Terminal::new(backend).map_err(|e| StashError::ListDecode(e.to_string()))?; .map_err(|e| StashError::ListDecode(e.to_string()))?;
let mut state = ListState::default(); let mut state = ListState::default();
if !entries.is_empty() { if !entries.is_empty() {
@ -165,7 +176,8 @@ impl SqliteClipboardDb {
mwidth += g_width; mwidth += g_width;
} }
// Compose the row as highlight + id + space + preview + space + mimetype // Compose the row as highlight + id + space + preview + space +
// mimetype
let mut spans = Vec::new(); let mut spans = Vec::new();
let (id, preview, mime) = entry; let (id, preview, mime) = entry;
if Some(i) == selected { if Some(i) == selected {
@ -234,11 +246,11 @@ impl SqliteClipboardDb {
} else { } else {
i + 1 i + 1
} }
} },
None => 0, None => 0,
}; };
state.select(Some(i)); state.select(Some(i));
} },
KeyCode::Up | KeyCode::Char('k') => { KeyCode::Up | KeyCode::Char('k') => {
let i = match state.selected() { let i = match state.selected() {
Some(i) => { Some(i) => {
@ -247,12 +259,12 @@ impl SqliteClipboardDb {
} else { } else {
i - 1 i - 1
} }
} },
None => 0, None => 0,
}; };
state.select(Some(i)); state.select(Some(i));
} },
_ => {} _ => {},
} }
} }
} }

View file

@ -1,6 +1,4 @@
use crate::db::{ClipboardDb, SqliteClipboardDb}; use crate::db::{ClipboardDb, SqliteClipboardDb, StashError};
use crate::db::StashError;
pub trait QueryCommand { pub trait QueryCommand {
fn query_delete(&self, query: &str) -> Result<usize, StashError>; fn query_delete(&self, query: &str) -> Result<usize, StashError>;

View file

@ -1,7 +1,7 @@
use crate::db::{ClipboardDb, SqliteClipboardDb};
use std::io::Read; use std::io::Read;
use crate::db::{ClipboardDb, SqliteClipboardDb};
pub trait StoreCommand { pub trait StoreCommand {
fn store( fn store(
&self, &self,

View file

@ -1,9 +1,10 @@
use crate::db::{ClipboardDb, Entry, SqliteClipboardDb}; use std::{io::Read, time::Duration};
use smol::Timer; use smol::Timer;
use std::io::Read;
use std::time::Duration;
use wl_clipboard_rs::paste::{ClipboardType, Seat, get_contents}; use wl_clipboard_rs::paste::{ClipboardType, Seat, get_contents};
use crate::db::{ClipboardDb, Entry, SqliteClipboardDb};
pub trait WatchCommand { pub trait WatchCommand {
fn watch(&self, max_dedupe_search: u64, max_items: u64); fn watch(&self, max_dedupe_search: u64, max_items: u64);
} }
@ -64,13 +65,13 @@ impl WatchCommand for SqliteClipboardDb {
// Drop clipboard contents after storing // Drop clipboard contents after storing
last_contents = None; last_contents = None;
} }
} },
Err(e) => { Err(e) => {
let error_msg = e.to_string(); let error_msg = e.to_string();
if !error_msg.contains("empty") { if !error_msg.contains("empty") {
log::error!("Failed to get clipboard contents: {e}"); log::error!("Failed to get clipboard contents: {e}");
} }
} },
} }
Timer::after(Duration::from_millis(500)).await; Timer::after(Duration::from_millis(500)).await;
} }

View file

@ -1,6 +1,4 @@
use crate::db::{ClipboardDb, SqliteClipboardDb}; use crate::db::{ClipboardDb, SqliteClipboardDb, StashError};
use crate::db::StashError;
pub trait WipeCommand { pub trait WipeCommand {
fn wipe(&self) -> Result<(), StashError>; fn wipe(&self) -> Result<(), StashError>;

View file

@ -1,20 +1,19 @@
use std::env; use std::{
use std::fmt; env,
use std::fs; fmt,
use std::io::{BufRead, BufReader, Read, Write}; fs,
use std::str; io::{BufRead, BufReader, Read, Write},
str,
};
use base64::{Engine, engine::general_purpose::STANDARD};
use imagesize::{ImageSize, ImageType}; use imagesize::{ImageSize, ImageType};
use log::{error, info, warn}; use log::{error, info, warn};
use regex::Regex; use regex::Regex;
use rusqlite::{Connection, OptionalExtension, params}; use rusqlite::{Connection, OptionalExtension, params};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use thiserror::Error;
use base64::Engine;
use base64::engine::general_purpose::STANDARD;
use serde_json::json; use serde_json::json;
use thiserror::Error;
#[derive(Error, Debug)] #[derive(Error, Debug)]
pub enum StashError { pub enum StashError {
@ -67,7 +66,11 @@ pub trait ClipboardDb {
fn trim_db(&self, max: u64) -> Result<(), StashError>; fn trim_db(&self, max: u64) -> Result<(), StashError>;
fn delete_last(&self) -> Result<(), StashError>; fn delete_last(&self) -> Result<(), StashError>;
fn wipe_db(&self) -> Result<(), StashError>; fn wipe_db(&self) -> Result<(), StashError>;
fn list_entries(&self, out: impl Write, preview_width: u32) -> Result<usize, StashError>; fn list_entries(
&self,
out: impl Write,
preview_width: u32,
) -> Result<usize, StashError>;
fn decode_entry( fn decode_entry(
&self, &self,
in_: impl Read, in_: impl Read,
@ -98,7 +101,8 @@ pub struct SqliteClipboardDb {
impl SqliteClipboardDb { impl SqliteClipboardDb {
pub fn new(conn: Connection) -> Result<Self, StashError> { pub fn new(conn: Connection) -> Result<Self, StashError> {
conn.execute_batch( conn
.execute_batch(
"CREATE TABLE IF NOT EXISTS clipboard ( "CREATE TABLE IF NOT EXISTS clipboard (
id INTEGER PRIMARY KEY AUTOINCREMENT, id INTEGER PRIMARY KEY AUTOINCREMENT,
contents BLOB NOT NULL, contents BLOB NOT NULL,
@ -138,7 +142,7 @@ impl SqliteClipboardDb {
let contents_str = match mime.as_deref() { let contents_str = match mime.as_deref() {
Some(m) if m.starts_with("text/") || m == "application/json" => { Some(m) if m.starts_with("text/") || m == "application/json" => {
String::from_utf8_lossy(&contents).to_string() String::from_utf8_lossy(&contents).to_string()
} },
_ => STANDARD.encode(&contents), _ => STANDARD.encode(&contents),
}; };
entries.push(json!({ entries.push(json!({
@ -148,7 +152,8 @@ impl SqliteClipboardDb {
})); }));
} }
serde_json::to_string_pretty(&entries).map_err(|e| StashError::ListDecode(e.to_string())) serde_json::to_string_pretty(&entries)
.map_err(|e| StashError::ListDecode(e.to_string()))
} }
} }
@ -160,7 +165,10 @@ impl ClipboardDb for SqliteClipboardDb {
max_items: u64, max_items: u64,
) -> Result<u64, StashError> { ) -> Result<u64, StashError> {
let mut buf = Vec::new(); let mut buf = Vec::new();
if input.read_to_end(&mut buf).is_err() || buf.is_empty() || buf.len() > 5 * 1_000_000 { if input.read_to_end(&mut buf).is_err()
|| buf.is_empty()
|| buf.len() > 5 * 1_000_000
{
return Err(StashError::EmptyOrTooLarge); return Err(StashError::EmptyOrTooLarge);
} }
if buf.iter().all(u8::is_ascii_whitespace) { if buf.iter().all(u8::is_ascii_whitespace) {
@ -175,7 +183,7 @@ impl ClipboardDb for SqliteClipboardDb {
} else { } else {
None None
} }
} },
other => other, other => other,
}; };
@ -186,14 +194,17 @@ impl ClipboardDb for SqliteClipboardDb {
if let Ok(s) = std::str::from_utf8(&buf) { if let Ok(s) = std::str::from_utf8(&buf) {
if re.is_match(s) { if re.is_match(s) {
warn!("Clipboard entry matches sensitive regex, skipping store."); warn!("Clipboard entry matches sensitive regex, skipping store.");
return Err(StashError::Store("Filtered by sensitive regex".to_string())); return Err(StashError::Store(
"Filtered by sensitive regex".to_string(),
));
} }
} }
} }
self.deduplicate(&buf, max_dedupe_search)?; self.deduplicate(&buf, max_dedupe_search)?;
self.conn self
.conn
.execute( .execute(
"INSERT INTO clipboard (contents, mime) VALUES (?1, ?2)", "INSERT INTO clipboard (contents, mime) VALUES (?1, ?2)",
params![buf, mime], params![buf, mime],
@ -224,7 +235,8 @@ impl ClipboardDb for SqliteClipboardDb {
.get(1) .get(1)
.map_err(|e| StashError::DeduplicationDecode(e.to_string()))?; .map_err(|e| StashError::DeduplicationDecode(e.to_string()))?;
if contents == buf { if contents == buf {
self.conn self
.conn
.execute("DELETE FROM clipboard WHERE id = ?1", params![id]) .execute("DELETE FROM clipboard WHERE id = ?1", params![id])
.map_err(|e| StashError::DeduplicationRemove(e.to_string()))?; .map_err(|e| StashError::DeduplicationRemove(e.to_string()))?;
deduped += 1; deduped += 1;
@ -240,10 +252,14 @@ impl ClipboardDb for SqliteClipboardDb {
.map_err(|e| StashError::Trim(e.to_string()))?; .map_err(|e| StashError::Trim(e.to_string()))?;
if count > max { if count > max {
let to_delete = count - max; let to_delete = count - max;
self.conn.execute( self
"DELETE FROM clipboard WHERE id IN (SELECT id FROM clipboard ORDER BY id ASC LIMIT ?1)", .conn
.execute(
"DELETE FROM clipboard WHERE id IN (SELECT id FROM clipboard ORDER \
BY id ASC LIMIT ?1)",
params![i64::try_from(to_delete).unwrap_or(i64::MAX)], params![i64::try_from(to_delete).unwrap_or(i64::MAX)],
).map_err(|e| StashError::Trim(e.to_string()))?; )
.map_err(|e| StashError::Trim(e.to_string()))?;
} }
Ok(()) Ok(())
} }
@ -259,7 +275,8 @@ impl ClipboardDb for SqliteClipboardDb {
.optional() .optional()
.map_err(|e| StashError::DeleteLast(e.to_string()))?; .map_err(|e| StashError::DeleteLast(e.to_string()))?;
if let Some(id) = id { if let Some(id) = id {
self.conn self
.conn
.execute("DELETE FROM clipboard WHERE id = ?1", params![id]) .execute("DELETE FROM clipboard WHERE id = ?1", params![id])
.map_err(|e| StashError::DeleteLast(e.to_string()))?; .map_err(|e| StashError::DeleteLast(e.to_string()))?;
Ok(()) Ok(())
@ -269,13 +286,18 @@ impl ClipboardDb for SqliteClipboardDb {
} }
fn wipe_db(&self) -> Result<(), StashError> { fn wipe_db(&self) -> Result<(), StashError> {
self.conn self
.conn
.execute("DELETE FROM clipboard", []) .execute("DELETE FROM clipboard", [])
.map_err(|e| StashError::Wipe(e.to_string()))?; .map_err(|e| StashError::Wipe(e.to_string()))?;
Ok(()) Ok(())
} }
fn list_entries(&self, mut out: impl Write, preview_width: u32) -> Result<usize, StashError> { fn list_entries(
&self,
mut out: impl Write,
preview_width: u32,
) -> Result<usize, StashError> {
let mut stmt = self let mut stmt = self
.conn .conn
.prepare("SELECT id, contents, mime FROM clipboard ORDER BY id DESC") .prepare("SELECT id, contents, mime FROM clipboard ORDER BY id DESC")
@ -315,11 +337,13 @@ impl ClipboardDb for SqliteClipboardDb {
input input
} else { } else {
let mut buf = String::new(); let mut buf = String::new();
in_.read_to_string(&mut buf) in_
.read_to_string(&mut buf)
.map_err(|e| StashError::DecodeRead(e.to_string()))?; .map_err(|e| StashError::DecodeRead(e.to_string()))?;
buf buf
}; };
let id = extract_id(&s).map_err(|e| StashError::DecodeExtractId(e.to_string()))?; let id =
extract_id(&s).map_err(|e| StashError::DecodeExtractId(e.to_string()))?;
let (contents, _mime): (Vec<u8>, Option<String>) = self let (contents, _mime): (Vec<u8>, Option<String>) = self
.conn .conn
.query_row( .query_row(
@ -328,7 +352,8 @@ impl ClipboardDb for SqliteClipboardDb {
|row| Ok((row.get(0)?, row.get(1)?)), |row| Ok((row.get(0)?, row.get(1)?)),
) )
.map_err(|e| StashError::DecodeGet(e.to_string()))?; .map_err(|e| StashError::DecodeGet(e.to_string()))?;
out.write_all(&contents) out
.write_all(&contents)
.map_err(|e| StashError::DecodeWrite(e.to_string()))?; .map_err(|e| StashError::DecodeWrite(e.to_string()))?;
info!("Decoded entry with id {id}"); info!("Decoded entry with id {id}");
Ok(()) Ok(())
@ -354,7 +379,8 @@ impl ClipboardDb for SqliteClipboardDb {
.get(1) .get(1)
.map_err(|e| StashError::QueryDelete(e.to_string()))?; .map_err(|e| StashError::QueryDelete(e.to_string()))?;
if contents.windows(query.len()).any(|w| w == query.as_bytes()) { if contents.windows(query.len()).any(|w| w == query.as_bytes()) {
self.conn self
.conn
.execute("DELETE FROM clipboard WHERE id = ?1", params![id]) .execute("DELETE FROM clipboard WHERE id = ?1", params![id])
.map_err(|e| StashError::QueryDelete(e.to_string()))?; .map_err(|e| StashError::QueryDelete(e.to_string()))?;
deleted += 1; deleted += 1;
@ -368,7 +394,8 @@ impl ClipboardDb for SqliteClipboardDb {
let mut deleted = 0; let mut deleted = 0;
for line in reader.lines().map_while(Result::ok) { for line in reader.lines().map_while(Result::ok) {
if let Ok(id) = extract_id(&line) { if let Ok(id) = extract_id(&line) {
self.conn self
.conn
.execute("DELETE FROM clipboard WHERE id = ?1", params![id]) .execute("DELETE FROM clipboard WHERE id = ?1", params![id])
.map_err(|e| StashError::DeleteEntry(id, e.to_string()))?; .map_err(|e| StashError::DeleteEntry(id, e.to_string()))?;
deleted += 1; deleted += 1;
@ -476,7 +503,7 @@ pub fn preview_entry(data: &[u8], mime: Option<&str>, width: u32) -> String {
Err(e) => { Err(e) => {
error!("Failed to decode UTF-8 clipboard data: {e}"); error!("Failed to decode UTF-8 clipboard data: {e}");
"" ""
} },
}; };
let s = s.trim().replace(|c: char| c.is_whitespace(), " "); let s = s.trim().replace(|c: char| c.is_whitespace(), " ");
return truncate(&s, width as usize, ""); return truncate(&s, width as usize, "");

View file

@ -1,7 +1,9 @@
use crate::db::{Entry, SqliteClipboardDb, detect_mime};
use log::{error, info};
use std::io::{self, BufRead}; use std::io::{self, BufRead};
use log::{error, info};
use crate::db::{Entry, SqliteClipboardDb, detect_mime};
pub trait ImportCommand { pub trait ImportCommand {
fn import_tsv(&self, input: impl io::Read); fn import_tsv(&self, input: impl io::Read);
} }
@ -34,7 +36,7 @@ impl ImportCommand for SqliteClipboardDb {
Ok(_) => { Ok(_) => {
imported += 1; imported += 1;
info!("Imported entry from TSV"); info!("Imported entry from TSV");
} },
Err(e) => error!("Failed to insert entry: {e}"), Err(e) => error!("Failed to insert entry: {e}"),
} }
} }

View file

@ -6,7 +6,6 @@ use std::{
}; };
use atty::Stream; use atty::Stream;
use clap::{CommandFactory, Parser, Subcommand}; use clap::{CommandFactory, Parser, Subcommand};
use inquire::Confirm; use inquire::Confirm;
@ -14,14 +13,18 @@ mod commands;
mod db; mod db;
mod import; mod import;
use crate::commands::decode::DecodeCommand; use crate::{
use crate::commands::delete::DeleteCommand; commands::{
use crate::commands::list::ListCommand; decode::DecodeCommand,
use crate::commands::query::QueryCommand; delete::DeleteCommand,
use crate::commands::store::StoreCommand; list::ListCommand,
use crate::commands::watch::WatchCommand; query::QueryCommand,
use crate::commands::wipe::WipeCommand; store::StoreCommand,
use crate::import::ImportCommand; watch::WatchCommand,
wipe::WipeCommand,
},
import::ImportCommand,
};
#[derive(Parser)] #[derive(Parser)]
#[command(name = "stash")] #[command(name = "stash")]
@ -65,8 +68,9 @@ enum Command {
/// Decode and output clipboard entry by id /// Decode and output clipboard entry by id
Decode { input: Option<String> }, Decode { input: Option<String> },
/// Delete clipboard entry by id (if numeric), or entries matching a query (if not). /// Delete clipboard entry by id (if numeric), or entries matching a query (if
/// Numeric arguments are treated as ids. Use --type to specify explicitly. /// not). Numeric arguments are treated as ids. Use --type to specify
/// explicitly.
Delete { Delete {
/// Id or query string /// Id or query string
arg: Option<String>, arg: Option<String>,
@ -102,13 +106,16 @@ enum Command {
Watch, Watch,
} }
fn report_error<T>(result: Result<T, impl std::fmt::Display>, context: &str) -> Option<T> { fn report_error<T>(
result: Result<T, impl std::fmt::Display>,
context: &str,
) -> Option<T> {
match result { match result {
Ok(val) => Some(val), Ok(val) => Some(val),
Err(e) => { Err(e) => {
log::error!("{context}: {e}"); log::error!("{context}: {e}");
None None
} },
} }
} }
@ -144,7 +151,7 @@ fn main() {
Err(e) => { Err(e) => {
log::error!("Failed to initialize SQLite database: {e}"); log::error!("Failed to initialize SQLite database: {e}");
process::exit(1); process::exit(1);
} },
}; };
match cli.command { match cli.command {
@ -154,25 +161,28 @@ fn main() {
db.store(io::stdin(), cli.max_dedupe_search, cli.max_items, state), db.store(io::stdin(), cli.max_dedupe_search, cli.max_items, state),
"Failed to store entry", "Failed to store entry",
); );
} },
Some(Command::List { format }) => match format.as_deref() { Some(Command::List { format }) => {
match format.as_deref() {
Some("tsv") => { Some("tsv") => {
report_error( report_error(
db.list(io::stdout(), cli.preview_width), db.list(io::stdout(), cli.preview_width),
"Failed to list entries", "Failed to list entries",
); );
} },
Some("json") => match db.list_json() { Some("json") => {
match db.list_json() {
Ok(json) => { Ok(json) => {
println!("{json}"); println!("{json}");
} },
Err(e) => { Err(e) => {
log::error!("Failed to list entries as JSON: {e}"); log::error!("Failed to list entries as JSON: {e}");
},
} }
}, },
Some(other) => { Some(other) => {
log::error!("Unsupported format: {other}"); log::error!("Unsupported format: {other}");
} },
None => { None => {
if atty::is(Stream::Stdout) { if atty::is(Stream::Stdout) {
report_error( report_error(
@ -185,6 +195,7 @@ fn main() {
"Failed to list entries", "Failed to list entries",
); );
} }
},
} }
}, },
Some(Command::Decode { input }) => { Some(Command::Decode { input }) => {
@ -192,7 +203,7 @@ fn main() {
db.decode(io::stdin(), io::stdout(), input), db.decode(io::stdin(), io::stdout(), input),
"Failed to decode entry", "Failed to decode entry",
); );
} },
Some(Command::Delete { arg, r#type, ask }) => { Some(Command::Delete { arg, r#type, ask }) => {
let mut should_proceed = true; let mut should_proceed = true;
if ask { if ask {
@ -218,10 +229,13 @@ fn main() {
} else { } else {
log::error!("Argument is not a valid id"); log::error!("Argument is not a valid id");
} }
} },
(Some(s), Some("query")) => { (Some(s), Some("query")) => {
report_error(db.query_delete(&s), "Failed to delete entry by query"); report_error(
} db.query_delete(&s),
"Failed to delete entry by query",
);
},
(Some(s), None) => { (Some(s), None) => {
if let Ok(id) = s.parse::<u64>() { if let Ok(id) = s.parse::<u64>() {
use std::io::Cursor; use std::io::Cursor;
@ -235,24 +249,25 @@ fn main() {
"Failed to delete entry by query", "Failed to delete entry by query",
); );
} }
} },
(None, _) => { (None, _) => {
report_error( report_error(
db.delete(io::stdin()), db.delete(io::stdin()),
"Failed to delete entry from stdin", "Failed to delete entry from stdin",
); );
} },
(_, Some(_)) => { (_, Some(_)) => {
log::error!("Unknown type for --type. Use \"id\" or \"query\"."); log::error!("Unknown type for --type. Use \"id\" or \"query\".");
},
} }
} }
} },
}
Some(Command::Wipe { ask }) => { Some(Command::Wipe { ask }) => {
let mut should_proceed = true; let mut should_proceed = true;
if ask { if ask {
should_proceed = should_proceed = Confirm::new(
Confirm::new("Are you sure you want to wipe all clipboard history?") "Are you sure you want to wipe all clipboard history?",
)
.with_default(false) .with_default(false)
.prompt() .prompt()
.unwrap_or(false); .unwrap_or(false);
@ -263,12 +278,15 @@ fn main() {
if should_proceed { if should_proceed {
report_error(db.wipe(), "Failed to wipe database"); report_error(db.wipe(), "Failed to wipe database");
} }
} },
Some(Command::Import { r#type, ask }) => { Some(Command::Import { r#type, ask }) => {
let mut should_proceed = true; let mut should_proceed = true;
if ask { if ask {
should_proceed = Confirm::new("Are you sure you want to import clipboard data? This may overwrite existing entries.") should_proceed = Confirm::new(
"Are you sure you want to import clipboard data? This may \
overwrite existing entries.",
)
.with_default(false) .with_default(false)
.prompt() .prompt()
.unwrap_or(false); .unwrap_or(false);
@ -281,22 +299,22 @@ fn main() {
match format { match format {
"tsv" => { "tsv" => {
db.import_tsv(io::stdin()); db.import_tsv(io::stdin());
} },
_ => { _ => {
log::error!("Unsupported import format: {format}"); log::error!("Unsupported import format: {format}");
},
} }
} }
} },
}
Some(Command::Watch) => { Some(Command::Watch) => {
db.watch(cli.max_dedupe_search, cli.max_items); db.watch(cli.max_dedupe_search, cli.max_items);
} },
None => { None => {
if let Err(e) = Cli::command().print_help() { if let Err(e) = Cli::command().print_help() {
log::error!("Failed to print help: {e}"); log::error!("Failed to print help: {e}");
} }
println!(); println!();
} },
} }
}); });
} }