1 | use crate::collections::BTreeMap; |
2 | use crate::ffi::{OsStr, OsString}; |
3 | use crate::sys::process::EnvKey; |
4 | use crate::{env, fmt}; |
5 | |
6 | /// Stores a set of changes to an environment |
7 | #[derive (Clone, Default)] |
8 | pub struct CommandEnv { |
9 | clear: bool, |
10 | saw_path: bool, |
11 | vars: BTreeMap<EnvKey, Option<OsString>>, |
12 | } |
13 | |
14 | impl fmt::Debug for CommandEnv { |
15 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
16 | let mut debug_command_env: DebugStruct<'_, '_> = f.debug_struct(name:"CommandEnv" ); |
17 | debug_command_env.field("clear" , &self.clear).field(name:"vars" , &self.vars); |
18 | debug_command_env.finish() |
19 | } |
20 | } |
21 | |
22 | impl CommandEnv { |
23 | // Capture the current environment with these changes applied |
24 | pub fn capture(&self) -> BTreeMap<EnvKey, OsString> { |
25 | let mut result = BTreeMap::<EnvKey, OsString>::new(); |
26 | if !self.clear { |
27 | for (k, v) in env::vars_os() { |
28 | result.insert(k.into(), v); |
29 | } |
30 | } |
31 | for (k, maybe_v) in &self.vars { |
32 | if let &Some(ref v) = maybe_v { |
33 | result.insert(k.clone(), v.clone()); |
34 | } else { |
35 | result.remove(k); |
36 | } |
37 | } |
38 | result |
39 | } |
40 | |
41 | pub fn is_unchanged(&self) -> bool { |
42 | !self.clear && self.vars.is_empty() |
43 | } |
44 | |
45 | pub fn capture_if_changed(&self) -> Option<BTreeMap<EnvKey, OsString>> { |
46 | if self.is_unchanged() { None } else { Some(self.capture()) } |
47 | } |
48 | |
49 | // The following functions build up changes |
50 | pub fn set(&mut self, key: &OsStr, value: &OsStr) { |
51 | let key = EnvKey::from(key); |
52 | self.maybe_saw_path(&key); |
53 | self.vars.insert(key, Some(value.to_owned())); |
54 | } |
55 | |
56 | pub fn remove(&mut self, key: &OsStr) { |
57 | let key = EnvKey::from(key); |
58 | self.maybe_saw_path(&key); |
59 | if self.clear { |
60 | self.vars.remove(&key); |
61 | } else { |
62 | self.vars.insert(key, None); |
63 | } |
64 | } |
65 | |
66 | pub fn clear(&mut self) { |
67 | self.clear = true; |
68 | self.vars.clear(); |
69 | } |
70 | |
71 | pub fn does_clear(&self) -> bool { |
72 | self.clear |
73 | } |
74 | |
75 | pub fn have_changed_path(&self) -> bool { |
76 | self.saw_path || self.clear |
77 | } |
78 | |
79 | fn maybe_saw_path(&mut self, key: &EnvKey) { |
80 | if !self.saw_path && key == "PATH" { |
81 | self.saw_path = true; |
82 | } |
83 | } |
84 | |
85 | pub fn iter(&self) -> CommandEnvs<'_> { |
86 | let iter = self.vars.iter(); |
87 | CommandEnvs { iter } |
88 | } |
89 | } |
90 | |
91 | #[derive (Debug)] |
92 | pub struct CommandEnvs<'a> { |
93 | iter: crate::collections::btree_map::Iter<'a, EnvKey, Option<OsString>>, |
94 | } |
95 | |
96 | impl<'a> Iterator for CommandEnvs<'a> { |
97 | type Item = (&'a OsStr, Option<&'a OsStr>); |
98 | |
99 | fn next(&mut self) -> Option<Self::Item> { |
100 | self.iter.next().map(|(key: &'a OsString, value: &'a Option)| (key.as_ref(), value.as_deref())) |
101 | } |
102 | |
103 | fn size_hint(&self) -> (usize, Option<usize>) { |
104 | self.iter.size_hint() |
105 | } |
106 | } |
107 | |
108 | impl<'a> ExactSizeIterator for CommandEnvs<'a> { |
109 | fn len(&self) -> usize { |
110 | self.iter.len() |
111 | } |
112 | fn is_empty(&self) -> bool { |
113 | self.iter.is_empty() |
114 | } |
115 | } |
116 | |