std/sys/process/
env.rs

1use crate::collections::BTreeMap;
2use crate::ffi::{OsStr, OsString};
3use crate::sys::process::EnvKey;
4use crate::{env, fmt};
5
6/// Stores a set of changes to an environment
7#[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    // 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)]
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}