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
48#[cfg(not(target_family = "wasm"))]
49impl Drop for EnvVarGuard {
50 fn drop(&mut self) {
51 if let Some(v) = &self.prev {
52 unsafe {
53 env::set_var(self.key, v);
54 }
55 } else {
56 unsafe {
57 env::remove_var(self.key);
58 }
59 }
60 }
61}
62
63#[cfg(not(target_family = "wasm"))]
89pub struct CwdGuard {
90 prev: PathBuf,
91}
92
93#[cfg(not(target_family = "wasm"))]
94impl CwdGuard {
95 pub fn set(new_dir: &Path) -> std::io::Result<Self> {
96 let prev = env::current_dir()?;
97 env::set_current_dir(new_dir)?;
98 Ok(Self { prev })
99 }
100}
101
102#[cfg(not(target_family = "wasm"))]
103impl Drop for CwdGuard {
104 fn drop(&mut self) {
105 let _ = env::set_current_dir(&self.prev);
106 }
107}
108
109pub struct ClosureGuard<F: FnOnce()> {
124 closure: Option<F>,
125}
126
127impl<F: FnOnce()> ClosureGuard<F> {
128 pub fn new(f: F) -> Self {
129 Self { closure: Some(f) }
130 }
131}
132
133impl<F: FnOnce()> Drop for ClosureGuard<F> {
134 fn drop(&mut self) {
135 if let Some(f) = self.closure.take() {
136 f();
137 }
138 }
139}
140
141#[cfg(test)]
142mod tests {
143 use serial_test::serial;
144 use tempfile::tempdir;
145
146 use super::*;
147
148 #[test]
149 #[serial(default_config_env)]
150 fn env_var_guard_sets_and_restores() {
151 let key = "TEST_ENV_VAR_GUARD";
152
153 unsafe {
155 env::set_var(key, "initial");
156 }
157
158 {
159 let _guard = EnvVarGuard::set(key, "temporary");
160 assert_eq!(env::var(key).unwrap(), "temporary");
161 }
162
163 assert_eq!(env::var(key).unwrap(), "initial");
165 }
166
167 #[test]
168 #[serial(default_config_env)]
169 fn env_var_guard_restores_none_when_var_did_not_exist() {
170 let key = "TEST_ENV_VAR_GUARD_NEW";
171
172 unsafe {
174 env::remove_var(key);
175 }
176 assert!(env::var(key).is_err());
177
178 {
179 let _guard = EnvVarGuard::set(key, "temporary");
180 assert_eq!(env::var(key).unwrap(), "temporary");
181 }
182
183 assert!(env::var(key).is_err());
185 }
186
187 #[test]
188 #[serial(default_config_env)]
189 fn env_var_guard_multiple_guards_same_key() {
190 let key = "TEST_ENV_VAR_GUARD_MULTI";
191
192 unsafe {
193 env::set_var(key, "initial");
194 }
195
196 {
197 let _guard1 = EnvVarGuard::set(key, "first");
198 assert_eq!(env::var(key).unwrap(), "first");
199
200 {
201 let _guard2 = EnvVarGuard::set(key, "second");
202 assert_eq!(env::var(key).unwrap(), "second");
203 }
204
205 assert_eq!(env::var(key).unwrap(), "first");
207 }
208
209 assert_eq!(env::var(key).unwrap(), "initial");
211 }
212
213 #[test]
214 #[serial(default_config_env)]
215 fn cwd_guard_changes_and_restores() {
216 let cur_dir = || env::current_dir().unwrap().canonicalize().unwrap();
217 let tmp1 = tempdir().unwrap();
218 let tmp2 = tempdir().unwrap();
219
220 let original_dir = cur_dir();
221
222 {
223 let _guard = CwdGuard::set(tmp1.path()).unwrap();
224 assert_eq!(cur_dir(), tmp1.path().canonicalize().unwrap());
225
226 {
227 let _guard2 = CwdGuard::set(tmp2.path()).unwrap();
228 assert_eq!(cur_dir(), tmp2.path().canonicalize().unwrap());
229 }
230
231 assert_eq!(cur_dir(), tmp1.path().canonicalize().unwrap());
233 }
234
235 assert_eq!(cur_dir(), original_dir);
237 }
238
239 #[test]
240 #[serial(default_config_env)]
241 fn cwd_guard_handles_nonexistent_directory_error() {
242 let nonexistent = Path::new("/nonexistent/path/that/does/not/exist");
243
244 let result = CwdGuard::set(nonexistent);
245 assert!(result.is_err());
246 }
247
248 #[test]
249 fn closure_guard_runs_on_drop() {
250 use std::sync::Arc;
251 use std::sync::atomic::{AtomicBool, Ordering};
252
253 let ran = Arc::new(AtomicBool::new(false));
254 let ran_clone = ran.clone();
255 {
256 let _guard = ClosureGuard::new(move || ran_clone.store(true, Ordering::SeqCst));
257 assert!(!ran.load(Ordering::SeqCst));
258 }
259 assert!(ran.load(Ordering::SeqCst));
260 }
261}