1#![deny(missing_docs)]
2use std::collections::HashMap;
47use std::env;
48use std::ffi::{OsStr, OsString};
49use std::hash::Hash;
50
51use parking_lot::{ReentrantMutex, ReentrantMutexGuard};
52
53static SERIAL_TEST: ReentrantMutex<()> = ReentrantMutex::new(());
55
56pub fn with_var<K, V, F, R>(key: K, value: Option<V>, closure: F) -> R
63where
64 K: AsRef<OsStr> + Clone + Eq + Hash,
65 V: AsRef<OsStr> + Clone,
66 F: FnOnce() -> R,
67{
68 with_vars([(key, value)], closure)
69}
70
71pub fn with_var_unset<K, F, R>(key: K, closure: F) -> R
83where
84 K: AsRef<OsStr> + Clone + Eq + Hash,
85 F: FnOnce() -> R,
86{
87 with_var(key, None::<&str>, closure)
88}
89
90struct RestoreEnv<'a> {
91 env: HashMap<&'a OsStr, Option<OsString>>,
92 _guard: ReentrantMutexGuard<'a, ()>,
93}
94
95impl<'a> RestoreEnv<'a> {
96 fn capture<I>(guard: ReentrantMutexGuard<'a, ()>, vars: I) -> Self
102 where
103 I: Iterator<Item = &'a OsStr> + 'a,
104 {
105 let env = vars.map(|v| (v, env::var_os(v))).collect();
106 Self { env, _guard: guard }
107 }
108}
109
110impl<'a> Drop for RestoreEnv<'a> {
111 fn drop(&mut self) {
112 for (var, value) in self.env.iter() {
113 update_env(var, value.as_ref().map(|v| v.as_os_str()));
114 }
115 }
116}
117
118pub fn with_vars<K, V, F, R>(kvs: impl AsRef<[(K, Option<V>)]>, closure: F) -> R
127where
128 K: AsRef<OsStr> + Clone + Eq + Hash,
129 V: AsRef<OsStr> + Clone,
130 F: FnOnce() -> R,
131{
132 let old_env = RestoreEnv::capture(
133 SERIAL_TEST.lock(),
134 kvs.as_ref().iter().map(|(k, _)| k.as_ref()),
135 );
136 for (key, value) in kvs.as_ref() {
137 update_env(key, value.as_ref());
138 }
139 let retval = closure();
140 drop(old_env);
141 retval
142}
143
144pub fn with_vars_unset<K, F, R>(keys: impl AsRef<[K]>, closure: F) -> R
163where
164 K: AsRef<OsStr> + Clone + Eq + Hash,
165 F: FnOnce() -> R,
166{
167 let kvs = keys
168 .as_ref()
169 .iter()
170 .map(|key| (key, None::<&str>))
171 .collect::<Vec<_>>();
172 with_vars(kvs, closure)
173}
174
175fn update_env<K, V>(key: K, value: Option<V>)
176where
177 K: AsRef<OsStr>,
178 V: AsRef<OsStr>,
179{
180 match value {
181 Some(v) => env::set_var(key, v),
182 None => env::remove_var(key),
183 }
184}
185
186#[cfg(feature = "async_closure")]
187pub async fn async_with_vars<K, V, F, R>(kvs: impl AsRef<[(K, Option<V>)]>, closure: F) -> R
201where
202 K: AsRef<OsStr> + Clone + Eq + Hash,
203 V: AsRef<OsStr> + Clone,
204 F: std::future::Future<Output = R> + std::future::IntoFuture<Output = R>,
205{
206 let old_env = RestoreEnv::capture(
207 SERIAL_TEST.lock(),
208 kvs.as_ref().iter().map(|(k, _)| k.as_ref()),
209 );
210 for (key, value) in kvs.as_ref() {
211 update_env(key, value.as_ref());
212 }
213 let retval = closure.await;
214 drop(old_env);
215 retval
216}
217
218#[cfg(test)]
221mod tests {
222 use std::env::VarError;
223 use std::{env, panic};
224
225 #[test]
227 fn test_with_var_set() {
228 let hello_not_set = env::var("HELLO");
229 assert!(hello_not_set.is_err(), "`HELLO` must not be set.");
230
231 crate::with_var("HELLO", Some("world!"), || {
232 let hello_is_set = env::var("HELLO").unwrap();
233 assert_eq!(hello_is_set, "world!", "`HELLO` must be set to \"world!\".");
234 });
235
236 let hello_not_set_after = env::var("HELLO");
237 assert!(hello_not_set_after.is_err(), "`HELLO` must not be set.");
238 }
239
240 #[test]
242 fn test_with_var_set_to_none() {
243 env::set_var("FOO", "bar");
244 let foo_is_set = env::var("FOO").unwrap();
245 assert_eq!(foo_is_set, "bar", "`FOO` must be set to \"bar\".");
246
247 crate::with_var("FOO", None::<&str>, || {
248 let foo_not_set = env::var("FOO");
249 assert!(foo_not_set.is_err(), "`FOO` must not be set.");
250 });
251
252 let foo_is_set_after = env::var("FOO").unwrap();
253 assert_eq!(foo_is_set_after, "bar", "`FOO` must be set to \"bar\".");
254 }
255
256 #[test]
258 fn test_with_var_unset() {
259 env::set_var("BAR", "foo");
260 let foo_is_set = env::var("BAR").unwrap();
261 assert_eq!(foo_is_set, "foo", "`BAR` must be set to \"foo\".");
262
263 crate::with_var_unset("BAR", || {
264 let foo_not_set = env::var("BAR");
265 assert!(foo_not_set.is_err(), "`BAR` must not be set.");
266 });
267
268 let foo_is_set_after = env::var("BAR").unwrap();
269 assert_eq!(foo_is_set_after, "foo", "`BAR` must be set to \"foo\".");
270 }
271
272 #[test]
274 fn test_with_var_override() {
275 env::set_var("BLAH", "blub");
276 let blah_is_set = env::var("BLAH").unwrap();
277 assert_eq!(blah_is_set, "blub", "`BLAH` must be set to \"blah\".");
278
279 crate::with_var("BLAH", Some("new"), || {
280 let blah_is_set_new = env::var("BLAH").unwrap();
281 assert_eq!(blah_is_set_new, "new", "`BLAH` must be set to \"newb\".");
282 });
283
284 let blah_is_set_after = env::var("BLAH").unwrap();
285 assert_eq!(
286 blah_is_set_after, "blub",
287 "`BLAH` must be set to \"blubr\"."
288 );
289 }
290
291 #[test]
293 fn test_with_var_panic() {
294 env::set_var("PANIC", "panic");
295 let panic_is_set = env::var("PANIC").unwrap();
296 assert_eq!(panic_is_set, "panic", "`PANIC` must be set to \"panic\".");
297
298 let did_panic = panic::catch_unwind(|| {
299 crate::with_var("PANIC", Some("don't panic"), || {
300 let panic_is_set_new = env::var("PANIC").unwrap();
301 assert_eq!(
302 panic_is_set_new, "don't panic",
303 "`PANIC` must be set to \"don't panic\"."
304 );
305 panic!("abort this closure with a panic.");
306 });
307 });
308
309 assert!(did_panic.is_err(), "The closure must panic.");
310 let panic_is_set_after = env::var("PANIC").unwrap();
311 assert_eq!(
312 panic_is_set_after, "panic",
313 "`PANIC` must be set to \"panic\"."
314 );
315 }
316
317 #[test]
319 fn test_with_vars_set() {
320 let one_not_set = env::var("ONE");
321 assert!(one_not_set.is_err(), "`ONE` must not be set.");
322 let two_not_set = env::var("TWO");
323 assert!(two_not_set.is_err(), "`TWO` must not be set.");
324
325 crate::with_vars([("ONE", Some("1")), ("TWO", Some("2"))], || {
326 let one_is_set = env::var("ONE").unwrap();
327 assert_eq!(one_is_set, "1", "`ONE` must be set to \"1\".");
328 let two_is_set = env::var("TWO").unwrap();
329 assert_eq!(two_is_set, "2", "`TWO` must be set to \"2\".");
330 });
331
332 let one_not_set_after = env::var("ONE");
333 assert!(one_not_set_after.is_err(), "`ONE` must not be set.");
334 let two_not_set_after = env::var("TWO");
335 assert!(two_not_set_after.is_err(), "`TWO` must not be set.");
336 }
337
338 #[test]
340 fn test_with_vars_set_returning() {
341 let one_not_set = env::var("ONE");
342 assert!(one_not_set.is_err(), "`ONE` must not be set.");
343 let two_not_set = env::var("TWO");
344 assert!(two_not_set.is_err(), "`TWO` must not be set.");
345
346 let r = crate::with_vars([("ONE", Some("1")), ("TWO", Some("2"))], || {
347 let one_is_set = env::var("ONE").unwrap();
348 let two_is_set = env::var("TWO").unwrap();
349 (one_is_set, two_is_set)
350 });
351
352 let (one_from_closure, two_from_closure) = r;
353
354 assert_eq!(one_from_closure, "1", "`ONE` had to be set to \"1\".");
355 assert_eq!(two_from_closure, "2", "`TWO` had to be set to \"2\".");
356
357 let one_not_set_after = env::var("ONE");
358 assert!(one_not_set_after.is_err(), "`ONE` must not be set.");
359 let two_not_set_after = env::var("TWO");
360 assert!(two_not_set_after.is_err(), "`TWO` must not be set.");
361 }
362
363 #[test]
365 fn test_with_vars_unset() {
366 env::set_var("SET_TO_BE_UNSET", "val");
367 env::remove_var("UNSET_TO_BE_UNSET");
368 assert_eq!(env::var("SET_TO_BE_UNSET"), Ok("val".to_string()));
370 assert_eq!(env::var("UNSET_TO_BE_UNSET"), Err(VarError::NotPresent));
371
372 crate::with_vars_unset(["SET_TO_BE_UNSET", "UNSET_TO_BE_UNSET"], || {
373 assert_eq!(env::var("SET_TO_BE_UNSET"), Err(VarError::NotPresent));
374 assert_eq!(env::var("UNSET_TO_BE_UNSET"), Err(VarError::NotPresent));
375 });
376 assert_eq!(env::var("SET_TO_BE_UNSET"), Ok("val".to_string()));
377 assert_eq!(env::var("UNSET_TO_BE_UNSET"), Err(VarError::NotPresent));
378 }
379
380 #[test]
382 fn test_with_vars_partially_unset() {
383 let to_be_set_not_set = env::var("TO_BE_SET");
384 assert!(to_be_set_not_set.is_err(), "`TO_BE_SET` must not be set.");
385 env::set_var("TO_BE_UNSET", "unset");
386 let to_be_unset_is_set = env::var("TO_BE_UNSET").unwrap();
387 assert_eq!(
388 to_be_unset_is_set, "unset",
389 "`TO_BE_UNSET` must be set to \"unset\"."
390 );
391
392 crate::with_vars(
393 [("TO_BE_SET", Some("set")), ("TO_BE_UNSET", None::<&str>)],
394 || {
395 let to_be_set_is_set = env::var("TO_BE_SET").unwrap();
396 assert_eq!(
397 to_be_set_is_set, "set",
398 "`TO_BE_SET` must be set to \"set\"."
399 );
400 let to_be_unset_not_set = env::var("TO_BE_UNSET");
401 assert!(
402 to_be_unset_not_set.is_err(),
403 "`TO_BE_UNSET` must not be set."
404 );
405 },
406 );
407
408 let to_be_set_not_set_after = env::var("TO_BE_SET");
409 assert!(
410 to_be_set_not_set_after.is_err(),
411 "`TO_BE_SET` must not be set."
412 );
413 let to_be_unset_is_set_after = env::var("TO_BE_UNSET").unwrap();
414 assert_eq!(
415 to_be_unset_is_set_after, "unset",
416 "`TO_BE_UNSET` must be set to \"unset\"."
417 );
418 }
419
420 #[test]
422 fn test_with_vars_override() {
423 env::set_var("DOIT", "doit");
424 let doit_is_set = env::var("DOIT").unwrap();
425 assert_eq!(doit_is_set, "doit", "`DOIT` must be set to \"doit\".");
426 env::set_var("NOW", "now");
427 let now_is_set = env::var("NOW").unwrap();
428 assert_eq!(now_is_set, "now", "`NOW` must be set to \"now\".");
429
430 crate::with_vars([("DOIT", Some("other")), ("NOW", Some("value"))], || {
431 let doit_is_set_new = env::var("DOIT").unwrap();
432 assert_eq!(doit_is_set_new, "other", "`DOIT` must be set to \"other\".");
433 let now_is_set_new = env::var("NOW").unwrap();
434 assert_eq!(now_is_set_new, "value", "`NOW` must be set to \"value\".");
435 });
436
437 let doit_is_set_after = env::var("DOIT").unwrap();
438 assert_eq!(doit_is_set_after, "doit", "`DOIT` must be set to \"doit\".");
439 let now_is_set_after = env::var("NOW").unwrap();
440 assert_eq!(now_is_set_after, "now", "`NOW` must be set to \"now\".");
441 }
442
443 #[test]
445 fn test_with_vars_same_vars() {
446 let override_not_set = env::var("OVERRIDE");
447 assert!(override_not_set.is_err(), "`OVERRIDE` must not be set.");
448
449 crate::with_vars(
450 [
451 ("OVERRIDE", Some("initial")),
452 ("OVERRIDE", Some("override")),
453 ],
454 || {
455 let override_is_set = env::var("OVERRIDE").unwrap();
456 assert_eq!(
457 override_is_set, "override",
458 "`OVERRIDE` must be set to \"override\"."
459 );
460 },
461 );
462
463 let override_not_set_after = env::var("OVERRIDE");
464 assert!(
465 override_not_set_after.is_err(),
466 "`OVERRIDE` must not be set."
467 );
468 }
469
470 #[test]
472 fn test_with_vars_unset_set() {
473 env::set_var("MY_VAR", "my_var");
474 let my_var_is_set = env::var("MY_VAR").unwrap();
475 assert_eq!(
476 my_var_is_set, "my_var",
477 "`MY_VAR` must be set to \"my_var`\"."
478 );
479
480 crate::with_vars(
481 [("MY_VAR", None::<&str>), ("MY_VAR", Some("new value"))],
482 || {
483 let my_var_is_set_new = env::var("MY_VAR").unwrap();
484 assert_eq!(
485 my_var_is_set_new, "new value",
486 "`MY_VAR` must be set to \"new value\"."
487 );
488 },
489 );
490
491 let my_var_is_set_after = env::var("MY_VAR").unwrap();
492 assert_eq!(
493 my_var_is_set_after, "my_var",
494 "`MY_VAR` must be set to \"my_var\"."
495 );
496 }
497
498 #[test]
500 fn test_with_vars_set_unset() {
501 let not_my_var_not_set = env::var("NOT_MY_VAR");
502 assert!(not_my_var_not_set.is_err(), "`NOT_MY_VAR` must not be set.");
503
504 crate::with_vars(
505 [
506 ("NOT_MY_VAR", Some("it is set")),
507 ("NOT_MY_VAR", None::<&str>),
508 ],
509 || {
510 let not_my_var_not_set_new = env::var("NOT_MY_VAR");
511 assert!(
512 not_my_var_not_set_new.is_err(),
513 "`NOT_MY_VAR` must not be set."
514 );
515 },
516 );
517
518 let not_my_var_not_set_after = env::var("NOT_MY_VAR");
519 assert!(
520 not_my_var_not_set_after.is_err(),
521 "`NOT_MY_VAR` must not be set."
522 );
523 }
524
525 #[test]
526 fn test_with_nested_set() {
527 crate::with_var("MY_VAR_1", Some("1"), || {
528 crate::with_var("MY_VAR_2", Some("2"), || {
529 assert_eq!(env::var("MY_VAR_1").unwrap(), "1");
530 assert_eq!(env::var("MY_VAR_2").unwrap(), "2");
531 })
532 });
533
534 assert!(env::var("MY_VAR_1").is_err());
535 assert!(env::var("MY_VAR_2").is_err());
536 }
537
538 #[test]
539 fn test_fn_once() {
540 let value = String::from("Hello, ");
541 let value = crate::with_var("WORLD", Some("world!"), || {
542 value + &env::var("WORLD").unwrap()
543 });
544 assert_eq!(value, "Hello, world!");
545 }
546
547 #[cfg(feature = "async_closure")]
548 async fn check_var() {
549 let v = std::env::var("MY_VAR").unwrap();
550 assert_eq!(v, "ok".to_owned());
551 }
552
553 #[cfg(feature = "async_closure")]
554 #[tokio::test]
555 async fn test_async_closure() {
556 crate::async_with_vars([("MY_VAR", Some("ok"))], check_var()).await;
557 let f = async {
558 let v = std::env::var("MY_VAR").unwrap();
559 assert_eq!(v, "ok".to_owned());
560 };
561 crate::async_with_vars([("MY_VAR", Some("ok"))], f).await;
562 }
563
564 #[cfg(feature = "async_closure")]
565 #[tokio::test(flavor = "multi_thread")]
566 async fn test_async_closure_calls_closure() {
567 let (tx, rx) = tokio::sync::oneshot::channel();
568 let f = async {
569 tx.send(std::env::var("MY_VAR")).unwrap();
570 };
571 crate::async_with_vars([("MY_VAR", Some("ok"))], f).await;
572 let value = rx.await.unwrap().unwrap();
573 assert_eq!(value, "ok".to_owned());
574 }
575
576 #[cfg(feature = "async_closure")]
577 #[tokio::test(flavor = "multi_thread")]
578 async fn test_async_with_vars_set_returning() {
579 let one_not_set = env::var("ONE");
580 assert!(one_not_set.is_err(), "`ONE` must not be set.");
581 let two_not_set = env::var("TWO");
582 assert!(two_not_set.is_err(), "`TWO` must not be set.");
583
584 let r = crate::async_with_vars([("ONE", Some("1")), ("TWO", Some("2"))], async {
585 let one_is_set = env::var("ONE").unwrap();
586 let two_is_set = env::var("TWO").unwrap();
587 (one_is_set, two_is_set)
588 })
589 .await;
590
591 let (one_from_closure, two_from_closure) = r;
592
593 assert_eq!(one_from_closure, "1", "`ONE` had to be set to \"1\".");
594 assert_eq!(two_from_closure, "2", "`TWO` had to be set to \"2\".");
595
596 let one_not_set_after = env::var("ONE");
597 assert!(one_not_set_after.is_err(), "`ONE` must not be set.");
598 let two_not_set_after = env::var("TWO");
599 assert!(two_not_set_after.is_err(), "`TWO` must not be set.");
600 }
601}