From 83c7296bf7bf469423f53b024cb65e608ff6c9d9 Mon Sep 17 00:00:00 2001
From: Awiteb
Date: Mon, 29 Apr 2024 17:50:32 +0200
Subject: [PATCH] refactor: Use select for vaults listing (#19)
Reviewed-on: https://git.4rs.nl/awiteb/lprs/pulls/19
Fixes: https://git.4rs.nl/awiteb/lprs/issues/10
Fixes: https://git.4rs.nl/awiteb/lprs/issues/8
Co-authored-by: Awiteb
Co-committed-by: Awiteb
---
src/cli/list_command.rs | 108 +++++++++++++++++++++++++++++++++-------
src/main.rs | 14 ++++--
src/vault/mod.rs | 36 ++++++++++++++
3 files changed, 136 insertions(+), 22 deletions(-)
diff --git a/src/cli/list_command.rs b/src/cli/list_command.rs
index d0f0cc1..48ad82a 100644
--- a/src/cli/list_command.rs
+++ b/src/cli/list_command.rs
@@ -17,6 +17,7 @@
use std::num::NonZeroU64;
use clap::Args;
+use inquire::Select;
use crate::{
vault::{vault_state::*, Vaults},
@@ -26,25 +27,13 @@ use crate::{
#[derive(Debug, Args)]
#[command(author, version, about, long_about = None)]
pub struct List {
- /// Show the clean password
- #[arg(short = 'p', long)]
- unhide_password: bool,
- /// Show the service of the password and search in it if you search
- #[arg(short = 's', long)]
- with_service: bool,
- /// Show the note of the password and search in it if you search
- #[arg(short = 'n', long)]
- with_note: bool,
-
/// Return the password with spesifc index
#[arg(short, long, value_name = "INDEX")]
get: Option,
- /// Search and display only matching passwords.
- ///
- /// The name and username will be searched. And service and note if included
- #[arg(short = 'e', long, value_name = "TEXT")]
- search: Option,
- /// Enable regex in the search
+ /// Filter the select list
+ #[arg(short, long, value_name = "TEXT")]
+ filter: Option,
+ /// Enable regex when use `--filter` option
#[arg(short, long)]
regex: bool,
}
@@ -53,10 +42,93 @@ impl LprsCommand for List {
fn run(self, vault_manager: Vaults) -> LprsResult<()> {
if vault_manager.vaults.is_empty() {
return Err(LprsError::Other(
- "Looks like there is no passwords to list".to_owned(),
+ "Looks like there is no vaults to list".to_owned(),
));
}
+ if let Some(user_vault_index) = self.get.map(|n| (n.get() - 1) as usize) {
+ if user_vault_index >= vault_manager.vaults.len() {
+ return Err(LprsError::Other(
+ "The `--get` index is great then the vaults length".to_owned(),
+ ));
+ }
+ println!(
+ "{}",
+ vault_manager
+ .vaults
+ .get(user_vault_index)
+ .expect("The index is correct")
+ );
+ } else {
+ let pattern = if self.regex || self.filter.is_none() {
+ self.filter.unwrap_or_else(|| ".".to_owned())
+ } else {
+ format!(
+ ".*{}.*",
+ regex::escape(self.filter.as_deref().unwrap_or(""))
+ )
+ };
- todo!("https://git.4rs.nl/awiteb/lprs/issues/8")
+ let re = regex::Regex::new(&pattern)?;
+
+ let vaults_list = vault_manager
+ .vaults
+ .iter()
+ .enumerate()
+ .filter_map(|(idx, v)| {
+ if re.is_match(&v.name)
+ || v.username.as_deref().is_some_and(|u| re.is_match(u))
+ || v.service.as_deref().is_some_and(|s| re.is_match(s))
+ || v.note.as_deref().is_some_and(|n| re.is_match(n))
+ {
+ return Some(format!("{}) {}", idx + 1, v.list_name()));
+ }
+ None
+ })
+ .collect::>();
+
+ if vaults_list.is_empty() {
+ return Err(LprsError::Other(
+ "There is no result match your filter".to_owned(),
+ ));
+ }
+
+ let vault_idx = Select::new("Select a vault to view:", vaults_list)
+ .with_formatter(&|s| {
+ s.value
+ .split_once(") ")
+ .expect("The bracket are hard coded above")
+ .1
+ .to_owned()
+ })
+ .prompt()?
+ .split_once(')')
+ .expect("The bracket are hard coded above")
+ .0
+ .parse::()
+ .unwrap_or_default();
+
+ println!(
+ "{}",
+ vault_manager
+ .vaults
+ .get(vault_idx - 1)
+ .expect("The index is correct")
+ );
+ }
+ Ok(())
+ }
+
+ fn validate_args(&self) -> LprsResult<()> {
+ if self.regex && self.filter.is_none() {
+ return Err(LprsError::Other(
+ "You cannot use the `--regex` flag if you did not use the search option".to_owned(),
+ ));
+ }
+ if self.filter.is_some() && self.get.is_some() {
+ return Err(LprsError::Other(
+ "You cannot search while you want a vault with a specific index".to_owned(),
+ ));
+ }
+ Ok(())
}
}
diff --git a/src/main.rs b/src/main.rs
index bfe7443..e7ee365 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -31,6 +31,7 @@ mod macros;
mod traits;
pub use errors::{Error as LprsError, Result as LprsResult};
+use inquire::InquireError;
pub use traits::*;
pub const STANDARDBASE: GeneralPurpose = GeneralPurpose::new(&alphabet::STANDARD, PAD);
@@ -64,9 +65,14 @@ fn main() -> ExitCode {
}
if let Err(err) = cli::Cli::parse().run() {
- eprintln!("{err}");
- err.exit_code()
- } else {
- ExitCode::SUCCESS
+ if !matches!(
+ err,
+ LprsError::Inquire(InquireError::OperationCanceled)
+ | LprsError::Inquire(InquireError::OperationInterrupted)
+ ) {
+ eprintln!("{err}");
+ return err.exit_code();
+ }
}
+ ExitCode::SUCCESS
}
diff --git a/src/vault/mod.rs b/src/vault/mod.rs
index 81c05d3..bd6b720 100644
--- a/src/vault/mod.rs
+++ b/src/vault/mod.rs
@@ -136,6 +136,22 @@ impl Vault {
cipher::encrypt_some(master_password, self.note.as_ref())?,
))
}
+
+ /// Return the name of the vault with the service if there
+ pub fn list_name(&self) -> String {
+ use std::fmt::Write;
+ let mut list_name = self.name.clone();
+ if let Some(ref username) = self.username {
+ write!(&mut list_name, " <{username}>").expect("String never fail");
+ }
+ if let Some(ref service) = self.service {
+ write!(&mut list_name, " ({service})").expect("String never fail");
+ }
+ if self.username.is_none() && self.password.is_none() && self.note.is_some() {
+ write!(&mut list_name, " *Note").expect("String never fail");
+ }
+ list_name
+ }
}
impl Vaults
@@ -198,3 +214,23 @@ impl std::fmt::Display for Format {
)
}
}
+
+impl std::fmt::Display for Vault {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ write!(f, "Name: {}", self.name)?;
+ if let Some(ref username) = self.username {
+ write!(f, "\nUsername: {username}")?;
+ }
+ if let Some(ref password) = self.password {
+ write!(f, "\nPassword: {password}")?;
+ }
+ if let Some(ref service) = self.service {
+ write!(f, "\nService: {service}")?;
+ }
+ if let Some(ref note) = self.note {
+ write!(f, "\nNote:\n{note}")?;
+ }
+
+ Ok(())
+ }
+}