1use crate::env::SubEnvironment;
2use std::borrow::{Borrow, Cow};
3use std::collections::HashMap;
4use std::fmt;
5use std::hash::Hash;
6use std::sync::Arc;
7
8pub trait VariableEnvironment {
10 type VarName: Eq + Hash;
12 type Var;
14 fn var<Q: ?Sized>(&self, name: &Q) -> Option<&Self::Var>
17 where
18 Self::VarName: Borrow<Q>,
19 Q: Hash + Eq;
20 fn set_var(&mut self, name: Self::VarName, val: Self::Var);
23 fn env_vars(&self) -> Cow<'_, [(&Self::VarName, &Self::Var)]>;
26}
27
28impl<'a, T: ?Sized + VariableEnvironment> VariableEnvironment for &'a mut T {
29 type VarName = T::VarName;
30 type Var = T::Var;
31
32 fn var<Q: ?Sized>(&self, name: &Q) -> Option<&Self::Var>
33 where
34 Self::VarName: Borrow<Q>,
35 Q: Hash + Eq,
36 {
37 (**self).var(name)
38 }
39
40 fn set_var(&mut self, name: Self::VarName, val: Self::Var) {
41 (**self).set_var(name, val);
42 }
43
44 fn env_vars(&self) -> Cow<'_, [(&Self::VarName, &Self::Var)]> {
45 (**self).env_vars()
46 }
47}
48
49pub trait ExportedVariableEnvironment: VariableEnvironment {
53 fn exported_var(&self, name: &Self::VarName) -> Option<(&Self::Var, bool)>;
55 fn set_exported_var(&mut self, name: Self::VarName, val: Self::Var, exported: bool);
57}
58
59impl<'a, T: ?Sized + ExportedVariableEnvironment> ExportedVariableEnvironment for &'a mut T {
60 fn exported_var(&self, name: &Self::VarName) -> Option<(&Self::Var, bool)> {
61 (**self).exported_var(name)
62 }
63
64 fn set_exported_var(&mut self, name: Self::VarName, val: Self::Var, exported: bool) {
65 (**self).set_exported_var(name, val, exported)
66 }
67}
68
69pub trait UnsetVariableEnvironment: VariableEnvironment {
71 fn unset_var(&mut self, name: &Self::VarName);
73}
74
75impl<'a, T: ?Sized + UnsetVariableEnvironment> UnsetVariableEnvironment for &'a mut T {
76 fn unset_var(&mut self, name: &T::VarName) {
77 (**self).unset_var(name);
78 }
79}
80
81#[derive(PartialEq, Eq)]
83pub struct VarEnv<N: Eq + Hash, V> {
84 vars: Arc<HashMap<N, (V, bool)>>,
88}
89
90impl<N, V> VarEnv<N, V>
91where
92 N: Eq + Hash,
93{
94 pub fn new() -> Self {
96 Self {
97 vars: Arc::new(HashMap::new()),
98 }
99 }
100
101 pub fn with_process_env_vars() -> Self
104 where
105 N: From<String>,
106 V: From<String>,
107 {
108 Self::with_env_vars(::std::env::vars().map(|(k, v)| (k.into(), v.into())))
109 }
110
111 pub fn with_env_vars<I: IntoIterator<Item = (N, V)>>(iter: I) -> Self {
115 Self {
116 vars: Arc::new(
117 iter.into_iter()
118 .map(|(k, v)| (k, (v, true)))
119 .collect::<HashMap<_, _>>(),
120 ),
121 }
122 }
123}
124
125impl<N, V> VariableEnvironment for VarEnv<N, V>
126where
127 N: Eq + Clone + Hash,
128 V: Eq + Clone,
129{
130 type VarName = N;
131 type Var = V;
132
133 fn var<Q: ?Sized>(&self, name: &Q) -> Option<&Self::Var>
134 where
135 Self::VarName: Borrow<Q>,
136 Q: Hash + Eq,
137 {
138 self.vars.get(name).map(|&(ref val, _)| val)
139 }
140
141 fn set_var(&mut self, name: Self::VarName, val: Self::Var) {
142 let (needs_insert, exported) = match self.vars.get(&name) {
143 Some(&(ref existing_val, exported)) => (&val != existing_val, exported),
144 None => (true, false),
145 };
146
147 if needs_insert {
148 Arc::make_mut(&mut self.vars).insert(name, (val, exported));
149 }
150 }
151
152 fn env_vars(&self) -> Cow<'_, [(&Self::VarName, &Self::Var)]> {
153 let ret: Vec<_> = self
154 .vars
155 .iter()
156 .filter_map(|(k, &(ref v, exported))| if exported { Some((k, v)) } else { None })
157 .collect();
158
159 Cow::Owned(ret)
160 }
161}
162
163impl<N, V> ExportedVariableEnvironment for VarEnv<N, V>
164where
165 N: Eq + Clone + Hash,
166 V: Eq + Clone,
167{
168 fn exported_var(&self, name: &Self::VarName) -> Option<(&Self::Var, bool)> {
169 self.vars
170 .get(name)
171 .map(|&(ref val, exported)| (val, exported))
172 }
173
174 fn set_exported_var(&mut self, name: Self::VarName, val: Self::Var, exported: bool) {
175 let needs_insert = match self.vars.get(&name) {
176 Some(&(ref existing_val, _)) => val != *existing_val,
177 None => true,
178 };
179
180 if needs_insert {
181 Arc::make_mut(&mut self.vars).insert(name, (val, exported));
182 }
183 }
184}
185
186impl<N, V> UnsetVariableEnvironment for VarEnv<N, V>
187where
188 N: Eq + Clone + Hash,
189 V: Eq + Clone,
190{
191 fn unset_var(&mut self, name: &N) {
192 if self.vars.contains_key(name) {
193 Arc::make_mut(&mut self.vars).remove(name);
194 }
195 }
196}
197
198impl<N, V> fmt::Debug for VarEnv<N, V>
199where
200 N: Eq + Ord + Hash + fmt::Debug,
201 V: fmt::Debug,
202{
203 fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
204 use std::collections::BTreeMap;
205
206 let mut vars = BTreeMap::new();
207 let mut env_vars = BTreeMap::new();
208
209 for (name, &(ref val, is_env)) in &*self.vars {
210 if is_env {
211 env_vars.insert(name, val);
212 } else {
213 vars.insert(name, val);
214 }
215 }
216
217 fmt.debug_struct(stringify!(VarEnv))
218 .field("env_vars", &env_vars)
219 .field("vars", &vars)
220 .finish()
221 }
222}
223
224impl<N, V> Default for VarEnv<N, V>
225where
226 N: Eq + Hash,
227{
228 fn default() -> Self {
229 Self::new()
230 }
231}
232
233impl<N, V> Clone for VarEnv<N, V>
234where
235 N: Eq + Hash,
236{
237 fn clone(&self) -> Self {
238 Self {
239 vars: self.vars.clone(),
240 }
241 }
242}
243
244impl<N, V> SubEnvironment for VarEnv<N, V>
245where
246 N: Eq + Hash,
247{
248 fn sub_env(&self) -> Self {
249 self.clone()
250 }
251}
252
253#[cfg(test)]
254mod tests {
255 use super::*;
256 use crate::env::SubEnvironment;
257
258 #[test]
259 fn test_set_get_unset_var() {
260 let name = "var";
261 let value = "value";
262 let mut env = VarEnv::new();
263 assert_eq!(env.var(name), None);
264 env.set_var(name, value);
265 assert_eq!(env.var(name), Some(&value));
266 env.unset_var(&name);
267 assert_eq!(env.var(name), None);
268 }
269
270 #[test]
271 fn test_set_get_unset_exported_var() {
272 let exported = "exported";
273 let exported_value = "exported_value";
274 let name = "var";
275 let value = "value";
276
277 let mut env = VarEnv::with_env_vars(vec![(exported, exported_value)]);
278 assert_eq!(env.exported_var(&exported), Some((&exported_value, true)));
279
280 assert_eq!(env.var(&name), None);
281 env.set_exported_var(name, value, false);
282 assert_eq!(env.exported_var(&name), Some((&value, false)));
283
284 let new_value = "new_value";
286 env.set_var(exported, new_value);
287 assert_eq!(env.exported_var(&exported), Some((&new_value, true)));
288 env.set_var(name, new_value);
289 assert_eq!(env.exported_var(&name), Some((&new_value, false)));
290 }
291
292 #[test]
293 fn test_sub_env_no_needless_clone() {
294 let not_set = "not set";
295 let name = "var";
296 let value = "value";
297 let mut env = VarEnv::new();
298 env.set_var(name, value);
299
300 let mut env = env.sub_env();
301 env.set_var(name, value);
302 if Arc::get_mut(&mut env.vars).is_some() {
303 panic!("needles clone!");
304 }
305
306 env.unset_var(¬_set);
307 if Arc::get_mut(&mut env.vars).is_some() {
308 panic!("needles clone!");
309 }
310 }
311
312 #[test]
313 fn test_env_vars() {
314 use std::collections::HashSet;
315 use std::iter::FromIterator;
316
317 let env_name1 = "env_name1";
318 let env_name2 = "env_name2";
319 let env_val1 = "env_val1";
320 let env_val2 = "env_val2";
321 let name = "name";
322 let val = "value";
323
324 let mut env = VarEnv::with_env_vars(vec![(env_name1, env_val1), (env_name2, env_val2)]);
325 env.set_var(name, val);
326
327 let correct = vec![(&env_name1, &env_val1), (&env_name2, &env_val2)];
328
329 let vars: HashSet<(_, _)> = HashSet::from_iter(env.env_vars().into_owned());
330 assert_eq!(vars, HashSet::from_iter(correct));
331 }
332
333 #[test]
334 fn test_set_var_in_child_env_should_not_affect_parent() {
335 let parent_name = "parent-var";
336 let parent_value = "parent-value";
337 let child_name = "child-var";
338 let child_value = "child-value";
339
340 let mut parent = VarEnv::new();
341 parent.set_var(parent_name, parent_value);
342
343 {
344 let mut child = parent.sub_env();
345 assert_eq!(child.var(parent_name), Some(&parent_value));
346
347 child.set_var(parent_name, child_value);
348 child.set_var(child_name, child_value);
349 assert_eq!(child.var(parent_name), Some(&child_value));
350 assert_eq!(child.var(child_name), Some(&child_value));
351
352 assert_eq!(parent.var(parent_name), Some(&parent_value));
353 assert_eq!(parent.var(child_name), None);
354 }
355
356 assert_eq!(parent.var(parent_name), Some(&parent_value));
357 assert_eq!(parent.var(child_name), None);
358 }
359
360 #[test]
361 fn test_get_env_vars_visible_in_parent_and_child() {
362 use std::collections::HashSet;
363 use std::iter::FromIterator;
364
365 let name1 = "var1";
366 let value1 = "value1";
367 let name2 = "var2";
368 let value2 = "value2";
369
370 let env = VarEnv::with_env_vars(vec![(name1, value1), (name2, value2)]);
371
372 let env_vars = HashSet::from_iter(vec![(&name1, &value1), (&name2, &value2)]);
373
374 let vars: HashSet<(_, _)> = HashSet::from_iter(env.env_vars().into_owned());
375 assert_eq!(vars, env_vars);
376
377 let child = env.sub_env();
378 let vars: HashSet<(_, _)> = HashSet::from_iter(child.env_vars().into_owned());
379 assert_eq!(vars, env_vars);
380 }
381}