1use crate::env::{
2 AsyncIoEnvironment, ExportedVariableEnvironment, FileDescEnvironment, FileDescOpener, Pipe,
3 UnsetVariableEnvironment, VariableEnvironment,
4};
5use crate::io::Permissions;
6use crate::Fd;
7use futures_core::future::BoxFuture;
8use std::borrow::{Borrow, Cow};
9use std::collections::HashMap;
10use std::fs::OpenOptions;
11use std::hash::Hash;
12use std::io;
13use std::path::Path;
14
15pub trait Restorer<'a, E: 'a + ?Sized> {
18 fn get(&self) -> &E;
20
21 fn get_mut(&mut self) -> &mut E;
26}
27
28impl<'a, 'b, E, T> Restorer<'a, E> for &'b mut T
29where
30 T: 'b + ?Sized + Restorer<'a, E>,
31 E: 'a + ?Sized,
32{
33 fn get(&self) -> &E {
34 (**self).get()
35 }
36
37 fn get_mut(&mut self) -> &mut E {
38 (**self).get_mut()
39 }
40}
41
42pub trait VarEnvRestorer<'a, E: 'a + ?Sized + VariableEnvironment>:
45 VariableEnvironment<Var = E::Var, VarName = E::VarName> + Restorer<'a, E>
46{
47 fn reserve_vars(&mut self, additional: usize);
49
50 fn backup_var(&mut self, key: &E::VarName);
57
58 fn restore_vars(&mut self);
60
61 fn clear_vars(&mut self);
63}
64
65impl<'a, 'b, E, T> VarEnvRestorer<'a, E> for &'b mut T
66where
67 T: 'b + ?Sized + VarEnvRestorer<'a, E>,
68 E: 'a + ?Sized + VariableEnvironment,
69{
70 fn reserve_vars(&mut self, additional: usize) {
71 (**self).reserve_vars(additional)
72 }
73
74 fn backup_var(&mut self, key: &E::VarName) {
75 (**self).backup_var(key)
76 }
77
78 fn restore_vars(&mut self) {
79 (**self).restore_vars();
80 }
81
82 fn clear_vars(&mut self) {
83 (**self).clear_vars();
84 }
85}
86
87pub trait RedirectEnvRestorer<'a, E: 'a + ?Sized + FileDescEnvironment>:
90 FileDescEnvironment<FileHandle = E::FileHandle> + Restorer<'a, E>
91{
92 fn reserve_redirects(&mut self, additional: usize);
94
95 fn backup_redirect(&mut self, fd: Fd);
102
103 fn restore_redirects(&mut self);
105
106 fn clear_redirects(&mut self);
108}
109
110impl<'a, 'b, E, T> RedirectEnvRestorer<'a, E> for &'b mut T
111where
112 T: 'b + ?Sized + RedirectEnvRestorer<'a, E>,
113 E: 'a + ?Sized + FileDescEnvironment,
114{
115 fn reserve_redirects(&mut self, additional: usize) {
116 (**self).reserve_redirects(additional)
117 }
118
119 fn backup_redirect(&mut self, fd: Fd) {
120 (**self).backup_redirect(fd);
121 }
122
123 fn restore_redirects(&mut self) {
124 (**self).restore_redirects();
125 }
126
127 fn clear_redirects(&mut self) {
128 (**self).clear_redirects();
129 }
130}
131
132#[derive(Debug, PartialEq)]
135pub struct EnvRestorer<'a, E>
136where
137 E: ?Sized + ExportedVariableEnvironment + FileDescEnvironment + UnsetVariableEnvironment,
138 E::FileHandle: Clone,
139 E::VarName: Clone,
140 E::Var: Clone,
141{
142 env: &'a mut E,
143 var_overrides: HashMap<E::VarName, Option<(E::Var, bool)>>,
144 redirect_overrides: HashMap<Fd, Option<(E::FileHandle, Permissions)>>,
145}
146
147impl<'a, E> EnvRestorer<'a, E>
148where
149 E: ?Sized + ExportedVariableEnvironment + FileDescEnvironment + UnsetVariableEnvironment,
150 E::FileHandle: Clone,
151 E::VarName: Clone,
152 E::Var: Clone,
153{
154 pub fn new(env: &'a mut E) -> Self {
156 Self {
157 env,
158 var_overrides: HashMap::new(),
159 redirect_overrides: HashMap::new(),
160 }
161 }
162}
163
164impl<'a, E> Restorer<'a, E> for EnvRestorer<'a, E>
165where
166 E: ?Sized + ExportedVariableEnvironment + FileDescEnvironment + UnsetVariableEnvironment,
167 E::FileHandle: Clone,
168 E::VarName: Clone,
169 E::Var: Clone,
170{
171 fn get(&self) -> &E {
172 &*self.env
173 }
174
175 fn get_mut(&mut self) -> &mut E {
176 &mut self.env
177 }
178}
179
180impl<'a, E> Drop for EnvRestorer<'a, E>
181where
182 E: ?Sized + ExportedVariableEnvironment + FileDescEnvironment + UnsetVariableEnvironment,
183 E::FileHandle: Clone,
184 E::VarName: Clone,
185 E::Var: Clone,
186{
187 fn drop(&mut self) {
188 self.restore_vars();
189 self.restore_redirects();
190 }
191}
192
193impl<'a, E> VarEnvRestorer<'a, E> for EnvRestorer<'a, E>
194where
195 E: ?Sized + ExportedVariableEnvironment + FileDescEnvironment + UnsetVariableEnvironment,
196 E::FileHandle: Clone,
197 E::VarName: Clone,
198 E::Var: Clone,
199{
200 fn reserve_vars(&mut self, additional: usize) {
201 self.var_overrides.reserve(additional);
202 }
203
204 fn backup_var(&mut self, key: &E::VarName) {
205 let value = self.env.exported_var(key);
206 self.var_overrides
207 .entry(key.clone())
208 .or_insert_with(|| value.map(|(val, exported)| (val.clone(), exported)));
209 }
210
211 fn restore_vars(&mut self) {
212 for (key, val) in self.var_overrides.drain() {
213 match val {
214 Some((val, exported)) => self.env.set_exported_var(key, val, exported),
215 None => self.env.unset_var(&key),
216 }
217 }
218 }
219
220 fn clear_vars(&mut self) {
221 self.var_overrides.clear();
222 }
223}
224
225impl<'a, E> VariableEnvironment for EnvRestorer<'a, E>
226where
227 E: ?Sized
228 + VariableEnvironment
229 + ExportedVariableEnvironment
230 + UnsetVariableEnvironment
231 + FileDescEnvironment,
232 E::FileHandle: Clone,
233 E::VarName: Clone,
234 E::Var: Clone,
235{
236 type VarName = E::VarName;
237 type Var = E::Var;
238
239 fn var<Q: ?Sized>(&self, name: &Q) -> Option<&Self::Var>
240 where
241 Self::VarName: Borrow<Q>,
242 Q: Hash + Eq,
243 {
244 self.env.var(name)
245 }
246
247 fn set_var(&mut self, name: Self::VarName, val: Self::Var) {
248 self.backup_var(&name);
249 self.env.set_var(name, val);
250 }
251
252 fn env_vars(&self) -> Cow<'_, [(&Self::VarName, &Self::Var)]> {
253 self.env.env_vars()
254 }
255}
256
257impl<'a, E> ExportedVariableEnvironment for EnvRestorer<'a, E>
258where
259 E: ?Sized
260 + VariableEnvironment
261 + ExportedVariableEnvironment
262 + UnsetVariableEnvironment
263 + FileDescEnvironment,
264 E::FileHandle: Clone,
265 E::VarName: Clone,
266 E::Var: Clone,
267{
268 fn exported_var(&self, name: &Self::VarName) -> Option<(&Self::Var, bool)> {
269 self.env.exported_var(name)
270 }
271
272 fn set_exported_var(&mut self, name: Self::VarName, val: Self::Var, exported: bool) {
273 self.backup_var(&name);
274 self.env.set_exported_var(name, val, exported)
275 }
276}
277
278impl<'a, E> UnsetVariableEnvironment for EnvRestorer<'a, E>
279where
280 E: ?Sized
281 + VariableEnvironment
282 + ExportedVariableEnvironment
283 + UnsetVariableEnvironment
284 + FileDescEnvironment,
285 E::FileHandle: Clone,
286 E::VarName: Clone,
287 E::Var: Clone,
288{
289 fn unset_var(&mut self, name: &E::VarName) {
290 self.backup_var(name);
291 self.env.unset_var(name);
292 }
293}
294
295impl<'a, E> FileDescEnvironment for EnvRestorer<'a, E>
296where
297 E: ?Sized + ExportedVariableEnvironment + FileDescEnvironment + UnsetVariableEnvironment,
298 E::FileHandle: Clone,
299 E::VarName: Clone,
300 E::Var: Clone,
301{
302 type FileHandle = E::FileHandle;
303
304 fn file_desc(&self, fd: Fd) -> Option<(&Self::FileHandle, Permissions)> {
305 self.env.file_desc(fd)
306 }
307
308 fn set_file_desc(&mut self, fd: Fd, handle: Self::FileHandle, perms: Permissions) {
309 self.backup_redirect(fd);
310 self.env.set_file_desc(fd, handle, perms)
311 }
312
313 fn close_file_desc(&mut self, fd: Fd) {
314 self.backup_redirect(fd);
315 self.env.close_file_desc(fd)
316 }
317}
318
319impl<'a, E> FileDescOpener for EnvRestorer<'a, E>
320where
321 E: ?Sized
322 + ExportedVariableEnvironment
323 + FileDescEnvironment
324 + FileDescOpener
325 + UnsetVariableEnvironment,
326 E::FileHandle: Clone,
327 E::VarName: Clone,
328 E::Var: Clone,
329{
330 type OpenedFileHandle = E::OpenedFileHandle;
331
332 fn open_path(&mut self, path: &Path, opts: &OpenOptions) -> io::Result<Self::OpenedFileHandle> {
333 self.env.open_path(path, opts)
334 }
335
336 fn open_pipe(&mut self) -> io::Result<Pipe<Self::OpenedFileHandle>> {
337 self.env.open_pipe()
338 }
339}
340
341impl<'b, E> AsyncIoEnvironment for EnvRestorer<'b, E>
342where
343 E: ?Sized
344 + AsyncIoEnvironment
345 + ExportedVariableEnvironment
346 + FileDescEnvironment
347 + UnsetVariableEnvironment,
348 E::FileHandle: Clone,
349 E::VarName: Clone,
350 E::Var: Clone,
351{
352 type IoHandle = E::IoHandle;
353
354 fn read_all(&mut self, fd: Self::IoHandle) -> BoxFuture<'static, io::Result<Vec<u8>>> {
355 self.env.read_all(fd)
356 }
357
358 fn write_all<'a>(
359 &mut self,
360 fd: Self::IoHandle,
361 data: Cow<'a, [u8]>,
362 ) -> BoxFuture<'a, io::Result<()>> {
363 self.env.write_all(fd, data)
364 }
365
366 fn write_all_best_effort(&mut self, fd: Self::IoHandle, data: Vec<u8>) {
367 self.env.write_all_best_effort(fd, data);
368 }
369}
370
371impl<'a, E> RedirectEnvRestorer<'a, E> for EnvRestorer<'a, E>
372where
373 E: ?Sized + ExportedVariableEnvironment + FileDescEnvironment + UnsetVariableEnvironment,
374 E::FileHandle: Clone,
375 E::VarName: Clone,
376 E::Var: Clone,
377{
378 fn reserve_redirects(&mut self, additional: usize) {
379 self.redirect_overrides.reserve(additional);
380 }
381
382 fn backup_redirect(&mut self, fd: Fd) {
383 let Self {
384 redirect_overrides,
385 env,
386 ..
387 } = self;
388 redirect_overrides.entry(fd).or_insert_with(|| {
389 env.file_desc(fd)
390 .map(|(handle, perms)| (handle.clone(), perms))
391 });
392 }
393
394 fn restore_redirects(&mut self) {
395 for (fd, backup) in self.redirect_overrides.drain() {
396 match backup {
397 Some((handle, perms)) => self.env.set_file_desc(fd, handle, perms),
398 None => self.env.close_file_desc(fd),
399 }
400 }
401 }
402
403 fn clear_redirects(&mut self) {
404 self.redirect_overrides.clear();
405 }
406}