1use std::ffi::OsString;
2
3#[derive(Clone, Debug, PartialEq, Eq)]
5pub struct Environment {
6 vars: Vec<(OsString, OsString)>,
8 inherit: bool,
10}
11
12impl Default for Environment {
13 fn default() -> Self {
14 Self {
15 vars: vec![],
16 inherit: false,
17 }
18 }
19}
20
21impl Environment {
22 pub fn inherit() -> Self {
36 Self {
37 vars: vec![],
38 inherit: true,
39 }
40 }
41
42 pub fn empty() -> Self {
53 Self::default()
54 }
55
56 pub fn insert<S1: Into<OsString>, S2: Into<OsString>>(mut self, key: S1, val: S2) -> Self {
69 self.vars.push((key.into(), val.into()));
70 self
71 }
72
73 pub fn compile(self) -> Vec<(OsString, OsString)> {
75 if self.inherit {
76 ::std::env::vars_os().chain(self.vars).collect()
77 } else {
78 self.vars
79 }
80 }
81}
82
83impl<'a> From<&'a Environment> for Environment {
85 fn from(v: &'a Environment) -> Self {
86 v.clone()
87 }
88}
89
90pub trait EnvironmentItem {
91 fn to_environment_tuple(&self) -> (OsString, OsString);
92}
93
94impl<T: ToString, Z: ToString> EnvironmentItem for (T, Z) {
95 fn to_environment_tuple(&self) -> (OsString, OsString) {
96 (
97 OsString::from(self.0.to_string()),
98 OsString::from(self.1.to_string()),
99 )
100 }
101}
102
103impl<'s, T: ToString, Z: ToString> EnvironmentItem for &'s (T, Z) {
104 fn to_environment_tuple(&self) -> (OsString, OsString) {
105 (
106 OsString::from(self.0.to_string()),
107 OsString::from(self.1.to_string()),
108 )
109 }
110}
111
112impl<'s, T> From<T> for Environment
113where
114 T: IntoIterator,
115 T::Item: EnvironmentItem,
116{
117 fn from(v: T) -> Self {
118 Self {
119 vars: v.into_iter().map(|k| k.to_environment_tuple()).collect(),
120 inherit: false,
121 }
122 }
123}
124
125#[cfg(test)]
126mod test {
127 use super::*;
128 use std::process::Command;
129
130 #[test]
131 fn take_ownership() {
132 let x = Environment::inherit();
133
134 let mut c = Command::new("printenv");
135 c.env_clear().envs(x.clone().compile()).envs(x.compile());
136 }
137
138 #[test]
139 fn in_place_mod() {
140 let y = Environment::empty();
141
142 let y = y.insert("key", "value");
143
144 assert_eq!(
145 y.compile(),
146 vec![(OsString::from("key"), OsString::from("value"))]
147 );
148 }
149
150 #[test]
151 fn in_place_mod2() {
152 let x = Environment::inherit();
153
154 let mut c = Command::new("printenv");
155
156 let output = c.env_clear()
157 .envs(x.insert("key", "value").insert("key", "vv").compile())
158 .output()
159 .expect("failed to execute command");
160
161 let output = String::from_utf8_lossy(&output.stdout);
162
163 assert!(output.contains("key=vv"));
164 }
167
168 #[test]
169 fn in_place_mod3() {
170 let y = Environment::empty();
172
173 assert_eq!(
174 y.clone().insert("key", "value").compile(),
175 vec![(OsString::from("key"), OsString::from("value"))]
176 );
177
178 let mut c = Command::new("printenv");
179
180 let output = c.env_clear()
181 .envs(y.compile())
182 .output()
183 .expect("failed to execute command");
184
185 let output = String::from_utf8_lossy(&output.stdout);
186
187 assert_eq!(output, "");
188 }
189
190 #[test]
191 fn empty_env() {
192 let y = Environment::empty();
194
195 let mut c = Command::new("printenv");
196
197 let output = c.env_clear()
198 .envs(y.compile())
199 .output()
200 .expect("failed to execute command");
201
202 let output = String::from_utf8_lossy(&output.stdout);
203
204 assert!(output.is_empty());
205 }
206
207 #[test]
208 fn take_vec() {
209 let v = vec![("bar", "baz")];
210
211 let e: Environment = v.into();
212
213 let mut c = Command::new("printenv");
214
215 let output = c.env_clear()
216 .envs(e.clone().compile())
217 .output()
218 .expect("failed to execute command");
219
220 let output = String::from_utf8_lossy(&output.stdout);
221
222 assert!(output.contains("bar=baz"));
223
224 let mut c = Command::new("printenv");
225
226 let output = c.env_clear()
227 .envs(e.clone().insert("bar", "vv").compile())
228 .output()
229 .expect("failed to execute command");
230
231 let output = String::from_utf8_lossy(&output.stdout);
232
233 assert!(output.contains("bar=vv"));
234 assert!(!output.contains("bar=baz"));
235 }
236}