mirror of
https://github.com/NotAShelf/stash.git
synced 2026-04-13 14:33:47 +00:00
stash: allow confirming destructive operations with --ask
Signed-off-by: NotAShelf <raf@notashelf.dev> Change-Id: I6a6a69644c23734a8b088e20473d381390d532b4
This commit is contained in:
parent
f6bf5586ad
commit
86001652cd
1 changed files with 111 additions and 49 deletions
90
src/main.rs
90
src/main.rs
|
|
@ -6,6 +6,7 @@ use std::{
|
||||||
};
|
};
|
||||||
|
|
||||||
use clap::{CommandFactory, Parser, Subcommand};
|
use clap::{CommandFactory, Parser, Subcommand};
|
||||||
|
use inquire::Confirm;
|
||||||
|
|
||||||
mod commands;
|
mod commands;
|
||||||
mod db;
|
mod db;
|
||||||
|
|
@ -39,6 +40,10 @@ struct Cli {
|
||||||
#[arg(long)]
|
#[arg(long)]
|
||||||
db_path: Option<PathBuf>,
|
db_path: Option<PathBuf>,
|
||||||
|
|
||||||
|
/// Ask for confirmation before destructive operations
|
||||||
|
#[arg(long)]
|
||||||
|
ask: bool,
|
||||||
|
|
||||||
#[command(flatten)]
|
#[command(flatten)]
|
||||||
verbosity: clap_verbosity_flag::Verbosity,
|
verbosity: clap_verbosity_flag::Verbosity,
|
||||||
}
|
}
|
||||||
|
|
@ -67,16 +72,28 @@ enum Command {
|
||||||
/// Explicitly specify type: "id" or "query"
|
/// Explicitly specify type: "id" or "query"
|
||||||
#[arg(long, value_parser = ["id", "query"])]
|
#[arg(long, value_parser = ["id", "query"])]
|
||||||
r#type: Option<String>,
|
r#type: Option<String>,
|
||||||
|
|
||||||
|
/// Ask for confirmation before deleting
|
||||||
|
#[arg(long)]
|
||||||
|
ask: bool,
|
||||||
},
|
},
|
||||||
|
|
||||||
/// Wipe all clipboard history
|
/// Wipe all clipboard history
|
||||||
Wipe,
|
Wipe {
|
||||||
|
/// Ask for confirmation before wiping
|
||||||
|
#[arg(long)]
|
||||||
|
ask: bool,
|
||||||
|
},
|
||||||
|
|
||||||
/// Import clipboard data from stdin (default: TSV format)
|
/// Import clipboard data from stdin (default: TSV format)
|
||||||
Import {
|
Import {
|
||||||
/// Explicitly specify format: "tsv" (default)
|
/// Explicitly specify format: "tsv" (default)
|
||||||
#[arg(long, value_parser = ["tsv"])]
|
#[arg(long, value_parser = ["tsv"])]
|
||||||
r#type: Option<String>,
|
r#type: Option<String>,
|
||||||
|
|
||||||
|
/// Ask for confirmation before importing
|
||||||
|
#[arg(long)]
|
||||||
|
ask: bool,
|
||||||
},
|
},
|
||||||
|
|
||||||
/// Watch clipboard for changes and store automatically
|
/// Watch clipboard for changes and store automatically
|
||||||
|
|
@ -144,17 +161,16 @@ fn main() {
|
||||||
"Failed to list entries",
|
"Failed to list entries",
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
"json" => {
|
|
||||||
// Implement JSON output
|
"json" => match db.list_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}");
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
}
|
|
||||||
_ => {
|
_ => {
|
||||||
log::error!("Unsupported format: {format}");
|
log::error!("Unsupported format: {format}");
|
||||||
}
|
}
|
||||||
|
|
@ -166,7 +182,21 @@ fn main() {
|
||||||
"Failed to decode entry",
|
"Failed to decode entry",
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
Some(Command::Delete { arg, r#type }) => match (arg, r#type.as_deref()) {
|
Some(Command::Delete { arg, r#type, ask }) => {
|
||||||
|
let mut should_proceed = true;
|
||||||
|
if ask {
|
||||||
|
should_proceed =
|
||||||
|
Confirm::new("Are you sure you want to delete clipboard entries?")
|
||||||
|
.with_default(false)
|
||||||
|
.prompt()
|
||||||
|
.unwrap_or(false);
|
||||||
|
|
||||||
|
if !should_proceed {
|
||||||
|
log::info!("Aborted by user.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if should_proceed {
|
||||||
|
match (arg, r#type.as_deref()) {
|
||||||
(Some(s), Some("id")) => {
|
(Some(s), Some("id")) => {
|
||||||
if let Ok(id) = s.parse::<u64>() {
|
if let Ok(id) = s.parse::<u64>() {
|
||||||
use std::io::Cursor;
|
use std::io::Cursor;
|
||||||
|
|
@ -189,22 +219,53 @@ fn main() {
|
||||||
"Failed to delete entry by id",
|
"Failed to delete entry by id",
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
report_error(db.query_delete(&s), "Failed to delete entry by query");
|
report_error(
|
||||||
|
db.query_delete(&s),
|
||||||
|
"Failed to delete entry by query",
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
(None, _) => {
|
(None, _) => {
|
||||||
report_error(db.delete(io::stdin()), "Failed to delete entry from stdin");
|
report_error(
|
||||||
|
db.delete(io::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) => {
|
}
|
||||||
|
}
|
||||||
|
Some(Command::Wipe { ask }) => {
|
||||||
|
let mut should_proceed = true;
|
||||||
|
if ask {
|
||||||
|
should_proceed =
|
||||||
|
Confirm::new("Are you sure you want to wipe all clipboard history?")
|
||||||
|
.with_default(false)
|
||||||
|
.prompt()
|
||||||
|
.unwrap_or(false);
|
||||||
|
if !should_proceed {
|
||||||
|
log::info!("Aborted by user.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if should_proceed {
|
||||||
report_error(db.wipe(), "Failed to wipe database");
|
report_error(db.wipe(), "Failed to wipe database");
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Some(Command::Import { r#type }) => {
|
Some(Command::Import { r#type, ask }) => {
|
||||||
// Default format is TSV (Cliphist compatible)
|
let mut should_proceed = true;
|
||||||
|
if ask {
|
||||||
|
should_proceed = Confirm::new("Are you sure you want to import clipboard data? This may overwrite existing entries.")
|
||||||
|
.with_default(false)
|
||||||
|
.prompt()
|
||||||
|
.unwrap_or(false);
|
||||||
|
if !should_proceed {
|
||||||
|
log::info!("Aborted by user.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if should_proceed {
|
||||||
let format = r#type.as_deref().unwrap_or("tsv");
|
let format = r#type.as_deref().unwrap_or("tsv");
|
||||||
match format {
|
match format {
|
||||||
"tsv" => {
|
"tsv" => {
|
||||||
|
|
@ -215,12 +276,13 @@ fn main() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
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() {
|
||||||
eprintln!("Failed to print help: {e}");
|
log::error!("Failed to print help: {e}");
|
||||||
}
|
}
|
||||||
println!();
|
println!();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue