conch_runtime_pshaw/env/
restorer.rs

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
15/// A base interface for any environment wrappers which track changes
16/// such that they can be undone later.
17pub trait Restorer<'a, E: 'a + ?Sized> {
18    /// Get a reference to the original environment.
19    fn get(&self) -> &E;
20
21    /// Get a mutable reference to the original environment.
22    ///
23    /// Note that any modifications done through a reference
24    /// to the original environment will *not* be backed up.
25    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
42/// An interface for wrapping an environment and maintaining a state of all variable
43/// definitions that have been modified so that they can be restored later.
44pub trait VarEnvRestorer<'a, E: 'a + ?Sized + VariableEnvironment>:
45    VariableEnvironment<Var = E::Var, VarName = E::VarName> + Restorer<'a, E>
46{
47    /// Reserves capacity for at least `additional` more variables to be backed up.
48    fn reserve_vars(&mut self, additional: usize);
49
50    /// Backs up the original value of specified variable.
51    ///
52    /// The original value of the variable is the one the environment
53    /// held before it was passed into this wrapper. That is, if a variable
54    /// is backed up multiple times, only the value before the first
55    /// call should be restored later.
56    fn backup_var(&mut self, key: &E::VarName);
57
58    /// Restore all variable definitions to their original state.
59    fn restore_vars(&mut self);
60
61    /// Forget any variables backed up to this point.
62    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
87/// An interface for wrapping an environment and maintaining a state of all file descriptors
88/// that have been modified so that they can be restored later.
89pub trait RedirectEnvRestorer<'a, E: 'a + ?Sized + FileDescEnvironment>:
90    FileDescEnvironment<FileHandle = E::FileHandle> + Restorer<'a, E>
91{
92    /// Reserves capacity for at least `additional` more redirects to be backed up.
93    fn reserve_redirects(&mut self, additional: usize);
94
95    /// Backs up the original value of specified variable.
96    ///
97    /// The original value of the variable is the one the environment
98    /// held before it was passed into this wrapper. That is, if a variable
99    /// is backed up multiple times, only the value before the first
100    /// call should be restored later.
101    fn backup_redirect(&mut self, fd: Fd);
102
103    /// Restore all redirects to their original state.
104    fn restore_redirects(&mut self);
105
106    /// Forget any redirects backed up to this point.
107    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/// Maintains a state of environment modifications so that
133/// they can be restored later, either on drop or on demand.
134#[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    /// Create a new restorer.
155    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}