use super::os::Os;
use lazy_static::lazy_static;
use std::collections::HashMap;
lazy_static! {
static ref ENV_VAR_MAPPINGS: HashMap<&'static str, &'static str> = {
let mut m = HashMap::new();
m.insert("USERPROFILE", "HOME");
m.insert("USERNAME", "USER");
m.insert("APPDATA", "XDG_CONFIG_HOME");
m.insert("LOCALAPPDATA", "XDG_DATA_HOME");
m.insert("TEMP", "TMPDIR");
m.insert("TMP", "TMPDIR");
m.insert("COMPUTERNAME", "HOSTNAME");
m.insert("CD", "PWD");
m.insert("COMSPEC", "SHELL");
m.insert("HOMEDRIVE", "HOME");
m.insert("HOMEPATH", "HOME");
m.insert("SYSTEMROOT", "/");
m.insert("WINDIR", "/");
m.insert("PROGRAMFILES", "/usr/local");
m.insert("COMMONPROGRAMFILES", "/usr/local");
m.insert("PROGRAMDATA", "/var");
m.insert("ALLUSERSPROFILE", "/var");
m
};
static ref ENV_VAR_MAPPINGS_REVERSE: HashMap<&'static str, &'static str> = {
let mut m = HashMap::new();
m.insert("HOME", "USERPROFILE");
m.insert("USER", "USERNAME");
m.insert("XDG_CONFIG_HOME", "APPDATA");
m.insert("XDG_DATA_HOME", "LOCALAPPDATA");
m.insert("XDG_CACHE_HOME", "LOCALAPPDATA");
m.insert("TMPDIR", "TEMP");
m.insert("HOSTNAME", "COMPUTERNAME");
m.insert("PWD", "CD");
m.insert("SHELL", "COMSPEC");
m
};
}
pub fn translate_env_vars(input: &str, from_os: Os, to_os: Os) -> String {
if from_os == to_os {
return input.to_string();
}
if from_os == Os::Windows && to_os.is_unix_like() {
translate_windows_to_unix_env(input)
} else if from_os.is_unix_like() && to_os == Os::Windows {
translate_unix_to_windows_env(input)
} else if from_os.is_unix_like() && to_os.is_unix_like() {
input.to_string()
} else {
input.to_string()
}
}
fn translate_windows_to_unix_env(input: &str) -> String {
let mut result = String::with_capacity(input.len());
let chars: Vec<char> = input.chars().collect();
let mut i = 0;
while i < chars.len() {
if chars[i] == '%' {
if let Some(end) = chars[i + 1..].iter().position(|&c| c == '%') {
let end = end + i + 1;
let var_name: String = chars[i + 1..end].iter().collect();
let mapped_name = ENV_VAR_MAPPINGS
.get(var_name.to_uppercase().as_str())
.copied()
.unwrap_or(&var_name);
result.push('$');
result.push_str(mapped_name);
i = end + 1;
continue;
}
}
result.push(chars[i]);
i += 1;
}
result
}
fn translate_unix_to_windows_env(input: &str) -> String {
let mut result = String::with_capacity(input.len());
let chars: Vec<char> = input.chars().collect();
let mut i = 0;
while i < chars.len() {
if chars[i] == '$' && i + 1 < chars.len() {
if chars[i + 1] == '{' {
if let Some(end) = chars[i + 2..].iter().position(|&c| c == '}') {
let end = end + i + 2;
let var_name: String = chars[i + 2..end].iter().collect();
let mapped_name = ENV_VAR_MAPPINGS_REVERSE
.get(var_name.to_uppercase().as_str())
.copied()
.unwrap_or(&var_name);
result.push('%');
result.push_str(mapped_name);
result.push('%');
i = end + 1;
continue;
}
}
else if chars[i + 1].is_alphanumeric() || chars[i + 1] == '_' {
let start = i + 1;
let mut end = start;
while end < chars.len() && (chars[end].is_alphanumeric() || chars[end] == '_') {
end += 1;
}
let var_name: String = chars[start..end].iter().collect();
let mapped_name = ENV_VAR_MAPPINGS_REVERSE
.get(var_name.to_uppercase().as_str())
.copied()
.unwrap_or(&var_name);
result.push('%');
result.push_str(mapped_name);
result.push('%');
i = end;
continue;
}
}
result.push(chars[i]);
i += 1;
}
result
}
#[inline]
pub fn translate_with_env(input: &str, from_os: Os, to_os: Os) -> String {
translate_env_vars(input, from_os, to_os)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_windows_to_unix_single_var() {
let result = translate_env_vars("echo %PATH%", Os::Windows, Os::Linux);
assert_eq!(result, "echo $PATH");
}
#[test]
fn test_windows_to_unix_multiple_vars() {
let result = translate_env_vars("%USERPROFILE%\\%USERNAME%", Os::Windows, Os::Linux);
assert_eq!(result, "$HOME\\$USER");
}
#[test]
fn test_windows_to_unix_with_mapping() {
let result = translate_env_vars("cd %USERPROFILE%", Os::Windows, Os::Linux);
assert_eq!(result, "cd $HOME");
}
#[test]
fn test_unix_to_windows_dollar_format() {
let result = translate_env_vars("echo $PATH", Os::Linux, Os::Windows);
assert_eq!(result, "echo %PATH%");
}
#[test]
fn test_unix_to_windows_braces_format() {
let result = translate_env_vars("echo ${PATH}", Os::Linux, Os::Windows);
assert_eq!(result, "echo %PATH%");
}
#[test]
fn test_unix_to_windows_with_mapping() {
let result = translate_env_vars("cd $HOME", Os::Linux, Os::Windows);
assert_eq!(result, "cd %USERPROFILE%");
}
#[test]
fn test_same_os_no_change() {
let result = translate_env_vars("echo %PATH%", Os::Windows, Os::Windows);
assert_eq!(result, "echo %PATH%");
}
#[test]
fn test_unix_to_unix_no_change() {
let result = translate_env_vars("echo $PATH", Os::Linux, Os::MacOS);
assert_eq!(result, "echo $PATH");
}
#[test]
fn test_no_env_vars() {
let result = translate_env_vars("echo hello world", Os::Windows, Os::Linux);
assert_eq!(result, "echo hello world");
}
#[test]
fn test_mixed_content() {
let result = translate_env_vars("cd %TEMP% && dir", Os::Windows, Os::Linux);
assert_eq!(result, "cd $TMPDIR && dir");
}
#[test]
fn test_temp_variable_mapping() {
let result = translate_env_vars("%TEMP%", Os::Windows, Os::Linux);
assert_eq!(result, "$TMPDIR");
}
#[test]
fn test_tmpdir_to_temp() {
let result = translate_env_vars("$TMPDIR", Os::Linux, Os::Windows);
assert_eq!(result, "%TEMP%");
}
}