use std::{env, fmt, error};
#[cfg(target_os = "windows")]
use winreg::{ enums::*, RegKey };
#[cfg(target_family = "unix")]
use std::{ fs, io::prelude::*, path::PathBuf, fs::OpenOptions };
#[derive(Debug, PartialEq, Eq, Clone)]
pub enum EnvError {
UnsupportedShell,
IOError,
VarError
}
impl error::Error for EnvError {}
impl fmt::Display for EnvError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str("ENV operation error : ")?;
f.write_str(match self {
EnvError::UnsupportedShell => "Unsupported shell",
EnvError::IOError => "I/O error",
EnvError::VarError => "error while getting or setting env",
})
}
}
impl From<std::io::Error> for EnvError {
fn from(_e: std::io::Error) -> EnvError {
EnvError::IOError
}
}
impl From<std::env::VarError> for EnvError {
fn from(_e: std::env::VarError) -> EnvError {
EnvError::VarError
}
}
#[cfg(target_os = "windows")]
pub fn set_var(var: &str, value: &str) -> Result<(), EnvError> {
let hkcu = RegKey::predef(HKEY_CURRENT_USER);
let key = hkcu.open_subkey_with_flags("Environment", KEY_SET_VALUE)?;
key.set_value(var, &value)?;
env::set_var(var, value);
Ok(())
}
#[cfg(target_family = "unix")]
pub fn set_var(var: &str, value: &str) -> Result<(), EnvError> {
let homedir = env::var("HOME")?;
let shell = env::var("SHELL")?;
let envfile = match shell.as_str() {
"/usr/bin/zsh" => ".zshenv",
"/bin/zsh" => ".zshenv",
"/bin/bash" => ".bashrc",
_ => return Err(EnvError::UnsupportedShell)
};
let mut envfilepath = PathBuf::from(homedir);
envfilepath.push(envfile);
let env = fs::read_to_string(&envfilepath)?;
let mut export = String::from("export ");
export.push_str(var);
export.push_str("=");
export.push_str(value);
export.push_str("\n");
if env.contains(&export) { env::set_var(var, value); return Ok(()); }
let mut env_file = OpenOptions::new()
.append(true)
.create(true)
.open(envfilepath)?;
env_file.write(export.as_bytes())?;
env::set_var(var, value);
Ok(())
}
#[cfg(target_os = "windows")]
pub fn unset_var(var: &str) -> Result<(), EnvError> {
let hkcu = RegKey::predef(HKEY_CURRENT_USER);
let key = hkcu.open_subkey_with_flags("Environment", KEY_SET_VALUE)?;
key.delete_value(var)?;
env::remove_var(var);
Ok(())
}
#[cfg(target_family = "unix")]
pub fn unset_var(var: &str) -> Result<(), EnvError> {
let homedir = env::var("HOME")?;
let shell = env::var("SHELL")?;
let envfile = match shell.as_str() {
"/usr/bin/zsh" => ".zshenv",
"/bin/zsh" => ".zshenv",
"/bin/bash" => ".bashrc",
_ => return Err(EnvError::UnsupportedShell)
};
let mut envfilepath = PathBuf::from(homedir);
envfilepath.push(envfile);
let env = fs::read_to_string(&envfilepath)?;
let mut export = String::from("export ");
export.push_str(var);
export.push_str("=");
if !env.contains(&export) { env::remove_var(var); return Ok(()); }
let mut updated_env = String::new();
for l in env.lines() { if !l.contains(var) { updated_env.push_str(l); updated_env.push_str("\n") } }
fs::write(envfilepath, updated_env)?;
env::remove_var(var);
Ok(())
}
#[cfg(target_os = "windows")]
#[cfg(test)]
mod tests {
use winreg::enums::*;
use winreg::RegKey;
use std::env;
#[test]
fn is_set_globally() {
crate::set_var("ENVTEST", "TESTVALUE").unwrap();
let hkcu = RegKey::predef(HKEY_CURRENT_USER);
let key = hkcu
.open_subkey_with_flags("Environment", KEY_READ)
.unwrap();
let var: String = key.get_value("ENVTEST").unwrap();
assert_eq!(String::from("TESTVALUE"), var);
}
#[test]
fn is_set_locally() {
assert_eq!(String::from("TESTVALUE"), env::var("ENVTEST").unwrap());
}
#[test]
#[should_panic]
fn is_unset_globally() {
crate::unset_var("ENVTEST").unwrap();
let hkcu = RegKey::predef(HKEY_CURRENT_USER);
let key = hkcu
.open_subkey_with_flags("Environment", KEY_READ)
.unwrap();
let _: String = key.get_value("ENVTEST").unwrap();
}
#[test]
#[should_panic]
fn is_unset_locally() {
env::var("ENVTEST").unwrap();
}
}
#[cfg(target_family = "unix")]
mod tests {
#[test]
fn is_set_globally() {
crate::set_var("ENVTEST", "TESTVALUE").unwrap();
let homedir = crate::env::var("HOME").unwrap();
let shell = crate::env::var("SHELL").unwrap();
let envfile = match shell.as_str() {
"/usr/bin/zsh" => ".zshenv",
"/bin/zsh" => ".zshenv",
"/bin/bash" => ".bashrc",
_ => panic!("Unsupported shell")
};
let mut envfilepath = crate::PathBuf::from(homedir);
envfilepath.push(envfile);
let env = crate::fs::read_to_string(&envfilepath).unwrap();
assert_eq!(env.contains("export ENVTEST=TESTVALUE\n"), true);
}
#[test]
fn is_set_locally() {
assert_eq!(String::from("TESTVALUE"), crate::env::var("ENVTEST").unwrap());
}
#[test]
fn is_unset_globally() {
crate::unset_var("ENVTEST").unwrap();
let homedir = crate::env::var("HOME").unwrap();
let shell = crate::env::var("SHELL").unwrap();
let envfile = match shell.as_str() {
"/usr/bin/zsh" => ".zshenv",
"/bin/zsh" => ".zshenv",
"/bin/bash" => ".bashrc",
_ => panic!("Unsupported shell")
};
let mut envfilepath = crate::PathBuf::from(homedir);
envfilepath.push(envfile);
let env = crate::fs::read_to_string(&envfilepath).unwrap();
assert_eq!(env.contains("export ENVTEST=TESTVALUE\n"), false);
}
#[test]
#[should_panic]
fn is_unset_locally() {
crate::env::var("ENVTEST").unwrap();
}
}