use std::collections::HashMap;
use std::{io::Write, path::PathBuf};
use chrono::NaiveDate;
use serde::{Deserialize, Serialize};
use crate::utils::{get_configdir, truncate_identity_bytes};
use crate::crypto::EncryptionType;
use crate::error::{Error, Result};
#[derive(Serialize, Deserialize, Clone)]
pub struct Env {
pub name: String,
pub value: String,
pub comment: Option<String>,
pub expiration_date: Option<NaiveDate>,
}
impl Env {
pub fn new(
name: String,
value: String,
comment: Option<String>,
expiration_date: Option<NaiveDate>,
) -> Env {
Env {
name,
value,
comment,
expiration_date,
}
}
pub fn from_key_value(key: String, value: String) -> Env {
Env {
name: key,
value,
comment: None,
expiration_date: None,
}
}
}
#[derive(Serialize, Deserialize, Clone)]
pub struct EnvVec {
envs: Vec<Env>,
}
impl From<Vec<Env>> for EnvVec {
fn from(envs: Vec<Env>) -> Self {
EnvVec { envs }
}
}
impl From<HashMap<String, String>> for EnvVec {
fn from(envs: HashMap<String, String>) -> Self {
let mut env_vec = EnvVec::new();
for (key, value) in envs {
env_vec.push(Env::from_key_value(key, value));
}
env_vec
}
}
impl From<EnvVec> for Vec<Env> {
fn from(val: EnvVec) -> Self {
val.envs
}
}
impl From<EnvVec> for HashMap<String, String> {
fn from(val: EnvVec) -> Self {
let mut envs = HashMap::new();
for e in val.envs {
envs.insert(e.name, e.value);
}
envs
}
}
impl Default for EnvVec {
fn default() -> Self {
Self::new()
}
}
impl EnvVec {
pub fn new() -> EnvVec {
EnvVec { envs: Vec::new() }
}
pub fn push(&mut self, env: Env) {
self.envs.push(env);
}
pub fn remove(&mut self, env: &str) {
self.envs.retain(|e| e.name != env);
}
pub fn iter(&self) -> std::slice::Iter<Env> {
self.envs.iter()
}
pub fn iter_mut(&mut self) -> std::slice::IterMut<Env> {
self.envs.iter_mut()
}
pub fn keys(&self) -> Vec<String> {
self.envs.iter().map(|e| e.name.clone()).collect()
}
pub fn contains_key(&self, key: &str) -> bool {
self.envs.iter().any(|e| e.name == key)
}
pub fn get(&self, key: &str) -> Option<&String> {
for e in self.envs.iter() {
if e.name == key {
return Some(&e.value);
}
}
None
}
pub fn is_empty(&self) -> bool {
self.envs.is_empty()
}
pub fn len(&self) -> usize {
self.envs.len()
}
pub fn retain<F>(&mut self, f: F)
where
F: FnMut(&Env) -> bool,
{
self.envs.retain(f);
}
}
impl IntoIterator for EnvVec {
type Item = Env;
type IntoIter = std::vec::IntoIter<Env>;
fn into_iter(self) -> Self::IntoIter {
self.envs.into_iter()
}
}
impl<'a> IntoIterator for &'a EnvVec {
type Item = &'a Env;
type IntoIter = std::slice::Iter<'a, Env>;
fn into_iter(self) -> Self::IntoIter {
self.envs.iter()
}
}
impl<'a> IntoIterator for &'a mut EnvVec {
type Item = &'a mut Env;
type IntoIter = std::slice::IterMut<'a, Env>;
fn into_iter(self) -> Self::IntoIter {
self.envs.iter_mut()
}
}
#[derive(Serialize, Deserialize)]
pub struct Profile {
pub name: String,
pub envs: EnvVec,
pub profile_file_path: PathBuf,
encryption_type: Box<dyn EncryptionType>,
}
impl Profile {
pub fn new(
name: String,
envs: EnvVec,
profile_file_path: PathBuf,
encryption_type: Box<dyn EncryptionType>,
) -> Profile {
Profile {
name,
envs,
profile_file_path,
encryption_type,
}
}
pub fn from_content(
encrypted_content: &[u8],
encryption_type: Box<dyn EncryptionType>,
) -> Result<Profile> {
let truncated_content = truncate_identity_bytes(encrypted_content);
let content = match encryption_type.decrypt(&truncated_content) {
Ok(c) => c,
Err(e) => {
return Err(e);
}
};
match bincode::deserialize(&content) {
Ok(profile) => Ok(profile),
Err(e) => Err(Error::Deserialization(e.to_string())),
}
}
pub fn does_exist(name: &str) -> bool {
let configdir = get_configdir();
let profile_path = configdir.join("profiles").join(format!("{}.env", name));
if profile_path.exists() {
return true;
}
false
}
pub fn insert_env(&mut self, env: String, env_value: String) {
self.envs.push(Env::from_key_value(env, env_value));
}
pub fn edit_env(&mut self, env: String, new_value: String) -> Result<()> {
if self.envs.iter().any(|e| e.name == env) {
for e in self.envs.iter_mut() {
if e.name == env {
e.value = new_value;
return Ok(());
}
}
}
Err(Error::EnvDoesNotExist(env))
}
pub fn remove_env(&mut self, env: &str) -> Result<()> {
if self.envs.iter().any(|e| e.name == env) {
self.envs.retain(|e| e.name != env);
return Ok(());
}
Err(Error::EnvDoesNotExist(env.to_string()))
}
pub fn get_env(&self, env: &str) -> Option<&String> {
for e in self.envs.iter() {
if e.name == env {
return Some(&e.value);
}
}
None
}
pub fn get_envs_hashmap(&self) -> std::collections::HashMap<String, String> {
let mut envs = std::collections::HashMap::new();
for e in self.envs.iter() {
envs.insert(e.name.clone(), e.value.clone());
}
envs
}
pub fn push_changes(&mut self) -> Result<()> {
let mut file = std::fs::OpenOptions::new()
.write(true)
.append(false)
.create(true)
.open(&self.profile_file_path)?;
file.set_len(0)?;
let serialized_data = match bincode::serialize(&self) {
Ok(data) => data,
Err(e) => {
return Err(Error::Serialization(e.to_string()));
}
};
let encrypted_data = match self.encryption_type.encrypt(&serialized_data) {
Ok(data) => data,
Err(e) => {
return Err(e);
}
};
file.write_all(&encrypted_data)?;
file.flush()?;
file.sync_all()?;
Ok(())
}
}
#[macro_export]
macro_rules! load_profile {
($name:expr $(, $get_key:expr)?) => {
(||->envio::error::Result<envio::Profile> {
use envio::Profile;
use envio::crypto;
use envio::utils;
let encrypted_content = utils::get_profile_content($name)?;
let mut encryption_type;
match crypto::get_encryption_type(&encrypted_content) {
Ok(t) => encryption_type = t,
Err(e) => return Err(e.into()),
}
if encryption_type.as_string() == "age" {
$(
let key = $get_key();
encryption_type.set_key(key);
)?
}
match Profile::from_content(&encrypted_content, encryption_type) {
Ok(profile) => return Ok(profile),
Err(e) => return Err(e.into()),
}
})()
};
}