turbo_tasks_env/
dotenv.rs1use std::{env, sync::MutexGuard};
2
3use anyhow::{Context, Result, anyhow};
4use turbo_rcstr::RcStr;
5use turbo_tasks::{FxIndexMap, ReadRef, ResolvedVc, Vc};
6use turbo_tasks_fs::{FileContent, FileSystemPath};
7
8use crate::{EnvMap, GLOBAL_ENV_LOCK, ProcessEnv, sorted_env_vars};
9
10#[turbo_tasks::value]
14pub struct DotenvProcessEnv {
15 prior: Option<ResolvedVc<Box<dyn ProcessEnv>>>,
16 path: FileSystemPath,
17}
18
19#[turbo_tasks::value_impl]
20impl DotenvProcessEnv {
21 #[turbo_tasks::function]
22 pub fn new(prior: Option<ResolvedVc<Box<dyn ProcessEnv>>>, path: FileSystemPath) -> Vc<Self> {
23 DotenvProcessEnv { prior, path }.cell()
24 }
25
26 #[turbo_tasks::function]
27 pub fn read_prior(&self) -> Vc<EnvMap> {
28 match self.prior {
29 None => EnvMap::empty(),
30 Some(p) => p.read_all(),
31 }
32 }
33
34 #[turbo_tasks::function]
35 pub async fn read_all_with_prior(self: Vc<Self>, prior: Vc<EnvMap>) -> Result<Vc<EnvMap>> {
36 let this = self.await?;
37 let prior = prior.await?;
38
39 let file = this.path.read().await?;
40 if let FileContent::Content(f) = &*file {
41 let res;
42 let vars;
43 {
44 let lock = GLOBAL_ENV_LOCK.lock().unwrap();
45
46 let initial = sorted_env_vars();
50
51 restore_env(&initial, &prior, &lock);
52
53 res = dotenv::from_read(f.read()).map(|e| e.load());
57
58 vars = sorted_env_vars();
59 restore_env(&vars, &initial, &lock);
60 }
61
62 if let Err(e) = res {
63 return Err(e).context(anyhow!(
64 "unable to read {} for env vars",
65 this.path.value_to_string().await?
66 ));
67 }
68
69 Ok(Vc::cell(vars))
70 } else {
71 Ok(ReadRef::cell(prior))
74 }
75 }
76}
77
78#[turbo_tasks::value_impl]
79impl ProcessEnv for DotenvProcessEnv {
80 #[turbo_tasks::function]
81 fn read_all(self: Vc<Self>) -> Vc<EnvMap> {
82 let prior = self.read_prior();
83 self.read_all_with_prior(prior)
84 }
85}
86
87fn restore_env(
89 from: &FxIndexMap<RcStr, RcStr>,
90 to: &FxIndexMap<RcStr, RcStr>,
91 _lock: &MutexGuard<()>,
92) {
93 for key in from.keys() {
94 if !to.contains_key(key) {
95 unsafe { env::remove_var(key) };
96 }
97 }
98 for (key, value) in to {
99 match from.get(key) {
100 Some(v) if v == value => {}
101 _ => unsafe { env::set_var(key, value) },
102 }
103 }
104}