use crate::files::*;
use crate::generator::Generator;
use anyhow::{Context, Result};
use serde_derive::Serialize;
use std::collections::HashMap;
use std::io::Read;
use std::io::Write;
#[derive(Debug, Serialize)]
pub struct PasswordsFile(HashMap<String, HashMap<String, String>>);
pub struct FileContent {
config_file: fs::File,
pub password_file: fs::File,
}
impl FileContent {
pub fn default() -> Result<Self> {
let paths_list = FilePaths::default().context("Failed to fetch default paths")?;
let config_file = fs::OpenOptions::new()
.read(true)
.write(true)
.open(paths_list.config_path)
.context("Could not open 'config.toml' file for reading and writing")?;
let password_file = fs::OpenOptions::new()
.read(true)
.write(true)
.open(paths_list.password_path)
.context("Could not open 'passwords.toml' file for reading and writing")?;
Ok(Self {
config_file,
password_file,
})
}
pub fn init_config(mut self) -> Result<Self> {
let mut config_content: String = String::new();
self.config_file
.read_to_string(&mut config_content)
.context("Failed to read and write 'config.toml' to string")?;
if config_content.is_empty() {
let config_struct: Generator = Generator::new(10)
.lowercase(true)
.uppercase(true)
.symbols(true)
.numbers(true)
.begin_with_letter(true)
.category("unknown".to_string());
config_content = toml::to_string(&config_struct)
.context("Failed to create string from default config")?;
let config_byte_content = config_content.as_bytes();
self.config_file
.write_all(config_byte_content)
.context("Failed to write default content to 'config.toml'")?;
}
Ok(self)
}
pub fn edit_passwords(
mut self,
category_name: Option<&str>,
password_name: String,
) -> Result<String> {
let mut config_content: String = String::new();
let mut passwords_content: String = String::new();
self.config_file
.read_to_string(&mut config_content)
.context("Failed to read and write 'config.toml' to string")?;
self.password_file
.read_to_string(&mut passwords_content)
.context("Failed to read and write 'passwords.toml' to string")?;
let generator: Generator = toml::from_str(&config_content).with_context(|| {
format!("Failed to generate config from string: {}", &config_content)
})?;
let password = generator
.generate()
.context("Failed to generate password")?;
if passwords_content.is_empty() {
let mut passwords_map: HashMap<String, String> = HashMap::new();
passwords_map.insert(password_name, password);
let mut categories_map: HashMap<String, HashMap<String, String>> = HashMap::new();
if let Some(category_value) = category_name {
categories_map.insert(category_value.to_string(), passwords_map);
} else {
categories_map.insert(generator.category, passwords_map);
}
let file_content = PasswordsFile(categories_map);
Ok(toml::to_string_pretty(&file_content)
.with_context(|| format!("Failed to create string from: {:?}", &file_content))?)
} else {
let mut file_content: HashMap<String, HashMap<String, String>> =
toml::from_str(&passwords_content)
.context("Failed to convert 'passwords.toml' as string to hashmap")?;
if let Some(category_value) = category_name {
if let Some(category_map) = file_content.get_mut(category_value) {
category_map.insert(password_name, password);
} else {
let mut password_map: HashMap<String, String> = HashMap::new();
password_map.insert(password_name, password);
file_content.insert(category_value.to_string(), password_map);
}
} else if let Some(category_map) = file_content.get_mut(&generator.category) {
category_map.insert(password_name, password);
} else {
let mut password_map: HashMap<String, String> = HashMap::new();
password_map.insert(password_name, password);
file_content.insert(generator.category, password_map);
}
println!("Password successfully generated!");
Ok(toml::to_string_pretty(&file_content)
.with_context(|| format!("Failed to create string from: {:?}", &file_content))?)
}
}
pub fn save_passwords(self, category_name: Option<&str>, password_name: String) -> Result<()> {
let password_filepath = {
FilePaths::default()
.context("Failed to fetch default paths")?
.password_path
};
let passwords_file_content = self
.edit_passwords(category_name, password_name)
.context("Failed to create new 'passwors.toml' content")?;
let mut passwords_file = fs::OpenOptions::new()
.write(true)
.open(password_filepath)
.context("Failed to open 'passwords.toml' for writing")?;
passwords_file.write_all(passwords_file_content.as_bytes())
.context("Failed to write to 'passwords.toml'")?;
Ok(())
}
}