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, ValueToString, 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: ResolvedVc<FileSystemPath>,
17}
18
19#[turbo_tasks::value_impl]
20impl DotenvProcessEnv {
21 #[turbo_tasks::function]
22 pub fn new(
23 prior: Option<ResolvedVc<Box<dyn ProcessEnv>>>,
24 path: ResolvedVc<FileSystemPath>,
25 ) -> Vc<Self> {
26 DotenvProcessEnv { prior, path }.cell()
27 }
28
29 #[turbo_tasks::function]
30 pub fn read_prior(&self) -> Vc<EnvMap> {
31 match self.prior {
32 None => EnvMap::empty(),
33 Some(p) => p.read_all(),
34 }
35 }
36
37 #[turbo_tasks::function]
38 pub async fn read_all_with_prior(self: Vc<Self>, prior: Vc<EnvMap>) -> Result<Vc<EnvMap>> {
39 let this = self.await?;
40 let prior = prior.await?;
41
42 let file = this.path.read().await?;
43 if let FileContent::Content(f) = &*file {
44 let res;
45 let vars;
46 {
47 let lock = GLOBAL_ENV_LOCK.lock().unwrap();
48
49 let initial = sorted_env_vars();
53
54 restore_env(&initial, &prior, &lock);
55
56 res = dotenv::from_read(f.read()).map(|e| e.load());
60
61 vars = sorted_env_vars();
62 restore_env(&vars, &initial, &lock);
63 }
64
65 if let Err(e) = res {
66 return Err(e).context(anyhow!(
67 "unable to read {} for env vars",
68 this.path.to_string().await?
69 ));
70 }
71
72 Ok(Vc::cell(vars))
73 } else {
74 Ok(ReadRef::cell(prior))
77 }
78 }
79}
80
81#[turbo_tasks::value_impl]
82impl ProcessEnv for DotenvProcessEnv {
83 #[turbo_tasks::function]
84 fn read_all(self: Vc<Self>) -> Vc<EnvMap> {
85 let prior = self.read_prior();
86 self.read_all_with_prior(prior)
87 }
88}
89
90fn restore_env(
92 from: &FxIndexMap<RcStr, RcStr>,
93 to: &FxIndexMap<RcStr, RcStr>,
94 _lock: &MutexGuard<()>,
95) {
96 for key in from.keys() {
97 if !to.contains_key(key) {
98 unsafe { env::remove_var(key) };
99 }
100 }
101 for (key, value) in to {
102 match from.get(key) {
103 Some(v) if v == value => {}
104 _ => unsafe { env::set_var(key, value) },
105 }
106 }
107}