1use std::{env, fmt, error};
11#[cfg(target_os = "windows")]
12use winreg::{ enums::*, RegKey };
13
14#[cfg(target_family = "unix")]
15use std::{ fs, io::prelude::*, path::PathBuf, fs::OpenOptions };
16
17#[derive(Debug, PartialEq, Eq, Clone)]
18pub enum EnvError {
19 UnsupportedShell,
21 IOError,
23 VarError
25}
26
27impl error::Error for EnvError {}
28
29impl fmt::Display for EnvError {
30 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
31 f.write_str("ENV operation error : ")?;
32 f.write_str(match self {
33 EnvError::UnsupportedShell => "Unsupported shell",
34 EnvError::IOError => "I/O error",
35 EnvError::VarError => "error while getting or setting env",
36 })
37 }
38}
39
40impl From<std::io::Error> for EnvError {
41 fn from(_e: std::io::Error) -> EnvError {
42 EnvError::IOError
43 }
44}
45
46impl From<std::env::VarError> for EnvError {
47 fn from(_e: std::env::VarError) -> EnvError {
48 EnvError::VarError
49 }
50}
51
52#[cfg(target_os = "windows")]
53pub fn set_var(var: &str, value: &str) -> Result<(), EnvError> {
55 let hkcu = RegKey::predef(HKEY_CURRENT_USER);
56 let key = hkcu.open_subkey_with_flags("Environment", KEY_SET_VALUE)?;
57 key.set_value(var, &value)?;
59 env::set_var(var, value);
61 Ok(())
62}
63
64#[cfg(target_family = "unix")]
65pub fn set_var(var: &str, value: &str) -> Result<(), EnvError> {
67 let homedir = env::var("HOME")?;
69 let shell = env::var("SHELL")?;
70 let envfile = match shell.as_str() {
71 "/usr/bin/zsh" => ".zshenv",
72 "/bin/zsh" => ".zshenv",
73 "/bin/bash" => ".bashrc",
74 _ => return Err(EnvError::UnsupportedShell)
75 };
76
77 let mut envfilepath = PathBuf::from(homedir);
78 envfilepath.push(envfile);
79
80 let env = fs::read_to_string(&envfilepath)?;
82
83 let mut export = String::from("export ");
85 export.push_str(var);
86 export.push_str("=");
87 export.push_str(value);
88 export.push_str("\n");
89
90 if env.contains(&export) { env::set_var(var, value); return Ok(()); }
92
93 let mut env_file = OpenOptions::new()
95 .append(true)
96 .create(true)
97 .open(envfilepath)?;
98 env_file.write(export.as_bytes())?;
99
100 env::set_var(var, value);
102
103 Ok(())
104}
105
106#[cfg(target_os = "windows")]
107pub fn unset_var(var: &str) -> Result<(), EnvError> {
109 let hkcu = RegKey::predef(HKEY_CURRENT_USER);
110 let key = hkcu.open_subkey_with_flags("Environment", KEY_SET_VALUE)?;
111 key.delete_value(var)?;
112 env::remove_var(var);
113 Ok(())
114}
115
116#[cfg(target_family = "unix")]
117pub fn unset_var(var: &str) -> Result<(), EnvError> {
119 let homedir = env::var("HOME")?;
121 let shell = env::var("SHELL")?;
122 let envfile = match shell.as_str() {
123 "/usr/bin/zsh" => ".zshenv",
124 "/bin/zsh" => ".zshenv",
125 "/bin/bash" => ".bashrc",
126 _ => return Err(EnvError::UnsupportedShell)
127 };
128
129 let mut envfilepath = PathBuf::from(homedir);
130 envfilepath.push(envfile);
131
132 let env = fs::read_to_string(&envfilepath)?;
134
135 let mut export = String::from("export ");
137 export.push_str(var);
138 export.push_str("=");
139
140 if !env.contains(&export) { env::remove_var(var); return Ok(()); }
142
143 let mut updated_env = String::new();
145 for l in env.lines() { if !l.contains(var) { updated_env.push_str(l); updated_env.push_str("\n") } }
146 fs::write(envfilepath, updated_env)?;
147
148 env::remove_var(var);
150
151 Ok(())
152}
153
154#[cfg(target_os = "windows")]
159#[cfg(test)]
160mod tests {
161 use winreg::enums::*;
162 use winreg::RegKey;
163 use std::env;
164 #[test]
165 fn is_set_globally() {
166 crate::set_var("ENVTEST", "TESTVALUE").unwrap();
167 let hkcu = RegKey::predef(HKEY_CURRENT_USER);
168 let key = hkcu
169 .open_subkey_with_flags("Environment", KEY_READ)
170 .unwrap();
171 let var: String = key.get_value("ENVTEST").unwrap();
172 assert_eq!(String::from("TESTVALUE"), var);
173 }
174
175 #[test]
176 fn is_set_locally() {
177 assert_eq!(String::from("TESTVALUE"), env::var("ENVTEST").unwrap());
178 }
179
180 #[test]
181 #[should_panic]
182 fn is_unset_globally() {
183 crate::unset_var("ENVTEST").unwrap();
184 let hkcu = RegKey::predef(HKEY_CURRENT_USER);
185 let key = hkcu
186 .open_subkey_with_flags("Environment", KEY_READ)
187 .unwrap();
188 let _: String = key.get_value("ENVTEST").unwrap();
189 }
190
191 #[test]
192 #[should_panic]
193 fn is_unset_locally() {
194 env::var("ENVTEST").unwrap();
195 }
196}
197
198#[cfg(target_family = "unix")]
199mod tests {
200 #[test]
201 fn is_set_globally() {
202 crate::set_var("ENVTEST", "TESTVALUE").unwrap();
203 let homedir = crate::env::var("HOME").unwrap();
205 let shell = crate::env::var("SHELL").unwrap();
206 let envfile = match shell.as_str() {
207 "/usr/bin/zsh" => ".zshenv",
208 "/bin/zsh" => ".zshenv",
209 "/bin/bash" => ".bashrc",
210 _ => panic!("Unsupported shell")
211 };
212
213 let mut envfilepath = crate::PathBuf::from(homedir);
214 envfilepath.push(envfile);
215
216 let env = crate::fs::read_to_string(&envfilepath).unwrap();
218
219 assert_eq!(env.contains("export ENVTEST=TESTVALUE\n"), true);
220 }
221
222 #[test]
223 fn is_set_locally() {
224 assert_eq!(String::from("TESTVALUE"), crate::env::var("ENVTEST").unwrap());
225 }
226
227 #[test]
228 fn is_unset_globally() {
229 crate::unset_var("ENVTEST").unwrap();
230 let homedir = crate::env::var("HOME").unwrap();
232 let shell = crate::env::var("SHELL").unwrap();
233 let envfile = match shell.as_str() {
234 "/usr/bin/zsh" => ".zshenv",
235 "/bin/zsh" => ".zshenv",
236 "/bin/bash" => ".bashrc",
237 _ => panic!("Unsupported shell")
238 };
239
240 let mut envfilepath = crate::PathBuf::from(homedir);
241 envfilepath.push(envfile);
242
243 let env = crate::fs::read_to_string(&envfilepath).unwrap();
245
246 assert_eq!(env.contains("export ENVTEST=TESTVALUE\n"), false);
247 }
248
249 #[test]
250 #[should_panic]
251 fn is_unset_locally() {
252 crate::env::var("ENVTEST").unwrap();
253 }
254}
255
256