chore: password refactor #11
18 changed files with 84 additions and 223 deletions
|
@ -17,7 +17,7 @@
|
|||
use clap::Args;
|
||||
|
||||
use crate::{
|
||||
password::{Vault, Vaults},
|
||||
vault::{vault_state::*, Vault, Vaults},
|
||||
LprsResult, RunCommand,
|
||||
};
|
||||
|
||||
|
|
|
@ -18,14 +18,17 @@ use std::fs;
|
|||
|
||||
use clap::Args;
|
||||
|
||||
use crate::{password::Vaults, LprsError, LprsResult, RunCommand};
|
||||
use crate::{
|
||||
vault::{vault_state::*, Vaults},
|
||||
LprsError, LprsResult, RunCommand,
|
||||
};
|
||||
|
||||
#[derive(Debug, Args)]
|
||||
#[command(author, version, about, long_about = None)]
|
||||
pub struct Clean {}
|
||||
|
||||
impl RunCommand for Clean {
|
||||
fn run(&self, password_manager: Vaults) -> LprsResult<()> {
|
||||
fs::write(password_manager.passwords_file, "[]").map_err(LprsError::Io)
|
||||
fn run(&self, password_manager: Vaults<Plain>) -> LprsResult<()> {
|
||||
fs::write(password_manager.vaults_file, "[]").map_err(LprsError::Io)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -19,7 +19,7 @@ use std::num::NonZeroU64;
|
|||
use clap::Args;
|
||||
|
||||
use crate::{
|
||||
password::{Vault, Vaults},
|
||||
vault::{vault_state::*, Vault, Vaults},
|
||||
LprsError, LprsResult, RunCommand,
|
||||
};
|
||||
|
||||
|
@ -47,7 +47,7 @@ pub struct Edit {
|
|||
}
|
||||
|
||||
impl RunCommand for Edit {
|
||||
fn run(&self, mut password_manager: Vaults) -> LprsResult<()> {
|
||||
fn run(&self, mut password_manager: Vaults<Plain>) -> LprsResult<()> {
|
||||
let index = self.index.get() as usize;
|
||||
|
||||
if let Some(vault) = vault_manager.vaults.get_mut(index - 1) {
|
||||
|
@ -74,7 +74,7 @@ impl RunCommand for Edit {
|
|||
Err(LprsError::InvalidVaultIndex(format!(
|
||||
"The index `{}` is greater than the vaults count {}",
|
||||
self.index,
|
||||
password_manager.passwords.len()
|
||||
password_manager.vaults.len()
|
||||
)))
|
||||
}
|
||||
}
|
||||
|
|
|
@ -19,7 +19,7 @@ use std::{fs, io::Error as IoError, io::ErrorKind as IoErrorKind, path::PathBuf}
|
|||
use clap::Args;
|
||||
|
||||
use crate::{
|
||||
password::{BitWardenPasswords, Format, Vaults},
|
||||
vault::{vault_state::*, BitWardenPasswords, Format, Vault, Vaults},
|
||||
LprsError, LprsResult, RunCommand,
|
||||
};
|
||||
|
||||
|
@ -34,7 +34,7 @@ pub struct Export {
|
|||
}
|
||||
|
||||
impl RunCommand for Export {
|
||||
fn run(&self, password_manager: Vaults) -> LprsResult<()> {
|
||||
fn run(&self, password_manager: Vaults<Plain>) -> LprsResult<()> {
|
||||
if self
|
||||
.path
|
||||
.extension()
|
||||
|
@ -42,7 +42,9 @@ impl RunCommand for Export {
|
|||
{
|
||||
if !self.path.exists() {
|
||||
let exported_data = match self.format {
|
||||
Format::Lprs => serde_json::to_string(&password_manager.encrypt()?.passwords),
|
||||
Format::Lprs => serde_json::to_string::<Vec<Vault<Encrypted>>>(
|
||||
&password_manager.encrypt_vaults()?,
|
||||
),
|
||||
Format::BitWarden => {
|
||||
serde_json::to_string(&BitWardenPasswords::from(password_manager))
|
||||
}
|
||||
|
|
|
@ -18,7 +18,10 @@ use std::num::NonZeroU64;
|
|||
|
||||
use clap::Args;
|
||||
|
||||
use crate::{password::Vaults, LprsError, LprsResult, RunCommand};
|
||||
use crate::{
|
||||
vault::{vault_state::*, Vaults},
|
||||
LprsError, LprsResult, RunCommand,
|
||||
};
|
||||
|
||||
#[derive(Debug, Args)]
|
||||
#[command(author, version, about, long_about = None)]
|
||||
|
@ -42,7 +45,7 @@ pub struct Gen {
|
|||
}
|
||||
|
||||
impl RunCommand for Gen {
|
||||
fn run(&self, _password_manager: Vaults) -> LprsResult<()> {
|
||||
fn run(&self, _password_manager: Vaults<Plain>) -> LprsResult<()> {
|
||||
if self.uppercase || self.lowercase || self.numbers || self.symbols {
|
||||
println!(
|
||||
"{}",
|
||||
|
|
|
@ -19,7 +19,7 @@ use std::{fs::File, io::Error as IoError, io::ErrorKind as IoErrorKind, path::Pa
|
|||
use clap::Args;
|
||||
|
||||
use crate::{
|
||||
password::{BitWardenPasswords, Format, Vault, Vaults},
|
||||
vault::{vault_state::*, BitWardenPasswords, Format, Vault, Vaults},
|
||||
LprsError, LprsResult, RunCommand,
|
||||
};
|
||||
|
||||
|
@ -35,7 +35,7 @@ pub struct Import {
|
|||
}
|
||||
|
||||
impl RunCommand for Import {
|
||||
fn run(&self, mut password_manager: Vaults) -> LprsResult<()> {
|
||||
fn run(&self, mut password_manager: Vaults<Plain>) -> LprsResult<()> {
|
||||
if self.path.exists() {
|
||||
if self
|
||||
.path
|
||||
|
|
|
@ -20,7 +20,10 @@ use clap::Args;
|
|||
use comfy_table::Table;
|
||||
use regex::Regex;
|
||||
|
||||
use crate::{password::Vaults, LprsError, LprsResult, RunCommand};
|
||||
use crate::{
|
||||
vault::{vault_state::*, Vaults},
|
||||
LprsError, LprsResult, RunCommand,
|
||||
};
|
||||
|
||||
#[derive(Debug, Args)]
|
||||
#[command(author, version, about, long_about = None)]
|
||||
|
@ -49,8 +52,8 @@ pub struct List {
|
|||
}
|
||||
|
||||
impl RunCommand for List {
|
||||
fn run(&self, password_manager: Vaults) -> LprsResult<()> {
|
||||
if password_manager.passwords.is_empty() {
|
||||
fn run(&self, password_manager: Vaults<Plain>) -> LprsResult<()> {
|
||||
if password_manager.vaults.is_empty() {
|
||||
Err(LprsError::Other(
|
||||
"Looks like there is no passwords to list".to_owned(),
|
||||
))
|
||||
|
|
|
@ -19,7 +19,7 @@ use std::path::PathBuf;
|
|||
use clap::Parser;
|
||||
|
||||
use crate::{
|
||||
password::{self, Vaults},
|
||||
vault::{self, Vaults},
|
||||
LprsError, LprsResult, RunCommand,
|
||||
};
|
||||
|
||||
|
@ -34,14 +34,14 @@ pub mod remove_command;
|
|||
|
||||
crate::create_commands!(
|
||||
enum Commands
|
||||
"Add new password", Add => add_command::Add
|
||||
"Remove password", Remove => remove_command::Remove
|
||||
"List your password and search", List => list_command::List
|
||||
"Clean the password file", Clean => clean_command::Clean
|
||||
"Edit the password content", Edit => edit_command::Edit
|
||||
"Generate password", Gen => gen_command::Gen
|
||||
"Export the passwords", Export => export_command::Export
|
||||
"Import passwords", Import => import_command::Import
|
||||
"Add new vault", Add => add_command::Add
|
||||
"Remove vault", Remove => remove_command::Remove
|
||||
"List your vaults and search", List => list_command::List
|
||||
"Clean the vaults file", Clean => clean_command::Clean
|
||||
"Edit the vault content", Edit => edit_command::Edit
|
||||
"Generate a password", Gen => gen_command::Gen
|
||||
"Export the vaults", Export => export_command::Export
|
||||
"Import vaults", Import => import_command::Import
|
||||
);
|
||||
|
||||
#[derive(Parser, Debug)]
|
||||
|
@ -49,7 +49,7 @@ crate::create_commands!(
|
|||
pub struct Cli {
|
||||
/// The vaults json file
|
||||
#[arg(short, long)]
|
||||
passwords_file: Option<PathBuf>,
|
||||
vaults_file: Option<PathBuf>,
|
||||
|
||||
// TODO: verbose flag
|
||||
#[command(subcommand)]
|
||||
|
@ -59,15 +59,15 @@ pub struct Cli {
|
|||
impl Cli {
|
||||
/// Run the cli
|
||||
pub fn run(self) -> LprsResult<()> {
|
||||
let passwords_file = if let Some(ref path) = self.passwords_file {
|
||||
let vaults_file = if let Some(ref path) = self.vaults_file {
|
||||
path.clone()
|
||||
} else {
|
||||
crate::utils::passwords_file()?
|
||||
crate::utils::vaults_file()?
|
||||
};
|
||||
log::debug!("Getting the vaults file: {}", vaults_file.to_string_lossy());
|
||||
let vault_manager = if matches!(self.command, Commands::Clean(..) | Commands::Gen(..)) {
|
||||
Vaults {
|
||||
passwords_file,
|
||||
vaults_file,
|
||||
..Default::default()
|
||||
}
|
||||
} else {
|
||||
|
@ -88,7 +88,7 @@ impl Cli {
|
|||
|
||||
let master_password = sha256::digest(master_password);
|
||||
Vaults::try_reload(
|
||||
passwords_file,
|
||||
vaults_file,
|
||||
master_password.into_bytes().into_iter().take(32).collect(),
|
||||
)?
|
||||
};
|
||||
|
|
|
@ -18,7 +18,10 @@ use std::num::NonZeroU64;
|
|||
|
||||
use clap::Args;
|
||||
|
||||
use crate::{password::Vaults, LprsError, LprsResult, RunCommand};
|
||||
use crate::{
|
||||
vault::{vault_state::*, Vaults},
|
||||
LprsError, LprsResult, RunCommand,
|
||||
};
|
||||
|
||||
#[derive(Debug, Args)]
|
||||
#[command(author, version, about, long_about = None)]
|
||||
|
@ -32,16 +35,16 @@ pub struct Remove {
|
|||
}
|
||||
|
||||
impl RunCommand for Remove {
|
||||
fn run(&self, mut password_manager: Vaults) -> LprsResult<()> {
|
||||
fn run(&self, mut password_manager: Vaults<Plain>) -> LprsResult<()> {
|
||||
let index = (self.index.get() - 1) as usize;
|
||||
if index > password_manager.passwords.len() {
|
||||
if index > password_manager.vaults.len() {
|
||||
if !self.force {
|
||||
return Err(LprsError::Other(
|
||||
"The index is greater than the passwords counts".to_owned(),
|
||||
));
|
||||
}
|
||||
} else {
|
||||
password_manager.passwords.remove(index);
|
||||
password_manager.vaults.remove(index);
|
||||
password_manager.try_export()?;
|
||||
}
|
||||
Ok(())
|
||||
|
|
|
@ -34,8 +34,8 @@ pub enum Error {
|
|||
WeakPassword(String),
|
||||
#[error("Args Conflict Error: {0}")]
|
||||
ArgsConflict(String),
|
||||
#[error("Invalid Password Index Error: {0}")]
|
||||
InvalidPasswordIndex(String),
|
||||
#[error("Invalid Vault Index Error: {0}")]
|
||||
InvalidVaultIndex(String),
|
||||
#[error("{0}")]
|
||||
Other(String),
|
||||
|
||||
|
@ -56,6 +56,7 @@ pub enum Error {
|
|||
impl Error {
|
||||
/// Return the exit code of the error
|
||||
pub fn exit_code(&self) -> ExitCode {
|
||||
// TODO: Exit with more specific exit code
|
||||
ExitCode::FAILURE
|
||||
}
|
||||
}
|
||||
|
|
|
@ -63,7 +63,7 @@ macro_rules! create_commands {
|
|||
|
||||
#[automatically_derived]
|
||||
impl $crate::RunCommand for $enum_name{
|
||||
fn run(&self, password_manager: $crate::password::Vaults) -> $crate::LprsResult<()> {
|
||||
fn run(&self, password_manager: $crate::vault::Vaults<$crate::vault::vault_state::Plain>) -> $crate::LprsResult<()> {
|
||||
match self {
|
||||
$(
|
||||
Self::$varint(command) => command.run(password_manager),
|
||||
|
|
|
@ -24,8 +24,8 @@ use clap::Parser;
|
|||
|
||||
pub mod cli;
|
||||
pub mod errors;
|
||||
pub mod password;
|
||||
pub mod utils;
|
||||
pub mod vault;
|
||||
|
||||
mod macros;
|
||||
mod traits;
|
||||
|
@ -34,7 +34,7 @@ pub use errors::{Error as LprsError, Result as LprsResult};
|
|||
pub use traits::*;
|
||||
|
||||
pub const STANDARDBASE: GeneralPurpose = GeneralPurpose::new(&alphabet::STANDARD, PAD);
|
||||
pub const DEFAULT_PASSWORD_FILE: &str = "passwords.json";
|
||||
pub const DEFAULT_VAULTS_FILE: &str = "vaults.json";
|
||||
|
||||
#[cfg(feature = "update-notify")]
|
||||
pub const VERSION: &str = env!("CARGO_PKG_VERSION");
|
||||
|
|
|
@ -1,157 +0,0 @@
|
|||
// Lprs - A local CLI password 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::{fs, path::PathBuf};
|
||||
|
||||
use clap::{Parser, ValueEnum};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::{LprsError, LprsResult};
|
||||
|
||||
pub mod cipher;
|
||||
|
||||
mod bitwarden;
|
||||
mod validator;
|
||||
|
||||
pub use bitwarden::*;
|
||||
pub use validator::*;
|
||||
|
||||
#[derive(Clone, Debug, ValueEnum)]
|
||||
pub enum Format {
|
||||
Lprs,
|
||||
BitWarden,
|
||||
}
|
||||
|
||||
/// The password struct
|
||||
#[serde_with_macros::skip_serializing_none]
|
||||
#[derive(Clone, Debug, Deserialize, Serialize, Parser)]
|
||||
pub struct Vault {
|
||||
/// The name of the password
|
||||
#[arg(short, long)]
|
||||
pub name: String,
|
||||
/// The username
|
||||
#[arg(short, long)]
|
||||
pub username: String,
|
||||
/// The password
|
||||
#[arg(short, long)]
|
||||
pub password: String,
|
||||
/// The service name. e.g the website url
|
||||
#[arg(short, long)]
|
||||
pub service: Option<String>,
|
||||
/// The note of the password
|
||||
#[arg(short = 'o', long)]
|
||||
pub note: Option<String>,
|
||||
}
|
||||
|
||||
/// The passwords manager
|
||||
#[derive(Default)]
|
||||
pub struct Vaults {
|
||||
/// Hash of the master password
|
||||
pub master_password: Vec<u8>,
|
||||
/// The json passwords file
|
||||
pub passwords_file: PathBuf,
|
||||
/// The passwords
|
||||
pub passwords: Vec<Vault>,
|
||||
}
|
||||
|
||||
impl Vault {
|
||||
/// Encrypt the password data
|
||||
pub fn encrypt(self, master_password: &[u8]) -> LprsResult<Self> {
|
||||
Ok(Self {
|
||||
name: cipher::encrypt(master_password, &self.name)?,
|
||||
username: cipher::encrypt(master_password, &self.username)?,
|
||||
password: cipher::encrypt(master_password, &self.password)?,
|
||||
service: self
|
||||
.service
|
||||
.map(|url| cipher::encrypt(master_password, &url))
|
||||
.transpose()?,
|
||||
note: self
|
||||
.note
|
||||
.map(|note| cipher::encrypt(master_password, ¬e))
|
||||
.transpose()?,
|
||||
})
|
||||
}
|
||||
|
||||
/// Decrypt the password data
|
||||
pub fn decrypt(self, master_password: &[u8]) -> LprsResult<Self> {
|
||||
Ok(Self {
|
||||
name: cipher::decrypt(master_password, &self.name)?,
|
||||
username: cipher::decrypt(master_password, &self.username)?,
|
||||
password: cipher::decrypt(master_password, &self.password)?,
|
||||
service: self
|
||||
.service
|
||||
.map(|url| cipher::decrypt(master_password, &url))
|
||||
.transpose()?,
|
||||
note: self
|
||||
.note
|
||||
.map(|note| cipher::decrypt(master_password, ¬e))
|
||||
.transpose()?,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl Vaults {
|
||||
/// Create new Passwords instnce
|
||||
pub fn new(master_password: Vec<u8>, passwords_file: PathBuf, passwords: Vec<Vault>) -> Self {
|
||||
Self {
|
||||
master_password,
|
||||
passwords_file,
|
||||
passwords,
|
||||
}
|
||||
}
|
||||
|
||||
/// Encrypt the passwords
|
||||
pub fn encrypt(self) -> LprsResult<Self> {
|
||||
Ok(Self {
|
||||
passwords: self
|
||||
.passwords
|
||||
.into_iter()
|
||||
.map(|p| p.encrypt(&self.master_password))
|
||||
.collect::<LprsResult<Vec<Vault>>>()?,
|
||||
..self
|
||||
})
|
||||
}
|
||||
|
||||
/// Reload the passwords from the file
|
||||
pub fn try_reload(passwords_file: PathBuf, master_password: Vec<u8>) -> LprsResult<Self> {
|
||||
let passwords = serde_json::from_str::<Vec<Vault>>(&fs::read_to_string(&passwords_file)?)?
|
||||
.into_iter()
|
||||
.map(|p| p.decrypt(master_password.as_slice()))
|
||||
.collect::<LprsResult<Vec<Vault>>>()?;
|
||||
|
||||
Ok(Self::new(master_password, passwords_file, passwords))
|
||||
}
|
||||
|
||||
/// Export the passwords to the file
|
||||
pub fn try_export(self) -> LprsResult<()> {
|
||||
let path = self.passwords_file.to_path_buf();
|
||||
fs::write(path, serde_json::to_string(&self.encrypt()?.passwords)?).map_err(LprsError::Io)
|
||||
}
|
||||
|
||||
/// Add new password
|
||||
pub fn add_password(&mut self, password: Vault) {
|
||||
self.passwords.push(password)
|
||||
}
|
||||
}
|
||||
|
||||
impl ToString for Format {
|
||||
fn to_string(&self) -> String {
|
||||
self.to_possible_value()
|
||||
.expect("There is no skiped values")
|
||||
.get_name()
|
||||
.to_owned()
|
||||
}
|
||||
}
|
|
@ -14,9 +14,12 @@
|
|||
// 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 crate::{password::Vaults, LprsResult};
|
||||
use crate::{
|
||||
vault::{vault_state::*, Vaults},
|
||||
LprsResult,
|
||||
};
|
||||
|
||||
/// Trait to run the command
|
||||
pub trait RunCommand {
|
||||
fn run(&self, password_manager: Vaults) -> LprsResult<()>;
|
||||
fn run(&self, password_manager: Vaults<Plain>) -> LprsResult<()>;
|
||||
}
|
||||
|
|
12
src/utils.rs
12
src/utils.rs
|
@ -31,13 +31,13 @@ pub fn local_project_file(filename: &str) -> LprsResult<PathBuf> {
|
|||
Ok(local_dir.join(filename))
|
||||
}
|
||||
|
||||
/// Returns the default passwords json file
|
||||
pub fn passwords_file() -> LprsResult<PathBuf> {
|
||||
let password_file = local_project_file(crate::DEFAULT_PASSWORD_FILE)?;
|
||||
if !password_file.exists() {
|
||||
fs::write(&password_file, "[]")?;
|
||||
/// Returns the default vaults json file
|
||||
pub fn vaults_file() -> LprsResult<PathBuf> {
|
||||
let vaults_file = local_project_file(crate::DEFAULT_VAULTS_FILE)?;
|
||||
if !vaults_file.exists() {
|
||||
fs::write(&vaults_file, "[]")?;
|
||||
}
|
||||
Ok(password_file)
|
||||
Ok(vaults_file)
|
||||
}
|
||||
|
||||
/// Retuns the current lprs version from `crates.io`
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use super::{Vault, Vaults};
|
||||
use super::{vault_state::*, Vault, Vaults};
|
||||
|
||||
#[derive(Clone, Debug, Deserialize, Serialize)]
|
||||
pub struct BitWardenLoginData {
|
||||
|
@ -39,28 +39,28 @@ pub struct BitWardenPasswords {
|
|||
pub items: Vec<BitWardenPassword>,
|
||||
}
|
||||
|
||||
impl From<BitWardenPassword> for Vault {
|
||||
impl From<BitWardenPassword> for Vault<Plain> {
|
||||
fn from(value: BitWardenPassword) -> Self {
|
||||
Self {
|
||||
name: value.name,
|
||||
username: value
|
||||
Self::new(
|
||||
value.name,
|
||||
value
|
||||
.login
|
||||
.as_ref()
|
||||
.map_or_else(String::new, |l| l.username.to_owned().unwrap_or_default()),
|
||||
password: value
|
||||
value
|
||||
.login
|
||||
.as_ref()
|
||||
.map_or_else(String::new, |l| l.password.to_owned().unwrap_or_default()),
|
||||
service: value
|
||||
value
|
||||
.login
|
||||
.and_then(|l| l.uris.and_then(|p| p.first().map(|u| u.uri.clone()))),
|
||||
note: value.notes,
|
||||
}
|
||||
value.notes,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Vault> for BitWardenPassword {
|
||||
fn from(value: Vault) -> Self {
|
||||
impl From<Vault<Plain>> for BitWardenPassword {
|
||||
fn from(value: Vault<Plain>) -> Self {
|
||||
Self {
|
||||
ty: 1,
|
||||
name: value.name,
|
||||
|
@ -76,13 +76,13 @@ impl From<Vault> for BitWardenPassword {
|
|||
}
|
||||
}
|
||||
|
||||
impl From<Vaults> for BitWardenPasswords {
|
||||
fn from(value: Vaults) -> Self {
|
||||
impl From<Vaults<Plain>> for BitWardenPasswords {
|
||||
fn from(value: Vaults<Plain>) -> Self {
|
||||
Self {
|
||||
encrypted: false,
|
||||
folders: Vec::new(),
|
||||
items: value
|
||||
.passwords
|
||||
.vaults
|
||||
.into_iter()
|
||||
.map(BitWardenPassword::from)
|
||||
.collect(),
|
|
@ -18,15 +18,15 @@ use std::{fs, path::Path};
|
|||
|
||||
use crate::LprsResult;
|
||||
|
||||
use super::Vault;
|
||||
use super::{vault_state::*, Vault};
|
||||
|
||||
/// Return if the password file new file or not
|
||||
pub fn is_new_password_file(path: &Path) -> LprsResult<bool> {
|
||||
/// Return if the vaults file new file or not
|
||||
pub fn is_new_vaults_file(path: &Path) -> LprsResult<bool> {
|
||||
if path.exists() {
|
||||
let file_content = fs::read_to_string(path)?;
|
||||
if !file_content.is_empty()
|
||||
&& file_content.trim() != "[]"
|
||||
&& serde_json::from_str::<Vec<Vault>>(&file_content).is_ok()
|
||||
&& serde_json::from_str::<Vec<Vault<Encrypted>>>(&file_content).is_ok()
|
||||
{
|
||||
return Ok(false);
|
||||
}
|
Loading…
Reference in a new issue