use std::collections::HashMap;
use std::path::PathBuf;
use std::process::Command;
use clap::Args;
use serde_yaml as sy;
use crate::cli::ENV_PATH_SEP;
use crate::error::{Result, YageError};
use crate::{decrypt_yaml, load_identities, read_yaml};
#[derive(Args, Debug)]
pub struct EnvArgs {
#[clap(short, long, default_value_t = false)]
pub ignore_environment: bool,
#[clap(short, long = "key", value_name = "KEY", env = "YAGE_KEY", value_delimiter = ',')]
pub keys: Vec<String>,
#[clap(
short = 'K',
long = "key-file",
value_name = "FILE",
env = "YAGE_KEY_FILE",
value_delimiter = ENV_PATH_SEP,
)]
pub key_files: Vec<PathBuf>,
#[arg()]
pub file: PathBuf,
#[arg()]
pub command: String,
#[arg()]
pub args: Vec<String>,
}
pub fn env(args: &EnvArgs) -> Result<i32> {
let identities = load_identities(&args.keys, &args.key_files)?;
let input_data = read_yaml(&args.file)?;
let output_data = decrypt_yaml(&input_data, &identities)?;
let env_data = build_env(&output_data)?;
for (key, value) in &env_data {
debug!("{key}={value}");
}
let mut command = Command::new(&args.command);
if args.ignore_environment {
command.env_clear();
}
command.args(&args.args).envs(&env_data);
let status = command.spawn()?.wait()?;
Ok(status.code().unwrap_or(1))
}
fn build_env(data: &sy::Value) -> Result<HashMap<String, String>> {
let mut env = HashMap::new();
match data {
sy::Value::Mapping(mapping) => {
for (key, value) in mapping {
let key = plain_value_to_string(key)?;
let value = plain_value_to_string(value)?;
env.insert(key, value);
}
}
_ => Err(YageError::NotAMap)?,
}
Ok(env)
}
fn plain_value_to_string(data: &sy::Value) -> Result<String> {
Ok(match data {
sy::Value::String(s) => s.to_owned(),
sy::Value::Number(n) => {
if n.is_f64() {
n.as_f64().unwrap().to_string()
} else if n.is_i64() {
n.as_i64().unwrap().to_string()
} else {
n.as_u64().unwrap().to_string()
}
}
_ => Err(YageError::NotAStringOrNumber)?,
})
}