From 00412f6f9863b839d92639a455fdb61dcf11db43 Mon Sep 17 00:00:00 2001
From: Awiteb
Date: Tue, 20 Aug 2024 11:31:55 +0000
Subject: [PATCH 1/3] chore(deps): Add `either` to the dependencies
Signed-off-by: Awiteb
---
Cargo.lock | 7 +++++++
Cargo.toml | 1 +
2 files changed, 8 insertions(+)
diff --git a/Cargo.lock b/Cargo.lock
index eda428d..a3ee073 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -349,6 +349,12 @@ version = "1.0.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0d6ef0072f8a535281e4876be788938b528e9a1d43900b82c2569af7da799125"
+[[package]]
+name = "either"
+version = "1.13.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0"
+
[[package]]
name = "env_logger"
version = "0.10.2"
@@ -708,6 +714,7 @@ dependencies = [
"clap",
"clap_complete",
"directories",
+ "either",
"inquire",
"log",
"passwords",
diff --git a/Cargo.toml b/Cargo.toml
index 6d5eb4d..daafff9 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -32,6 +32,7 @@ base64 = "0.22.1"
clap_complete = "4.5.2"
totp-lite = "2.0.1"
base32 = "0.5.0"
+either = { version = "1.13.0", default-features = false }
[features]
default = ["update-notify"]
--
2.45.2
From 2fc357bfb79fb54e5e2ed0f31596ff9be9b1f9c5 Mon Sep 17 00:00:00 2001
From: Awiteb
Date: Tue, 20 Aug 2024 11:33:14 +0000
Subject: [PATCH 2/3] chore: Clap parser to parse `Either` type
Signed-off-by: Awiteb
---
src/clap_parsers.rs | 29 +++++++++++++++++++++++++++++
1 file changed, 29 insertions(+)
diff --git a/src/clap_parsers.rs b/src/clap_parsers.rs
index 7666505..db60083 100644
--- a/src/clap_parsers.rs
+++ b/src/clap_parsers.rs
@@ -14,9 +14,14 @@
// You should have received a copy of the GNU General Public License
// along with this program. If not, see .
+use std::{fmt::Display, str::FromStr};
+
+use either::Either::{self, Left, Right};
+
use crate::{LprsError, LprsResult};
/// Parse the key & value arguments.
+///
/// ## Errors
/// - If the argument value syntax not `key=value`
pub fn kv_parser(value: &str) -> LprsResult<(String, Option)> {
@@ -30,3 +35,27 @@ pub fn kv_parser(value: &str) -> LprsResult<(String, Option)> {
Ok((value.trim().to_owned(), None))
}
}
+
+/// Parse `Either` type arguments.
+///
+/// ## Errors
+/// - If the argument value can't be parsed to `L` or `R`
+pub fn either_parser(value: &str) -> LprsResult>
+where
+ L: FromStr,
+ R: FromStr,
+ ::Err: Display,
+ ::Err: Display,
+{
+ value
+ .trim()
+ .parse::()
+ .map_err(|err| LprsError::ArgParse(err.to_string()))
+ .map(Left)
+ .or_else(|_| {
+ value
+ .parse::()
+ .map_err(|err| LprsError::ArgParse(err.to_string()))
+ .map(Right)
+ })
+}
--
2.45.2
From 2ac3947f548639ea73ae056eda978b257aeff04b Mon Sep 17 00:00:00 2001
From: Awiteb
Date: Tue, 20 Aug 2024 11:34:30 +0000
Subject: [PATCH 3/3] refactor: Use `Either` type instade of
`String` for index or name
Signed-off-by: Awiteb
---
src/cli/edit_command.rs | 28 +++++++++++----------
src/cli/get_command.rs | 13 ++++++----
src/cli/remove_command.rs | 28 +++++++++++----------
src/utils.rs | 52 +++++++++++++++++++--------------------
4 files changed, 63 insertions(+), 58 deletions(-)
diff --git a/src/cli/edit_command.rs b/src/cli/edit_command.rs
index 23ad011..caa43a1 100644
--- a/src/cli/edit_command.rs
+++ b/src/cli/edit_command.rs
@@ -14,10 +14,13 @@
// You should have received a copy of the GNU General Public License
// along with this program. If not, see .
+use std::num::NonZeroUsize;
+
use clap::Args;
+use either::Either;
use crate::{
- clap_parsers,
+ clap_parsers::{either_parser, kv_parser},
utils,
vault::{cipher, Vaults},
LprsCommand,
@@ -29,8 +32,8 @@ use crate::{
/// Edit command, used to edit the vault content
pub struct Edit {
/// The vault to edit, index or name
- #[arg(name = "INDEX-or-NAME")]
- location: String,
+ #[arg(name = "INDEX-or-NAME", value_parser = either_parser::)]
+ location: Either,
#[arg(short, long)]
/// The new vault name
@@ -61,7 +64,7 @@ pub struct Edit {
/// If the custom field not exist will created it, if it's will update it,
/// if there is no value, you will enter it through a prompt (e.g `-c key`)
#[arg(name = "KEY=VALUE", short = 'c', long = "custom")]
- #[arg(value_parser = clap_parsers::kv_parser)]
+ #[arg(value_parser = kv_parser)]
custom_fields: Vec<(String, Option)>,
/// Force edit, will not return error if there is a problem with the args.
///
@@ -72,16 +75,15 @@ pub struct Edit {
impl LprsCommand for Edit {
fn run(self, mut vault_manager: Vaults) -> LprsResult<()> {
- let vault =
- match utils::vault_by_index_or_name(self.location.trim(), &mut vault_manager.vaults) {
- Ok((_, v)) => v,
- Err(err) => {
- if self.force {
- return Ok(());
- }
- return Err(err);
+ let vault = match utils::vault_by_index_or_name(self.location, &mut vault_manager.vaults) {
+ Ok((_, v)) => v,
+ Err(err) => {
+ if self.force {
+ return Ok(());
}
- };
+ return Err(err);
+ }
+ };
log::info!("Applying the new values to the vault");
if let Some(new_name) = self.name {
diff --git a/src/cli/get_command.rs b/src/cli/get_command.rs
index 237b1c1..93d5498 100644
--- a/src/cli/get_command.rs
+++ b/src/cli/get_command.rs
@@ -14,11 +14,13 @@
// You should have received a copy of the GNU General Public License
// along with this program. If not, see .
-use std::str::FromStr;
+use std::{num::NonZeroUsize, str::FromStr};
use clap::Args;
+use either::Either;
use crate::{
+ clap_parsers::either_parser,
utils,
vault::{cipher, Vault, Vaults},
LprsCommand,
@@ -94,8 +96,9 @@ impl VaultGetField {
/// Command to get a entire vault or single field from it
pub struct Get {
/// Whether the index of the vault or its name
- #[arg(value_name = "INDEX-or-NAME")]
- location: String,
+ #[arg(name = "INDEX-or-NAME", value_parser = either_parser::)]
+ location: Either,
+
/// A Specific field to get.
///
/// Can be [name, username, password, service, note, totp_secret, totp_code,
@@ -103,13 +106,13 @@ pub struct Get {
///
/// where the string means a custom field
#[arg(value_parser = VaultGetField::from_str)]
- field: Option,
+ field: Option,
}
impl LprsCommand for Get {
fn run(self, mut vault_manager: Vaults) -> LprsResult<()> {
let (index, vault) =
- utils::vault_by_index_or_name(self.location.trim(), &mut vault_manager.vaults)?;
+ utils::vault_by_index_or_name(self.location, &mut vault_manager.vaults)?;
if let Some(field) = self.field {
if field == VaultGetField::Index {
diff --git a/src/cli/remove_command.rs b/src/cli/remove_command.rs
index 28d6323..d0a904d 100644
--- a/src/cli/remove_command.rs
+++ b/src/cli/remove_command.rs
@@ -14,16 +14,19 @@
// You should have received a copy of the GNU General Public License
// along with this program. If not, see .
-use clap::Args;
+use std::num::NonZeroUsize;
-use crate::{utils, vault::Vaults, LprsCommand, LprsResult};
+use clap::Args;
+use either::Either;
+
+use crate::{clap_parsers::either_parser, utils, vault::Vaults, LprsCommand, LprsResult};
#[derive(Debug, Args)]
/// Remove command, used to remove a vault from the vaults file
pub struct Remove {
/// The vault to remove, index or name
- #[arg(name = "INDEX-or-NAME")]
- location: String,
+ #[arg(name = "INDEX-or-NAME", value_parser = either_parser::)]
+ location: Either,
/// Force remove, will not return error if there is no vault with the given
/// index or name
@@ -33,16 +36,15 @@ pub struct Remove {
impl LprsCommand for Remove {
fn run(self, mut vault_manager: Vaults) -> LprsResult<()> {
- let index =
- match utils::vault_by_index_or_name(self.location.trim(), &mut vault_manager.vaults) {
- Ok((idx, _)) => idx,
- Err(err) => {
- if self.force {
- return Ok(());
- }
- return Err(err);
+ let index = match utils::vault_by_index_or_name(self.location, &mut vault_manager.vaults) {
+ Ok((idx, _)) => idx,
+ Err(err) => {
+ if self.force {
+ return Ok(());
}
- };
+ return Err(err);
+ }
+ };
vault_manager.vaults.remove(index);
vault_manager.try_export()
}
diff --git a/src/utils.rs b/src/utils.rs
index 74eccba..86492a7 100644
--- a/src/utils.rs
+++ b/src/utils.rs
@@ -15,8 +15,10 @@
// along with this program. If not, see .
use std::collections::BTreeMap;
+use std::num::NonZeroUsize;
use std::{fs, path::PathBuf};
+use either::Either;
use inquire::{validator::Validation, Password, PasswordDisplayMode};
use passwords::{analyzer, scorer};
#[cfg(feature = "update-notify")]
@@ -231,35 +233,31 @@ pub fn prompt_custom(
Ok(new_fields)
}
-/// Returns the vault with its index by its index or name
+/// Returns the vault with its index by either its index or name
///
/// ## Errors
/// - If there is no vault with the given index or name
-pub fn vault_by_index_or_name<'a>(
- index_or_name: &str,
- vaults: &'a mut [Vault],
-) -> LprsResult<(usize, &'a mut Vault)> {
- let parsed_index = index_or_name.parse::();
+pub fn vault_by_index_or_name(
+ location: Either,
+ vaults: &mut [Vault],
+) -> LprsResult<(usize, &mut Vault)> {
+ let idx = location
+ .map_right(|name| {
+ vaults
+ .iter()
+ .enumerate()
+ .find_map(|(idx, v)| (v.name == name).then_some(idx))
+ .ok_or_else(|| {
+ LprsError::Other(format!("There is no vault with the given name `{name}`"))
+ })
+ })
+ .map_left(|idx| LprsResult::Ok(idx.get() - 1))
+ .into_inner()?;
- let Some((index, vault)) = (if let Ok(index) = parsed_index {
- index
- .checked_sub(1)
- .and_then(|zeroindex| vaults.get_mut(zeroindex).map(|v| (index, v)))
- } else {
- vaults
- .iter_mut()
- .enumerate()
- .find(|(_, v)| v.name == index_or_name)
- }) else {
- return Err(LprsError::Other(format!(
- "There is no vault with the given {} `{}`",
- if parsed_index.is_ok() {
- "index"
- } else {
- "name"
- },
- index_or_name,
- )));
- };
- Ok((index, vault))
+ Ok((
+ idx,
+ vaults.get_mut(idx).ok_or_else(|| {
+ LprsError::Other(format!("There is no vault with the given index `{idx}`"))
+ })?,
+ ))
}
--
2.45.2