use crate::crypto::decrypt;
use crate::parser::DotenvParser;
use crate::utils::error::{DotenvxError, Result};
use crate::utils::fs::read_file;
use std::collections::HashMap;
use std::path::Path;
pub fn printenv_command(env_files: &[&Path], keys_file: Option<&Path>, format: &str) -> Result<()> {
let mut env_vars = HashMap::new();
for env_file in env_files {
let file_vars = load_env_file(env_file, keys_file)?;
env_vars.extend(file_vars);
}
match format {
"json" => print_json(&env_vars),
"bash" | "sh" => print_bash(&env_vars),
"fish" => print_fish(&env_vars),
"powershell" | "ps1" => print_powershell(&env_vars),
_ => print_bash(&env_vars), }
Ok(())
}
fn load_env_file(env_file: &Path, keys_file: Option<&Path>) -> Result<HashMap<String, String>> {
let content = read_file(env_file)?;
let mut parser = DotenvParser::new();
parser.parse_with_processing(&content)?;
let mut variables = parser.variables().clone();
let private_key = find_private_key(env_file, keys_file);
if let Ok(private_key) = private_key {
for (_key, value) in variables.iter_mut() {
if value.starts_with("encrypted:") {
match decrypt(value, &private_key) {
Ok(decrypted) => {
*value = decrypted;
}
Err(_) => {
}
}
}
}
}
variables.remove("DOTENV_PUBLIC_KEY");
Ok(variables)
}
fn find_private_key(env_file: &Path, keys_file: Option<&Path>) -> Result<String> {
if let Some(keys_path) = keys_file {
if keys_path.exists() {
let content = read_file(keys_path)?;
if let Some(key) = extract_key_from_content(&content, "DOTENV_PRIVATE_KEY") {
return Ok(key);
}
}
}
if let Some(parent) = env_file.parent() {
let default_keys = parent.join(".env.keys");
if default_keys.exists() {
let content = read_file(&default_keys)?;
if let Some(key) = extract_key_from_content(&content, "DOTENV_PRIVATE_KEY") {
return Ok(key);
}
}
}
if let Ok(key) = std::env::var("DOTENV_PRIVATE_KEY") {
return Ok(key);
}
Err(DotenvxError::MissingPrivateKey {
key_name: "DOTENV_PRIVATE_KEY".to_string(),
})
}
fn extract_key_from_content(content: &str, key_name: &str) -> Option<String> {
for line in content.lines() {
let trimmed = line.trim();
if trimmed.starts_with(&format!("{}=", key_name)) {
let value = &trimmed[key_name.len() + 1..];
return Some(parse_value(value));
}
}
None
}
fn parse_value(value: &str) -> String {
let value = value.trim();
if ((value.starts_with('"') && value.ends_with('"'))
|| (value.starts_with('\'') && value.ends_with('\'')))
&& value.len() >= 2
{
return value[1..value.len() - 1].to_string();
}
value.to_string()
}
fn print_bash(env_vars: &HashMap<String, String>) {
for (key, value) in env_vars {
let escaped_value = value.replace('\'', r"'\''");
println!("export {}='{}'", key, escaped_value);
}
}
fn print_fish(env_vars: &HashMap<String, String>) {
for (key, value) in env_vars {
let escaped_value = value.replace('\'', r"\'");
println!("set -gx {} '{}'", key, escaped_value);
}
}
fn print_powershell(env_vars: &HashMap<String, String>) {
for (key, value) in env_vars {
let escaped_value = value.replace('"', r#""`""#);
println!("$env:{}=\"{}\"", key, escaped_value);
}
}
fn print_json(env_vars: &HashMap<String, String>) {
use std::collections::BTreeMap;
let sorted: BTreeMap<_, _> = env_vars.iter().collect();
println!("{{");
let count = sorted.len();
for (i, (key, value)) in sorted.iter().enumerate() {
let json_value = serde_json::to_string(value).unwrap_or_else(|_| "\"\"".to_string());
if i < count - 1 {
println!(" \"{}\": {},", key, json_value);
} else {
println!(" \"{}\": {}", key, json_value);
}
}
println!("}}");
}