use crate::error::Result;
use crate::string::{from_wide, to_wide, WideString};
use std::collections::HashMap;
use std::path::PathBuf;
use windows::Win32::System::Environment::{
ExpandEnvironmentStringsW, GetEnvironmentVariableW, SetEnvironmentVariableW,
};
pub fn get(name: &str) -> Option<String> {
let name_wide = WideString::new(name);
let size = unsafe { GetEnvironmentVariableW(name_wide.as_pcwstr(), None) };
if size == 0 {
return None;
}
let mut buffer = vec![0u16; size as usize];
let len = unsafe { GetEnvironmentVariableW(name_wide.as_pcwstr(), Some(&mut buffer)) } as usize;
if len == 0 {
return None;
}
from_wide(&buffer[..len]).ok()
}
pub fn set(name: &str, value: &str) -> Result<()> {
let name_wide = WideString::new(name);
let value_wide = WideString::new(value);
unsafe {
SetEnvironmentVariableW(name_wide.as_pcwstr(), value_wide.as_pcwstr())?;
}
Ok(())
}
pub fn remove(name: &str) -> Result<()> {
let name_wide = WideString::new(name);
unsafe {
SetEnvironmentVariableW(name_wide.as_pcwstr(), None)?;
}
Ok(())
}
pub fn expand(s: &str) -> Result<String> {
let wide = to_wide(s);
let size = unsafe { ExpandEnvironmentStringsW(windows::core::PCWSTR(wide.as_ptr()), None) };
if size == 0 {
return Err(crate::error::last_error());
}
let mut buffer = vec![0u16; size as usize];
let len = unsafe {
ExpandEnvironmentStringsW(windows::core::PCWSTR(wide.as_ptr()), Some(&mut buffer))
} as usize;
if len == 0 {
return Err(crate::error::last_error());
}
from_wide(&buffer[..len.saturating_sub(1)])
}
pub fn vars() -> HashMap<String, String> {
std::env::vars().collect()
}
pub fn path() -> Vec<PathBuf> {
get("PATH")
.unwrap_or_default()
.split(';')
.filter(|s| !s.is_empty())
.map(PathBuf::from)
.collect()
}
pub fn temp_dir() -> Option<PathBuf> {
get("TEMP").or_else(|| get("TMP")).map(PathBuf::from)
}
pub fn home_dir() -> Option<PathBuf> {
get("USERPROFILE").map(PathBuf::from)
}
pub fn username() -> Option<String> {
get("USERNAME")
}
pub fn computer_name() -> Option<String> {
get("COMPUTERNAME")
}
pub fn system_root() -> Option<PathBuf> {
get("SystemRoot").map(PathBuf::from)
}
pub fn windows_dir() -> Option<PathBuf> {
get("windir").map(PathBuf::from)
}
pub fn program_data() -> Option<PathBuf> {
get("ProgramData").map(PathBuf::from)
}
pub fn program_files() -> Option<PathBuf> {
get("ProgramFiles").map(PathBuf::from)
}
pub fn program_files_x86() -> Option<PathBuf> {
get("ProgramFiles(x86)").map(PathBuf::from)
}
pub fn app_data() -> Option<PathBuf> {
get("APPDATA").map(PathBuf::from)
}
pub fn local_app_data() -> Option<PathBuf> {
get("LOCALAPPDATA").map(PathBuf::from)
}
pub fn processor_count() -> Option<u32> {
get("NUMBER_OF_PROCESSORS").and_then(|s| s.parse().ok())
}
pub fn processor_architecture() -> Option<String> {
get("PROCESSOR_ARCHITECTURE")
}
pub fn exists(name: &str) -> bool {
get(name).is_some()
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_get_set_remove() {
let var_name = "ERGONOMIC_WINDOWS_TEST_VAR";
assert!(get(var_name).is_none());
set(var_name, "test_value").unwrap();
assert_eq!(get(var_name), Some("test_value".to_string()));
remove(var_name).unwrap();
assert!(get(var_name).is_none());
}
#[test]
fn test_expand() {
let expanded = expand("%SystemRoot%\\System32").unwrap();
assert!(expanded.contains("System32"));
assert!(!expanded.contains("%"));
}
#[test]
fn test_path() {
let paths = path();
assert!(!paths.is_empty());
}
#[test]
fn test_standard_vars() {
assert!(home_dir().is_some());
assert!(username().is_some());
assert!(computer_name().is_some());
assert!(system_root().is_some());
assert!(temp_dir().is_some());
}
#[test]
fn test_exists() {
assert!(exists("PATH"));
assert!(!exists("NONEXISTENT_VAR_12345"));
}
}