envio 0.7.0

A secure command-line tool for managing environment variables
pub mod cipher;
pub mod env;
pub mod error;
pub mod profile;
pub mod utils;

use std::path::Path;
use zeroize::Zeroizing;

pub use env::{Env, EnvMap};
pub use profile::{Profile, ProfileMetadata};

use crate::{
    cipher::{CipherKind, PASSPHRASE, SYMMETRIC},
    error::{Error, Result},
};

pub fn get_profile<P, F>(file_path: P, key_provider: Option<F>) -> Result<Profile>
where
    P: AsRef<Path>,
    F: FnOnce(&ProfileMetadata) -> Zeroizing<String>,
{
    let file_path = file_path.as_ref().to_path_buf();

    let serialized_profile = utils::get_serialized_profile(&file_path)?;
    let mut cipher = crate::cipher::create_cipher(serialized_profile.metadata.cipher_kind, None)?;

    if let Some(cipher_metadata) = &serialized_profile.metadata.cipher_metadata {
        cipher.import_metadata(cipher_metadata.clone())?;
    }

    if matches!(
        cipher.kind(),
        CipherKind::PASSPHRASE | CipherKind::SYMMETRIC
    ) {
        let key_provider = key_provider.ok_or_else(|| {
            Error::Msg("Key provider is required for profiles using encryption".into())
        })?;

        let key = key_provider(&serialized_profile.metadata);

        match cipher.kind() {
            CipherKind::PASSPHRASE => cipher
                .as_any_mut()
                .downcast_mut::<PASSPHRASE>()
                .expect("Failed to cast to PASSPHRASE")
                .set_key(key),
            CipherKind::SYMMETRIC => cipher
                .as_any_mut()
                .downcast_mut::<SYMMETRIC>()
                .expect("Failed to cast to SYMMETRIC")
                .set_key(key),
            _ => {}
        }
    }

    Ok(Profile {
        metadata: serialized_profile.metadata,
        file_path,
        envs: cipher.decrypt(&serialized_profile.content)?,
        cipher,
    })
}

pub fn load_profile<P, F>(file_path: P, key_provider: Option<F>) -> Result<Profile>
where
    P: AsRef<Path>,
    F: FnOnce(&ProfileMetadata) -> Zeroizing<String>,
{
    let file_path = file_path.as_ref().to_path_buf();
    let profile = get_profile(file_path, key_provider)?;

    for env in &profile.envs {
        unsafe { std::env::set_var(&env.key, &env.value) };
    }

    Ok(profile)
}