feat: Add get
command #37
4 changed files with 202 additions and 82 deletions
|
@ -44,7 +44,7 @@ pub struct Gen {
|
||||||
|
|
||||||
impl LprsCommand for Gen {
|
impl LprsCommand for Gen {
|
||||||
fn run(self, _vault_manager: Vaults) -> LprsResult<()> {
|
fn run(self, _vault_manager: Vaults) -> LprsResult<()> {
|
||||||
println!(
|
print!(
|
||||||
"{}",
|
"{}",
|
||||||
passwords::PasswordGenerator::new()
|
passwords::PasswordGenerator::new()
|
||||||
.length(self.length.get() as usize)
|
.length(self.length.get() as usize)
|
||||||
|
@ -52,7 +52,7 @@ impl LprsCommand for Gen {
|
||||||
.lowercase_letters(self.lowercase)
|
.lowercase_letters(self.lowercase)
|
||||||
.numbers(self.numbers)
|
.numbers(self.numbers)
|
||||||
.symbols(self.symbols)
|
.symbols(self.symbols)
|
||||||
.strict(true)
|
.strict(false)
|
||||||
.generate_one()
|
.generate_one()
|
||||||
.expect("The length cannot be zero")
|
.expect("The length cannot be zero")
|
||||||
);
|
);
|
||||||
|
|
140
src/cli/get_command.rs
Normal file
140
src/cli/get_command.rs
Normal file
|
@ -0,0 +1,140 @@
|
||||||
|
// Lprs - A local CLI vault manager
|
||||||
|
// Copyright (C) 2024 Awiteb <a@4rs.nl>
|
||||||
|
//
|
||||||
|
// This program is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
//
|
||||||
|
// This program is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU General Public License
|
||||||
|
// along with this program. If not, see <https://www.gnu.org/licenses/gpl-3.0.html>.
|
||||||
|
|
||||||
|
use std::str::FromStr;
|
||||||
|
|
||||||
|
use clap::Args;
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
vault::{Vault, Vaults},
|
||||||
|
LprsCommand,
|
||||||
|
LprsError,
|
||||||
|
LprsResult,
|
||||||
|
};
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Eq, PartialEq)]
|
||||||
|
enum VaultGetField {
|
||||||
|
Index,
|
||||||
|
Name,
|
||||||
|
Username,
|
||||||
|
Password,
|
||||||
|
Service,
|
||||||
|
Note,
|
||||||
|
Custom(String),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FromStr for VaultGetField {
|
||||||
|
type Err = LprsError;
|
||||||
|
|
||||||
|
fn from_str(input: &str) -> Result<Self, Self::Err> {
|
||||||
|
Ok(match input.to_lowercase().as_str() {
|
||||||
|
"index" => Self::Index,
|
||||||
|
"name" => Self::Name,
|
||||||
|
"username" => Self::Username,
|
||||||
|
"password" => Self::Password,
|
||||||
|
"service" => Self::Service,
|
||||||
|
"note" => Self::Note,
|
||||||
|
_ => Self::Custom(input.to_owned()),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl VaultGetField {
|
||||||
|
/// Returns the field from the vault
|
||||||
|
pub fn get_from_vault<'a>(&self, vault: &'a Vault) -> Option<&'a str> {
|
||||||
|
match self {
|
||||||
|
Self::Index => None,
|
||||||
|
Self::Name => Some(&vault.name),
|
||||||
|
Self::Username => vault.username.as_deref(),
|
||||||
|
Self::Password => vault.password.as_deref(),
|
||||||
|
Self::Service => vault.service.as_deref(),
|
||||||
|
Self::Note => vault.note.as_deref(),
|
||||||
|
Self::Custom(custom_field) => vault.custom_fields.get(custom_field).map(|x| x.as_str()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the field as `&str`
|
||||||
|
pub fn as_str(&self) -> &str {
|
||||||
|
match self {
|
||||||
|
Self::Index => "index",
|
||||||
|
Self::Name => "name",
|
||||||
|
Self::Username => "username",
|
||||||
|
Self::Password => "password",
|
||||||
|
Self::Service => "service",
|
||||||
|
Self::Note => "note",
|
||||||
|
Self::Custom(field) => field.as_str(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Args)]
|
||||||
|
#[command(author, version, about, long_about = None)]
|
||||||
|
/// 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,
|
||||||
|
/// A Specific field to get.
|
||||||
|
///
|
||||||
|
/// Can be [name,username,password,service,note,"string"] where the string
|
||||||
|
/// means a custom field
|
||||||
|
#[arg(value_parser = VaultGetField::from_str)]
|
||||||
|
field: Option<VaultGetField>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl LprsCommand for Get {
|
||||||
|
fn run(self, vault_manager: Vaults) -> LprsResult<()> {
|
||||||
|
let parsed_index = self.location.trim().parse::<usize>();
|
||||||
|
let Some((index, vault)) = (if let Ok(index) = parsed_index {
|
||||||
|
vault_manager.vaults.get(index - 1).map(|v| (index, v))
|
||||||
|
} else {
|
||||||
|
vault_manager
|
||||||
|
.vaults
|
||||||
|
.iter()
|
||||||
|
.enumerate()
|
||||||
|
.find(|(_, v)| v.name == self.location)
|
||||||
|
}) else {
|
||||||
|
return Err(LprsError::Other(format!(
|
||||||
|
"There is no vault with the given {} `{}`",
|
||||||
|
if parsed_index.is_ok() {
|
||||||
|
"index"
|
||||||
|
} else {
|
||||||
|
"name"
|
||||||
|
},
|
||||||
|
self.location.trim(),
|
||||||
|
)));
|
||||||
|
};
|
||||||
|
|
||||||
|
if let Some(field) = self.field {
|
||||||
|
if field == VaultGetField::Index {
|
||||||
|
print!("{index}");
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(value) = field.get_from_vault(vault) {
|
||||||
|
print!("{value}")
|
||||||
|
} else {
|
||||||
|
return Err(LprsError::Other(format!(
|
||||||
|
"There is no value for `{}`",
|
||||||
|
field.as_str()
|
||||||
|
)));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
println!("{vault}");
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
|
@ -14,8 +14,6 @@
|
||||||
// You should have received a copy of the GNU General Public License
|
// You should have received a copy of the GNU General Public License
|
||||||
// along with this program. If not, see <https://www.gnu.org/licenses/gpl-3.0.html>.
|
// along with this program. If not, see <https://www.gnu.org/licenses/gpl-3.0.html>.
|
||||||
|
|
||||||
use std::num::NonZeroU64;
|
|
||||||
|
|
||||||
use clap::Args;
|
use clap::Args;
|
||||||
use inquire::Select;
|
use inquire::Select;
|
||||||
|
|
||||||
|
@ -25,9 +23,6 @@ use crate::{vault::Vaults, LprsCommand, LprsError, LprsResult};
|
||||||
#[command(author, version, about, long_about = None)]
|
#[command(author, version, about, long_about = None)]
|
||||||
/// List command, used to list the vaults and search
|
/// List command, used to list the vaults and search
|
||||||
pub struct List {
|
pub struct List {
|
||||||
/// Return the password with spesifc index
|
|
||||||
#[arg(short, long, value_name = "INDEX")]
|
|
||||||
get: Option<NonZeroU64>,
|
|
||||||
/// Filter the select list
|
/// Filter the select list
|
||||||
#[arg(short, long, value_name = "TEXT")]
|
#[arg(short, long, value_name = "TEXT")]
|
||||||
filter: Option<String>,
|
filter: Option<String>,
|
||||||
|
@ -43,80 +38,66 @@ impl LprsCommand for List {
|
||||||
"Looks like there is no vaults 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) {
|
|
||||||
log::info!("Getting the vault at index: {user_vault_index}");
|
let pattern = if self.regex || self.filter.is_none() {
|
||||||
if user_vault_index >= vault_manager.vaults.len() {
|
self.filter.unwrap_or_else(|| ".".to_owned())
|
||||||
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 {
|
} else {
|
||||||
let pattern = if self.regex || self.filter.is_none() {
|
format!(
|
||||||
self.filter.unwrap_or_else(|| ".".to_owned())
|
".*{}.*",
|
||||||
} else {
|
regex::escape(self.filter.as_deref().unwrap_or(""))
|
||||||
format!(
|
)
|
||||||
".*{}.*",
|
};
|
||||||
regex::escape(self.filter.as_deref().unwrap_or(""))
|
log::debug!("Listing vaults filtered by: {pattern}");
|
||||||
)
|
|
||||||
};
|
|
||||||
log::debug!("Listing vaults filtered by: {pattern}");
|
|
||||||
|
|
||||||
let re = regex::Regex::new(&pattern)?;
|
let re = regex::Regex::new(&pattern)?;
|
||||||
|
|
||||||
let vaults_list = vault_manager
|
let vaults_list = vault_manager
|
||||||
.vaults
|
.vaults
|
||||||
.iter()
|
.iter()
|
||||||
.enumerate()
|
.enumerate()
|
||||||
.filter_map(|(idx, v)| {
|
.filter_map(|(idx, v)| {
|
||||||
if re.is_match(&v.name)
|
if re.is_match(&v.name)
|
||||||
|| v.username.as_deref().is_some_and(|u| re.is_match(u))
|
|| 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.service.as_deref().is_some_and(|s| re.is_match(s))
|
||||||
|| v.note.as_deref().is_some_and(|n| re.is_match(n))
|
|| v.note.as_deref().is_some_and(|n| re.is_match(n))
|
||||||
{
|
{
|
||||||
return Some(format!("{}) {}", idx + 1, v.list_name()));
|
return Some(format!("{}) {}", idx + 1, v.list_name()));
|
||||||
}
|
}
|
||||||
None
|
None
|
||||||
})
|
})
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
if vaults_list.is_empty() {
|
if vaults_list.is_empty() {
|
||||||
return Err(LprsError::Other(
|
return Err(LprsError::Other(
|
||||||
"There is no result match your filter".to_owned(),
|
"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::<usize>()
|
|
||||||
.unwrap_or_default();
|
|
||||||
|
|
||||||
log::debug!("The user selected the vault at index: {vault_idx}");
|
|
||||||
|
|
||||||
println!(
|
|
||||||
"{}",
|
|
||||||
vault_manager
|
|
||||||
.vaults
|
|
||||||
.get(vault_idx - 1)
|
|
||||||
.expect("The index is correct")
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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::<usize>()
|
||||||
|
.unwrap_or_default();
|
||||||
|
|
||||||
|
log::debug!("The user selected the vault at index: {vault_idx}");
|
||||||
|
|
||||||
|
println!(
|
||||||
|
"{}",
|
||||||
|
vault_manager
|
||||||
|
.vaults
|
||||||
|
.get(vault_idx - 1)
|
||||||
|
.expect("The index is correct")
|
||||||
|
);
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -126,11 +107,6 @@ impl LprsCommand for List {
|
||||||
"You cannot use the `--regex` flag if you did not use the search option".to_owned(),
|
"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(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -31,6 +31,8 @@ pub mod edit_command;
|
||||||
pub mod export_command;
|
pub mod export_command;
|
||||||
/// Generate command, used to generate a password
|
/// Generate command, used to generate a password
|
||||||
pub mod gen_command;
|
pub mod gen_command;
|
||||||
|
/// Command to get a entire vault or single field from it
|
||||||
|
pub mod get_command;
|
||||||
/// Import command, used to import vaults from the exported files, `lprs` or
|
/// Import command, used to import vaults from the exported files, `lprs` or
|
||||||
/// `BitWarden`
|
/// `BitWarden`
|
||||||
pub mod import_command;
|
pub mod import_command;
|
||||||
|
@ -56,13 +58,15 @@ pub enum Commands {
|
||||||
Edit(edit_command::Edit),
|
Edit(edit_command::Edit),
|
||||||
/// Generate a password
|
/// Generate a password
|
||||||
Gen(gen_command::Gen),
|
Gen(gen_command::Gen),
|
||||||
|
/// Get a entire vault or single field from it
|
||||||
|
Get(get_command::Get),
|
||||||
/// Export the vaults
|
/// Export the vaults
|
||||||
Export(export_command::Export),
|
Export(export_command::Export),
|
||||||
/// Import vaults
|
/// Import vaults
|
||||||
Import(import_command::Import),
|
Import(import_command::Import),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl_commands!(Commands, Add Remove List Clean Edit Gen Export Import);
|
impl_commands!(Commands, Add Remove List Clean Edit Gen Get Export Import);
|
||||||
|
|
||||||
#[derive(Parser, Debug)]
|
#[derive(Parser, Debug)]
|
||||||
#[command(author, version, about, long_about = None)]
|
#[command(author, version, about, long_about = None)]
|
||||||
|
|
Loading…
Reference in a new issue