Skip to content

Commit 4561a3d

Browse files
authored
Feature request: deleting entries from the commandline (#462)
This PR adds a new sub-command that deletes codes based on their index, issuer or label. Note, deletion of OTP codes it's also supported in the interative dashboard by selecting the desired code and then typing 'd'.
2 parents b974057 + 29bb837 commit 4561a3d

File tree

2 files changed

+76
-0
lines changed

2 files changed

+76
-0
lines changed

src/arguments/delete.rs

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
use std::io::{self, Write};
2+
3+
use clap::Args;
4+
use color_eyre::eyre::eyre;
5+
6+
use crate::otp::otp_element::OTPDatabase;
7+
8+
use super::SubcommandExecutor;
9+
10+
#[derive(Args)]
11+
pub struct DeleteArgs {
12+
/// Code Index
13+
#[arg(short, long, required_unless_present_any=["issuer", "label"])]
14+
pub index: Option<usize>,
15+
16+
/// Issuer of the first matching code that will be deleted
17+
#[arg(short = 's', long, required_unless_present_any=["index", "label"])]
18+
pub issuer: Option<String>,
19+
20+
/// Label of the first matching code that will be deleted
21+
#[arg(short, long, required_unless_present_any=["index","issuer"])]
22+
pub label: Option<String>,
23+
}
24+
25+
impl SubcommandExecutor for DeleteArgs {
26+
fn run_command(self, mut otp_database: OTPDatabase) -> color_eyre::Result<OTPDatabase> {
27+
let index_to_delete = self
28+
.index
29+
.and_then(|i| i.checked_sub(1))
30+
.or_else(|| get_first_matching_element(&otp_database, &self))
31+
.ok_or(eyre!("No code has been found using the given arguments"))?;
32+
33+
let mut output = String::with_capacity(1);
34+
35+
let element = otp_database.elements_ref().get(index_to_delete).unwrap();
36+
print!(
37+
"Are you sure you want to delete the {}th code ({}, {}) [Y,N]: ",
38+
index_to_delete + 1,
39+
element.issuer,
40+
element.label
41+
);
42+
io::stdout().flush()?;
43+
44+
io::stdin().read_line(&mut output)?;
45+
46+
if output.trim().eq_ignore_ascii_case("y") {
47+
otp_database.delete_element(index_to_delete);
48+
Ok(otp_database)
49+
} else {
50+
Err(eyre!("Operation interrupt by the user"))
51+
}
52+
}
53+
}
54+
55+
fn get_first_matching_element(
56+
otp_database: &OTPDatabase,
57+
delete_args: &DeleteArgs,
58+
) -> Option<usize> {
59+
otp_database
60+
.elements_ref()
61+
.iter()
62+
.enumerate()
63+
.find(|(_, element)| {
64+
element
65+
.issuer
66+
.contains(delete_args.issuer.as_deref().unwrap_or_default())
67+
&& element
68+
.label
69+
.contains(delete_args.label.as_deref().unwrap_or_default())
70+
})
71+
.map(|(index, _)| index)
72+
}

src/arguments/mod.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ use crate::otp::otp_element::OTPDatabase;
22
use crate::{arguments::extract::ExtractArgs, dashboard};
33
use clap::{Parser, Subcommand};
44
use color_eyre::eyre::eyre;
5+
use delete::DeleteArgs;
56
use enum_dispatch::enum_dispatch;
67

78
use self::{
@@ -10,6 +11,7 @@ use self::{
1011
};
1112

1213
mod add;
14+
mod delete;
1315
mod edit;
1416
mod export;
1517
mod extract;
@@ -44,6 +46,8 @@ pub enum CotpSubcommands {
4446
Edit(EditArgs),
4547
/// List codes
4648
List(ListArgs),
49+
/// Delete codes
50+
Delete(DeleteArgs),
4751
/// Import codes from other apps
4852
Import(ImportArgs),
4953
/// Export cotp database

0 commit comments

Comments
 (0)