std/sys_common/
process.rs

1#![allow(dead_code)]
2#![unstable(feature = "process_internals", issue = "none")]
3
4use crate::collections::BTreeMap;
5use crate::ffi::{OsStr, OsString};
6use crate::sys::pipe::read2;
7use crate::sys::process::{EnvKey, ExitStatus, Process, StdioPipes};
8use crate::{env, fmt, io};
9
10// Stores a set of changes to an environment
11#[derive(Clone, Default)]
12pub struct CommandEnv {
13    clear: bool,
14    saw_path: bool,
15    vars: BTreeMap<EnvKey, Option<OsString>>,
16}
17
18impl fmt::Debug for CommandEnv {
19    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
20        let mut debug_command_env = f.debug_struct("CommandEnv");
21        debug_command_env.field("clear", &self.clear).field("vars", &self.vars);
22        debug_command_env.finish()
23    }
24}
25
26impl CommandEnv {
27    // Capture the current environment with these changes applied
28    pub fn capture(&self) -> BTreeMap<EnvKey, OsString> {
29        let mut result = BTreeMap::<EnvKey, OsString>::new();
30        if !self.clear {
31            for (k, v) in env::vars_os() {
32                result.insert(k.into(), v);
33            }
34        }
35        for (k, maybe_v) in &self.vars {
36            if let &Some(ref v) = maybe_v {
37                result.insert(k.clone(), v.clone());
38            } else {
39                result.remove(k);
40            }
41        }
42        result
43    }
44
45    pub fn is_unchanged(&self) -> bool {
46        !self.clear && self.vars.is_empty()
47    }
48
49    pub fn capture_if_changed(&self) -> Option<BTreeMap<EnvKey, OsString>> {
50        if self.is_unchanged() { None } else { Some(self.capture()) }
51    }
52
53    // The following functions build up changes
54    pub fn set(&mut self, key: &OsStr, value: &OsStr) {
55        let key = EnvKey::from(key);
56        self.maybe_saw_path(&key);
57        self.vars.insert(key, Some(value.to_owned()));
58    }
59
60    pub fn remove(&mut self, key: &OsStr) {
61        let key = EnvKey::from(key);
62        self.maybe_saw_path(&key);
63        if self.clear {
64            self.vars.remove(&key);
65        } else {
66            self.vars.insert(key, None);
67        }
68    }
69
70    pub fn clear(&mut self) {
71        self.clear = true;
72        self.vars.clear();
73    }
74
75    pub fn does_clear(&self) -> bool {
76        self.clear
77    }
78
79    pub fn have_changed_path(&self) -> bool {
80        self.saw_path || self.clear
81    }
82
83    fn maybe_saw_path(&mut self, key: &EnvKey) {
84        if !self.saw_path && key == "PATH" {
85            self.saw_path = true;
86        }
87    }
88
89    pub fn iter(&self) -> CommandEnvs<'_> {
90        let iter = self.vars.iter();
91        CommandEnvs { iter }
92    }
93}
94
95/// An iterator over the command environment variables.
96///
97/// This struct is created by
98/// [`Command::get_envs`][crate::process::Command::get_envs]. See its
99/// documentation for more.
100#[must_use = "iterators are lazy and do nothing unless consumed"]
101#[stable(feature = "command_access", since = "1.57.0")]
102#[derive(Debug)]
103pub struct CommandEnvs<'a> {
104    iter: crate::collections::btree_map::Iter<'a, EnvKey, Option<OsString>>,
105}
106
107#[stable(feature = "command_access", since = "1.57.0")]
108impl<'a> Iterator for CommandEnvs<'a> {
109    type Item = (&'a OsStr, Option<&'a OsStr>);
110    fn next(&mut self) -> Option<Self::Item> {
111        self.iter.next().map(|(key, value)| (key.as_ref(), value.as_deref()))
112    }
113    fn size_hint(&self) -> (usize, Option<usize>) {
114        self.iter.size_hint()
115    }
116}
117
118#[stable(feature = "command_access", since = "1.57.0")]
119impl<'a> ExactSizeIterator for CommandEnvs<'a> {
120    fn len(&self) -> usize {
121        self.iter.len()
122    }
123    fn is_empty(&self) -> bool {
124        self.iter.is_empty()
125    }
126}
127
128pub fn wait_with_output(
129    mut process: Process,
130    mut pipes: StdioPipes,
131) -> io::Result<(ExitStatus, Vec<u8>, Vec<u8>)> {
132    drop(pipes.stdin.take());
133
134    let (mut stdout, mut stderr) = (Vec::new(), Vec::new());
135    match (pipes.stdout.take(), pipes.stderr.take()) {
136        (None, None) => {}
137        (Some(out), None) => {
138            let res = out.read_to_end(&mut stdout);
139            res.unwrap();
140        }
141        (None, Some(err)) => {
142            let res = err.read_to_end(&mut stderr);
143            res.unwrap();
144        }
145        (Some(out), Some(err)) => {
146            let res = read2(out, &mut stdout, err, &mut stderr);
147            res.unwrap();
148        }
149    }
150
151    let status = process.wait()?;
152    Ok((status, stdout, stderr))
153}