Skip to content

Commit

Permalink
Feature request: deleting entries from the commandline (#462)
Browse files Browse the repository at this point in the history
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'.
  • Loading branch information
replydev authored Jul 29, 2024
2 parents b974057 + 29bb837 commit 4561a3d
Show file tree
Hide file tree
Showing 2 changed files with 76 additions and 0 deletions.
72 changes: 72 additions & 0 deletions src/arguments/delete.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
use std::io::{self, Write};

use clap::Args;
use color_eyre::eyre::eyre;

use crate::otp::otp_element::OTPDatabase;

use super::SubcommandExecutor;

#[derive(Args)]
pub struct DeleteArgs {
/// Code Index
#[arg(short, long, required_unless_present_any=["issuer", "label"])]
pub index: Option<usize>,

/// Issuer of the first matching code that will be deleted
#[arg(short = 's', long, required_unless_present_any=["index", "label"])]
pub issuer: Option<String>,

/// Label of the first matching code that will be deleted
#[arg(short, long, required_unless_present_any=["index","issuer"])]
pub label: Option<String>,
}

impl SubcommandExecutor for DeleteArgs {
fn run_command(self, mut otp_database: OTPDatabase) -> color_eyre::Result<OTPDatabase> {
let index_to_delete = self
.index
.and_then(|i| i.checked_sub(1))
.or_else(|| get_first_matching_element(&otp_database, &self))
.ok_or(eyre!("No code has been found using the given arguments"))?;

let mut output = String::with_capacity(1);

let element = otp_database.elements_ref().get(index_to_delete).unwrap();
print!(
"Are you sure you want to delete the {}th code ({}, {}) [Y,N]: ",
index_to_delete + 1,
element.issuer,
element.label
);
io::stdout().flush()?;

io::stdin().read_line(&mut output)?;

if output.trim().eq_ignore_ascii_case("y") {
otp_database.delete_element(index_to_delete);
Ok(otp_database)
} else {
Err(eyre!("Operation interrupt by the user"))
}
}
}

fn get_first_matching_element(
otp_database: &OTPDatabase,
delete_args: &DeleteArgs,
) -> Option<usize> {
otp_database
.elements_ref()
.iter()
.enumerate()
.find(|(_, element)| {
element
.issuer
.contains(delete_args.issuer.as_deref().unwrap_or_default())
&& element
.label
.contains(delete_args.label.as_deref().unwrap_or_default())
})
.map(|(index, _)| index)
}
4 changes: 4 additions & 0 deletions src/arguments/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ use crate::otp::otp_element::OTPDatabase;
use crate::{arguments::extract::ExtractArgs, dashboard};
use clap::{Parser, Subcommand};
use color_eyre::eyre::eyre;
use delete::DeleteArgs;
use enum_dispatch::enum_dispatch;

use self::{
Expand All @@ -10,6 +11,7 @@ use self::{
};

mod add;
mod delete;
mod edit;
mod export;
mod extract;
Expand Down Expand Up @@ -44,6 +46,8 @@ pub enum CotpSubcommands {
Edit(EditArgs),
/// List codes
List(ListArgs),
/// Delete codes
Delete(DeleteArgs),
/// Import codes from other apps
Import(ImportArgs),
/// Export cotp database
Expand Down

0 comments on commit 4561a3d

Please sign in to comment.