1use crate::collections::BTreeMap;
2use crate::ffi::{OsStr, OsString};
3use crate::sys::process::EnvKey;
4use crate::{env, fmt};
5
6#[derive(Clone, Default)]
8pub struct CommandEnv {
9 clear: bool,
10 saw_path: bool,
11 vars: BTreeMap<EnvKey, Option<OsString>>,
12}
13
14impl fmt::Debug for CommandEnv {
15 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
16 let mut debug_command_env = f.debug_struct("CommandEnv");
17 debug_command_env.field("clear", &self.clear).field("vars", &self.vars);
18 debug_command_env.finish()
19 }
20}
21
22impl CommandEnv {
23 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 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)]
92pub struct CommandEnvs<'a> {
93 iter: crate::collections::btree_map::Iter<'a, EnvKey, Option<OsString>>,
94}
95
96impl<'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, value)| (key.as_ref(), value.as_deref()))
101 }
102
103 fn size_hint(&self) -> (usize, Option<usize>) {
104 self.iter.size_hint()
105 }
106}
107
108impl<'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}