Skip to content

Commit faf4bf0

Browse files
Fix Windows environment (#242)
* feat: ✨ Add env mod and rework Windows environment setup * style: 🎨 Format includes * build: ⬆️ Bump version * fix: 🐛 Fix unix includes * docs: 📝 Add file docstring
1 parent 21d0bc7 commit faf4bf0

File tree

6 files changed

+207
-177
lines changed

6 files changed

+207
-177
lines changed

Cargo.lock

Lines changed: 13 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "espup"
3-
version = "0.3.3-dev"
3+
version = "0.4.0-dev"
44
authors = ["Sergio Gasquez Arcos <sergio.gasquez@gmail.com>"]
55
edition = "2021"
66
license = "MIT OR Apache-2.0"
@@ -42,6 +42,9 @@ zip = "0.6.4"
4242
[target.'cfg(unix)'.dependencies]
4343
openssl = { version = "0.10.50", features = ["vendored"] }
4444

45+
[target.'cfg(windows)'.dependencies]
46+
winreg = "0.50.0"
47+
4548
[dev-dependencies]
4649
assert_cmd = "2.0.11"
4750
assert_fs = "1.0.13"

src/env.rs

Lines changed: 167 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,167 @@
1+
//! Environment variables set up and export file support.
2+
3+
use crate::{emoji, error::Error};
4+
use directories::BaseDirs;
5+
use log::{info, warn};
6+
#[cfg(windows)]
7+
use std::env;
8+
use std::{
9+
fs::File,
10+
io::Write,
11+
path::{Path, PathBuf},
12+
};
13+
#[cfg(windows)]
14+
use winreg::{
15+
enums::{HKEY_CURRENT_USER, KEY_READ, KEY_WRITE},
16+
RegKey,
17+
};
18+
19+
#[cfg(windows)]
20+
const DEFAULT_EXPORT_FILE: &str = "export-esp.ps1";
21+
#[cfg(not(windows))]
22+
const DEFAULT_EXPORT_FILE: &str = "export-esp.sh";
23+
24+
#[cfg(windows)]
25+
/// Sets an environment variable for the current user.
26+
pub fn set_environment_variable(key: &str, value: &str) -> Result<(), Error> {
27+
env::set_var(key, value);
28+
29+
let hkcu = RegKey::predef(HKEY_CURRENT_USER);
30+
let environment_key = hkcu.open_subkey_with_flags("Environment", KEY_WRITE)?;
31+
environment_key.set_value(key, &value)?;
32+
Ok(())
33+
}
34+
35+
#[cfg(windows)]
36+
/// Deletes an environment variable for the current user.
37+
pub fn delete_environment_variable(key: &str) -> Result<(), Error> {
38+
if env::var_os(key).is_none() {
39+
return Ok(());
40+
}
41+
42+
env::remove_var(key);
43+
44+
let hkcu = RegKey::predef(HKEY_CURRENT_USER);
45+
let environment_key = hkcu.open_subkey_with_flags("Environment", KEY_READ | KEY_WRITE)?;
46+
environment_key.delete_value(key)?;
47+
Ok(())
48+
}
49+
50+
/// Returns the absolute path to the export file, uses the DEFAULT_EXPORT_FILE if no arg is provided.
51+
pub fn get_export_file(export_file: Option<PathBuf>) -> Result<PathBuf, Error> {
52+
if let Some(export_file) = export_file {
53+
if export_file.is_dir() {
54+
return Err(Error::InvalidDestination(export_file.display().to_string()));
55+
}
56+
if export_file.is_absolute() {
57+
Ok(export_file)
58+
} else {
59+
let current_dir = std::env::current_dir()?;
60+
Ok(current_dir.join(export_file))
61+
}
62+
} else {
63+
Ok(BaseDirs::new()
64+
.unwrap()
65+
.home_dir()
66+
.join(DEFAULT_EXPORT_FILE))
67+
}
68+
}
69+
70+
/// Creates the export file with the necessary environment variables.
71+
pub fn create_export_file(export_file: &PathBuf, exports: &[String]) -> Result<(), Error> {
72+
info!("{} Creating export file", emoji::WRENCH);
73+
let mut file = File::create(export_file)?;
74+
for e in exports.iter() {
75+
#[cfg(windows)]
76+
let e = e.replace('/', r#"\"#);
77+
file.write_all(e.as_bytes())?;
78+
file.write_all(b"\n")?;
79+
}
80+
81+
Ok(())
82+
}
83+
84+
/// Instructions to export the environment variables.
85+
pub fn export_environment(export_file: &Path) -> Result<(), Error> {
86+
#[cfg(windows)]
87+
if cfg!(windows) {
88+
set_environment_variable("PATH", &env::var("PATH").unwrap())?;
89+
warn!(
90+
"{} Your environments variables have been updated! Shell may need to be restarted for changes to be effective.",
91+
emoji::INFO
92+
);
93+
warn!(
94+
"{} A file was created at '{}' showing the injected environment variables.",
95+
emoji::INFO,
96+
export_file.display()
97+
);
98+
}
99+
#[cfg(unix)]
100+
if cfg!(unix) {
101+
warn!(
102+
"{} Please, set up the environment variables by running: '. {}'",
103+
emoji::INFO,
104+
export_file.display()
105+
);
106+
warn!(
107+
"{} This step must be done every time you open a new terminal.",
108+
emoji::WARN
109+
);
110+
}
111+
Ok(())
112+
}
113+
114+
#[cfg(test)]
115+
mod tests {
116+
use crate::env::{create_export_file, get_export_file, DEFAULT_EXPORT_FILE};
117+
use directories::BaseDirs;
118+
use std::{env::current_dir, path::PathBuf};
119+
120+
#[test]
121+
#[allow(unused_variables)]
122+
fn test_get_export_file() {
123+
// No arg provided
124+
let home_dir = BaseDirs::new().unwrap().home_dir().to_path_buf();
125+
let export_file = home_dir.join(DEFAULT_EXPORT_FILE);
126+
assert!(matches!(get_export_file(None), Ok(export_file)));
127+
// Relative path
128+
let current_dir = current_dir().unwrap();
129+
let export_file = current_dir.join("export.sh");
130+
assert!(matches!(
131+
get_export_file(Some(PathBuf::from("export.sh"))),
132+
Ok(export_file)
133+
));
134+
// Absolute path
135+
let export_file = PathBuf::from("/home/user/export.sh");
136+
assert!(matches!(
137+
get_export_file(Some(PathBuf::from("/home/user/export.sh"))),
138+
Ok(export_file)
139+
));
140+
// Path is a directory instead of a file
141+
assert!(get_export_file(Some(home_dir)).is_err());
142+
}
143+
144+
#[test]
145+
fn test_create_export_file() {
146+
// Creates the export file and writes the correct content to it
147+
let temp_dir = tempfile::TempDir::new().unwrap();
148+
let export_file = temp_dir.path().join("export.sh");
149+
let exports = vec![
150+
"export VAR1=value1".to_string(),
151+
"export VAR2=value2".to_string(),
152+
];
153+
create_export_file(&export_file, &exports).unwrap();
154+
let contents = std::fs::read_to_string(export_file).unwrap();
155+
assert_eq!(contents, "export VAR1=value1\nexport VAR2=value2\n");
156+
157+
// Returns the correct error when it fails to create the export file (it already exists)
158+
let temp_dir = tempfile::TempDir::new().unwrap();
159+
let export_file = temp_dir.path().join("export.sh");
160+
std::fs::create_dir_all(&export_file).unwrap();
161+
let exports = vec![
162+
"export VAR1=value1".to_string(),
163+
"export VAR2=value2".to_string(),
164+
];
165+
assert!(create_export_file(&export_file, &exports).is_err());
166+
}
167+
}

src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
pub mod emoji;
2+
pub mod env;
23
pub mod error;
34
pub mod host_triple;
45
pub mod targets;

0 commit comments

Comments
 (0)