xet_runtime/utils/
guards.rs1#[cfg(not(target_family = "wasm"))]
2use std::env;
3#[cfg(not(target_family = "wasm"))]
4use std::ffi::OsStr;
5#[cfg(not(target_family = "wasm"))]
6use std::path::{Path, PathBuf};
7
8#[cfg(not(target_family = "wasm"))]
32pub struct EnvVarGuard {
33 key: &'static str,
34 prev: Option<String>,
35}
36
37#[cfg(not(target_family = "wasm"))]
38impl EnvVarGuard {
39 pub fn set(key: &'static str, value: impl AsRef<OsStr>) -> Self {
40 let prev = env::var(key).ok();
41 unsafe {
42 env::set_var(key, value);
43 }
44 Self { key, prev }
45 }
46
47 pub fn unset(key: &'static str) -> Self {
52 let prev = env::var(key).ok();
53 unsafe {
54 env::remove_var(key);
55 }
56 Self { key, prev }
57 }
58}
59
60#[cfg(not(target_family = "wasm"))]
61impl Drop for EnvVarGuard {
62 fn drop(&mut self) {
63 if let Some(v) = &self.prev {
64 unsafe {
65 env::set_var(self.key, v);
66 }
67 } else {
68 unsafe {
69 env::remove_var(self.key);
70 }
71 }
72 }
73}
74
75#[cfg(not(target_family = "wasm"))]
101pub struct CwdGuard {
102 prev: PathBuf,
103}
104
105#[cfg(not(target_family = "wasm"))]
106impl CwdGuard {
107 pub fn set(new_dir: &Path) -> std::io::Result<Self> {
108 let prev = env::current_dir()?;
109 env::set_current_dir(new_dir)?;
110 Ok(Self { prev })
111 }
112}
113
114#[cfg(not(target_family = "wasm"))]
115impl Drop for CwdGuard {
116 fn drop(&mut self) {
117 let _ = env::set_current_dir(&self.prev);
118 }
119}
120
121pub struct ClosureGuard<F: FnOnce()> {
136 closure: Option<F>,
137}
138
139impl<F: FnOnce()> ClosureGuard<F> {
140 pub fn new(f: F) -> Self {
141 Self { closure: Some(f) }
142 }
143}
144
145impl<F: FnOnce()> Drop for ClosureGuard<F> {
146 fn drop(&mut self) {
147 if let Some(f) = self.closure.take() {
148 f();
149 }
150 }
151}
152
153#[cfg(test)]
154mod tests {
155 use serial_test::serial;
156 use tempfile::tempdir;
157
158 use super::*;
159
160 #[test]
161 #[serial(default_config_env)]
162 fn env_var_guard_sets_and_restores() {
163 let key = "TEST_ENV_VAR_GUARD";
164
165 unsafe {
167 env::set_var(key, "initial");
168 }
169
170 {
171 let _guard = EnvVarGuard::set(key, "temporary");
172 assert_eq!(env::var(key).unwrap(), "temporary");
173 }
174
175 assert_eq!(env::var(key).unwrap(), "initial");
177 }
178
179 #[test]
180 #[serial(default_config_env)]
181 fn env_var_guard_restores_none_when_var_did_not_exist() {
182 let key = "TEST_ENV_VAR_GUARD_NEW";
183
184 unsafe {
186 env::remove_var(key);
187 }
188 assert!(env::var(key).is_err());
189
190 {
191 let _guard = EnvVarGuard::set(key, "temporary");
192 assert_eq!(env::var(key).unwrap(), "temporary");
193 }
194
195 assert!(env::var(key).is_err());
197 }
198
199 #[test]
200 #[serial(default_config_env)]
201 fn env_var_guard_multiple_guards_same_key() {
202 let key = "TEST_ENV_VAR_GUARD_MULTI";
203
204 unsafe {
205 env::set_var(key, "initial");
206 }
207
208 {
209 let _guard1 = EnvVarGuard::set(key, "first");
210 assert_eq!(env::var(key).unwrap(), "first");
211
212 {
213 let _guard2 = EnvVarGuard::set(key, "second");
214 assert_eq!(env::var(key).unwrap(), "second");
215 }
216
217 assert_eq!(env::var(key).unwrap(), "first");
219 }
220
221 assert_eq!(env::var(key).unwrap(), "initial");
223 }
224
225 #[test]
226 #[serial(default_config_env)]
227 fn cwd_guard_changes_and_restores() {
228 let cur_dir = || env::current_dir().unwrap().canonicalize().unwrap();
229 let tmp1 = tempdir().unwrap();
230 let tmp2 = tempdir().unwrap();
231
232 let original_dir = cur_dir();
233
234 {
235 let _guard = CwdGuard::set(tmp1.path()).unwrap();
236 assert_eq!(cur_dir(), tmp1.path().canonicalize().unwrap());
237
238 {
239 let _guard2 = CwdGuard::set(tmp2.path()).unwrap();
240 assert_eq!(cur_dir(), tmp2.path().canonicalize().unwrap());
241 }
242
243 assert_eq!(cur_dir(), tmp1.path().canonicalize().unwrap());
245 }
246
247 assert_eq!(cur_dir(), original_dir);
249 }
250
251 #[test]
252 #[serial(default_config_env)]
253 fn cwd_guard_handles_nonexistent_directory_error() {
254 let nonexistent = Path::new("/nonexistent/path/that/does/not/exist");
255
256 let result = CwdGuard::set(nonexistent);
257 assert!(result.is_err());
258 }
259
260 #[test]
261 fn closure_guard_runs_on_drop() {
262 use std::sync::Arc;
263 use std::sync::atomic::{AtomicBool, Ordering};
264
265 let ran = Arc::new(AtomicBool::new(false));
266 let ran_clone = ran.clone();
267 {
268 let _guard = ClosureGuard::new(move || ran_clone.store(true, Ordering::SeqCst));
269 assert!(!ran.load(Ordering::SeqCst));
270 }
271 assert!(ran.load(Ordering::SeqCst));
272 }
273}