conch_runtime_pshaw/env/
env_impl.rs

1// FIXME: consider removing all these generics here and only offer a concrete env (generic over word types and Fn errors)
2// FIXME: consumers still have all the pieces so they can make their own environment and swap out pieces there
3// FIXME: downside is any unit tests which want a mock env, will need to basically do the same
4use crate::env::builtin::{BuiltinEnv, BuiltinEnvironment};
5use crate::env::{
6    ArgsEnv, ArgumentsEnvironment, AsyncIoEnvironment, ChangeWorkingDirectoryEnvironment,
7    ExecutableData, ExecutableEnvironment, ExportedVariableEnvironment, FileDescEnvironment,
8    FileDescOpener, FnEnv, FnFrameEnv, FunctionEnvironment, FunctionFrameEnvironment,
9    IsInteractiveEnvironment, LastStatusEnv, LastStatusEnvironment, Pipe, ReportErrorEnvironment,
10    SetArgumentsEnvironment, ShiftArgumentsEnvironment, StringWrapper, SubEnvironment,
11    TokioExecEnv, TokioFileDescManagerEnv, UnsetFunctionEnvironment, UnsetVariableEnvironment,
12    VarEnv, VariableEnvironment, VirtualWorkingDirEnv, WorkingDirectoryEnvironment,
13};
14use crate::error::{CommandError, RuntimeError};
15use crate::io::Permissions;
16use crate::{ExitStatus, Fd, Spawn, IFS_DEFAULT, STDERR_FILENO};
17use futures_core::future::BoxFuture;
18use std::borrow::{Borrow, Cow};
19use std::convert::From;
20use std::error::Error;
21use std::fmt;
22use std::fs::OpenOptions;
23use std::hash::Hash;
24use std::io;
25use std::marker::PhantomData;
26use std::path::Path;
27use std::sync::Arc;
28
29/// A struct for configuring a new `Env` instance.
30///
31/// It implements `Default` (via `DefaultEnvConfig` alias) so it is possible
32/// to selectively override certain environment modules while retaining the rest
33/// of the default implementations.
34///
35/// ```
36/// # use std::sync::Arc;
37/// # use conch_runtime::env::{ArgsEnv, ArgumentsEnvironment, DefaultEnvConfig, Env, EnvConfig};
38/// let env = Env::with_config(EnvConfig {
39///     args_env: ArgsEnv::with_name(Arc::new(String::from("my_shell"))),
40///     .. DefaultEnvConfig::new().expect("failed to create config")
41/// });
42///
43/// assert_eq!(**env.name(), "my_shell");
44/// ```
45#[derive(Default, Debug, PartialEq, Eq, Clone)]
46pub struct EnvConfig<A, FM, L, V, EX, WD, B, N, ERR> {
47    /// Specify if the environment is running in interactive mode.
48    pub interactive: bool,
49    /// An implementation of `ArgumentsEnvironment` and possibly `SetArgumentsEnvironment`.
50    pub args_env: A,
51    /// An implementation of `FileDescManagerEnvironment`.
52    pub file_desc_manager_env: FM,
53    /// An implementation of `LastStatusEnvironment`.
54    pub last_status_env: L,
55    /// An implementation of `VariableEnvironment`, `UnsetVariableEnvironment`, and
56    /// `ExportedVariableEnvironment`.
57    pub var_env: V,
58    /// An implementation of `ExecutableEnvironment`.
59    pub exec_env: EX,
60    /// An implementation of `WorkingDirectoryEnvironment`.
61    pub working_dir_env: WD,
62    /// An implementation of `BuiltinEnvironment`.
63    pub builtin_env: B,
64    /// A marker to indicate the type used for function names.
65    pub fn_name: PhantomData<N>,
66    /// A marker to indicate the type used for function errors.
67    pub fn_error: PhantomData<ERR>,
68}
69
70impl<A, FM, L, V, EX, WD, B, N, ERR> EnvConfig<A, FM, L, V, EX, WD, B, N, ERR> {
71    /// Change the type of the `args_env` instance.
72    pub fn change_args_env<T>(self, args_env: T) -> EnvConfig<T, FM, L, V, EX, WD, B, N, ERR> {
73        EnvConfig {
74            interactive: self.interactive,
75            args_env,
76            file_desc_manager_env: self.file_desc_manager_env,
77            last_status_env: self.last_status_env,
78            var_env: self.var_env,
79            exec_env: self.exec_env,
80            working_dir_env: self.working_dir_env,
81            builtin_env: self.builtin_env,
82            fn_name: self.fn_name,
83            fn_error: self.fn_error,
84        }
85    }
86
87    /// Change the type of the `file_desc_manager_env` instance.
88    pub fn change_file_desc_manager_env<T>(
89        self,
90        file_desc_manager_env: T,
91    ) -> EnvConfig<A, T, L, V, EX, WD, B, N, ERR> {
92        EnvConfig {
93            interactive: self.interactive,
94            args_env: self.args_env,
95            file_desc_manager_env,
96            last_status_env: self.last_status_env,
97            var_env: self.var_env,
98            exec_env: self.exec_env,
99            working_dir_env: self.working_dir_env,
100            builtin_env: self.builtin_env,
101            fn_name: self.fn_name,
102            fn_error: self.fn_error,
103        }
104    }
105
106    /// Change the type of the `last_status_env` instance.
107    pub fn change_last_status_env<T>(
108        self,
109        last_status_env: T,
110    ) -> EnvConfig<A, FM, T, V, EX, WD, B, N, ERR> {
111        EnvConfig {
112            interactive: self.interactive,
113            args_env: self.args_env,
114            file_desc_manager_env: self.file_desc_manager_env,
115            last_status_env,
116            var_env: self.var_env,
117            exec_env: self.exec_env,
118            working_dir_env: self.working_dir_env,
119            builtin_env: self.builtin_env,
120            fn_name: self.fn_name,
121            fn_error: self.fn_error,
122        }
123    }
124
125    /// Change the type of the `var_env` instance.
126    pub fn change_var_env<T>(self, var_env: T) -> EnvConfig<A, FM, L, T, EX, WD, B, N, ERR> {
127        EnvConfig {
128            interactive: self.interactive,
129            args_env: self.args_env,
130            file_desc_manager_env: self.file_desc_manager_env,
131            last_status_env: self.last_status_env,
132            var_env,
133            exec_env: self.exec_env,
134            working_dir_env: self.working_dir_env,
135            builtin_env: self.builtin_env,
136            fn_name: self.fn_name,
137            fn_error: self.fn_error,
138        }
139    }
140
141    /// Change the type of the `exec_env` instance.
142    pub fn change_exec_env<T>(self, exec_env: T) -> EnvConfig<A, FM, L, V, T, WD, B, N, ERR> {
143        EnvConfig {
144            interactive: self.interactive,
145            args_env: self.args_env,
146            file_desc_manager_env: self.file_desc_manager_env,
147            last_status_env: self.last_status_env,
148            var_env: self.var_env,
149            exec_env,
150            working_dir_env: self.working_dir_env,
151            builtin_env: self.builtin_env,
152            fn_name: self.fn_name,
153            fn_error: self.fn_error,
154        }
155    }
156
157    /// Change the type of the `working_dir_env` instance.
158    pub fn change_working_dir_env<T>(
159        self,
160        working_dir_env: T,
161    ) -> EnvConfig<A, FM, L, V, EX, T, B, N, ERR> {
162        EnvConfig {
163            interactive: self.interactive,
164            args_env: self.args_env,
165            file_desc_manager_env: self.file_desc_manager_env,
166            last_status_env: self.last_status_env,
167            var_env: self.var_env,
168            exec_env: self.exec_env,
169            working_dir_env,
170            builtin_env: self.builtin_env,
171            fn_name: self.fn_name,
172            fn_error: self.fn_error,
173        }
174    }
175
176    /// Change the type of the `builtin_env` instance.
177    pub fn change_builtin_env<T>(
178        self,
179        builtin_env: T,
180    ) -> EnvConfig<A, FM, L, V, EX, WD, T, N, ERR> {
181        EnvConfig {
182            interactive: self.interactive,
183            args_env: self.args_env,
184            file_desc_manager_env: self.file_desc_manager_env,
185            last_status_env: self.last_status_env,
186            var_env: self.var_env,
187            exec_env: self.exec_env,
188            working_dir_env: self.working_dir_env,
189            builtin_env,
190            fn_name: self.fn_name,
191            fn_error: self.fn_error,
192        }
193    }
194
195    /// Change the type of the `fn_name` instance.
196    pub fn change_fn_name<T>(self) -> EnvConfig<A, FM, L, V, EX, WD, B, T, ERR> {
197        EnvConfig {
198            interactive: self.interactive,
199            args_env: self.args_env,
200            file_desc_manager_env: self.file_desc_manager_env,
201            last_status_env: self.last_status_env,
202            var_env: self.var_env,
203            exec_env: self.exec_env,
204            working_dir_env: self.working_dir_env,
205            builtin_env: self.builtin_env,
206            fn_name: PhantomData,
207            fn_error: self.fn_error,
208        }
209    }
210
211    /// Change the type of the `fn_error` instance.
212    pub fn change_fn_error<T>(self) -> EnvConfig<A, FM, L, V, EX, WD, B, N, T> {
213        EnvConfig {
214            interactive: self.interactive,
215            args_env: self.args_env,
216            file_desc_manager_env: self.file_desc_manager_env,
217            last_status_env: self.last_status_env,
218            var_env: self.var_env,
219            exec_env: self.exec_env,
220            working_dir_env: self.working_dir_env,
221            builtin_env: self.builtin_env,
222            fn_name: self.fn_name,
223            fn_error: PhantomData,
224        }
225    }
226}
227
228/// A default environment configuration using provided (non-atomic) implementations,
229/// and powered by `tokio`.
230///
231/// Generic over the representation of shell words, variables, function names, etc.
232pub type DefaultEnvConfig<T> = EnvConfig<
233    ArgsEnv<T>,
234    TokioFileDescManagerEnv,
235    LastStatusEnv,
236    VarEnv<T, T>,
237    TokioExecEnv,
238    VirtualWorkingDirEnv,
239    BuiltinEnv<T>,
240    T,
241    RuntimeError,
242>;
243
244/// A default environment configuration using provided implementations
245/// and `Arc<String>` to represent shell values.
246pub type DefaultEnvConfigArc = DefaultEnvConfig<Arc<String>>;
247
248impl<T> DefaultEnvConfig<T>
249where
250    T: Eq + Hash + From<String>,
251{
252    /// Creates a new `DefaultEnvConfig` using default environment components.
253    pub fn new() -> io::Result<Self> {
254        let file_desc_manager_env = TokioFileDescManagerEnv::with_process_stdio()?;
255
256        Ok(DefaultEnvConfig {
257            interactive: false,
258            args_env: ArgsEnv::new(),
259            file_desc_manager_env,
260            last_status_env: LastStatusEnv::new(),
261            var_env: VarEnv::with_process_env_vars(),
262            exec_env: TokioExecEnv::new(),
263            working_dir_env: VirtualWorkingDirEnv::with_process_working_dir()?,
264            builtin_env: BuiltinEnv::new(),
265            fn_name: PhantomData,
266            fn_error: PhantomData,
267        })
268    }
269}
270
271/// A shell environment implementation which delegates work to other
272/// environment implementations.
273pub struct Env<A, FM, L, V, EX, WD, B, N: Eq + Hash, ERR> {
274    /// If the shell is running in interactive mode
275    interactive: bool,
276    args_env: A,
277    file_desc_manager_env: FM,
278    #[allow(clippy::type_complexity)]
279    fn_env:
280        FnEnv<N, Arc<dyn Spawn<Env<A, FM, L, V, EX, WD, B, N, ERR>, Error = ERR> + Send + Sync>>,
281    fn_frame_env: FnFrameEnv,
282    last_status_env: L,
283    var_env: V,
284    exec_env: EX,
285    working_dir_env: WD,
286    builtin_env: B,
287}
288
289impl<A, FM, L, V, EX, WD, B, N, ERR> Env<A, FM, L, V, EX, WD, B, N, ERR>
290where
291    N: Hash + Eq,
292{
293    /// Creates an environment using the provided configuration of subcomponents.
294    ///
295    /// See `EnvConfig` for the kinds of overrides possible. `DefaultEnvConfig`
296    /// comes with provided implementations to get you up and running.
297    ///
298    /// General recommendations:
299    ///
300    /// * The result of evaluating a shell word will often be copied and reused
301    /// in many different places. It's strongly recommened that an `Arc`
302    /// wrapper (e.g. `Arc<String>`) is used to minimize having to reallocate
303    /// and copy the same data.
304    /// * Whatever type represents a shell function body needs to be cloned to
305    /// get around borrow restrictions and potential recursive executions and
306    /// (re-)definitions. Since this type is probably an AST (which may be
307    /// arbitrarily large), `Arc` is your friend.
308    pub fn with_config(cfg: EnvConfig<A, FM, L, V, EX, WD, B, N, ERR>) -> Self
309    where
310        V: ExportedVariableEnvironment,
311        V::VarName: From<String>,
312        V::Var: Borrow<String> + From<String> + Clone,
313        WD: WorkingDirectoryEnvironment,
314    {
315        let mut env = Env {
316            interactive: cfg.interactive,
317            args_env: cfg.args_env,
318            fn_env: FnEnv::new(),
319            fn_frame_env: FnFrameEnv::new(),
320            file_desc_manager_env: cfg.file_desc_manager_env,
321            last_status_env: cfg.last_status_env,
322            var_env: cfg.var_env,
323            exec_env: cfg.exec_env,
324            working_dir_env: cfg.working_dir_env,
325            builtin_env: cfg.builtin_env,
326        };
327
328        let sh_lvl = "SHLVL".to_owned().into();
329        let level = env
330            .var(&sh_lvl)
331            .and_then(|lvl| lvl.borrow().parse::<isize>().ok().map(|l| l + 1))
332            .unwrap_or(1)
333            .to_string()
334            .into();
335
336        let cwd: V::Var = env
337            .current_working_dir()
338            .to_string_lossy()
339            .into_owned()
340            .into();
341
342        env.set_exported_var(sh_lvl, level, true);
343        env.set_exported_var("PWD".to_owned().into(), cwd.clone(), true);
344        env.set_exported_var("OLDPWD".to_owned().into(), cwd, true);
345        env.set_var("IFS".to_owned().into(), IFS_DEFAULT.to_owned().into());
346        env
347    }
348}
349
350impl<A, FM, L, V, EX, WD, B, N, ERR> Clone for Env<A, FM, L, V, EX, WD, B, N, ERR>
351where
352    A: Clone,
353    FM: Clone,
354    L: Clone,
355    V: Clone,
356    B: Clone,
357    N: Hash + Eq,
358    EX: Clone,
359    WD: Clone,
360{
361    fn clone(&self) -> Self {
362        Env {
363            interactive: self.interactive,
364            args_env: self.args_env.clone(),
365            file_desc_manager_env: self.file_desc_manager_env.clone(),
366            fn_env: self.fn_env.clone(),
367            fn_frame_env: self.fn_frame_env,
368            last_status_env: self.last_status_env.clone(),
369            var_env: self.var_env.clone(),
370            exec_env: self.exec_env.clone(),
371            working_dir_env: self.working_dir_env.clone(),
372            builtin_env: self.builtin_env.clone(),
373        }
374    }
375}
376
377impl<A, FM, L, V, EX, WD, B, N, ERR> fmt::Debug for Env<A, FM, L, V, EX, WD, B, N, ERR>
378where
379    A: fmt::Debug,
380    FM: fmt::Debug,
381    L: fmt::Debug,
382    V: fmt::Debug,
383    B: fmt::Debug,
384    N: Hash + Eq + Ord + fmt::Debug,
385    EX: fmt::Debug,
386    WD: fmt::Debug,
387{
388    fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
389        use std::collections::BTreeSet;
390        let fn_names: BTreeSet<_> = self.fn_env.fn_names().collect();
391
392        fmt.debug_struct(stringify!(Env))
393            .field("interactive", &self.interactive)
394            .field("args_env", &self.args_env)
395            .field("file_desc_manager_env", &self.file_desc_manager_env)
396            .field("functions", &fn_names)
397            .field("fn_frame_env", &self.fn_frame_env)
398            .field("last_status_env", &self.last_status_env)
399            .field("var_env", &self.var_env)
400            .field("exec_env", &self.exec_env)
401            .field("working_dir_env", &self.working_dir_env)
402            .field("builtin_env", &self.builtin_env)
403            .finish()
404    }
405}
406
407impl<A, FM, L, V, EX, WD, B, N, ERR> From<EnvConfig<A, FM, L, V, EX, WD, B, N, ERR>>
408    for Env<A, FM, L, V, EX, WD, B, N, ERR>
409where
410    N: Hash + Eq,
411    V: ExportedVariableEnvironment,
412    V::VarName: From<String>,
413    V::Var: Borrow<String> + From<String> + Clone,
414    WD: WorkingDirectoryEnvironment,
415{
416    fn from(cfg: EnvConfig<A, FM, L, V, EX, WD, B, N, ERR>) -> Self {
417        Self::with_config(cfg)
418    }
419}
420
421impl<A, FM, L, V, EX, WD, B, N, ERR> IsInteractiveEnvironment
422    for Env<A, FM, L, V, EX, WD, B, N, ERR>
423where
424    N: Hash + Eq,
425{
426    fn is_interactive(&self) -> bool {
427        self.interactive
428    }
429}
430
431impl<A, FM, L, V, EX, WD, B, N, ERR> SubEnvironment for Env<A, FM, L, V, EX, WD, B, N, ERR>
432where
433    A: SubEnvironment,
434    FM: SubEnvironment,
435    L: SubEnvironment,
436    V: SubEnvironment,
437    B: SubEnvironment,
438    N: Hash + Eq,
439    EX: SubEnvironment,
440    WD: SubEnvironment,
441{
442    fn sub_env(&self) -> Self {
443        Env {
444            interactive: self.is_interactive(),
445            args_env: self.args_env.sub_env(),
446            file_desc_manager_env: self.file_desc_manager_env.sub_env(),
447            fn_env: self.fn_env.sub_env(),
448            fn_frame_env: self.fn_frame_env.sub_env(),
449            last_status_env: self.last_status_env.sub_env(),
450            var_env: self.var_env.sub_env(),
451            exec_env: self.exec_env.sub_env(),
452            working_dir_env: self.working_dir_env.sub_env(),
453            builtin_env: self.builtin_env.sub_env(),
454        }
455    }
456}
457
458impl<A, FM, L, V, EX, WD, B, N, ERR> ArgumentsEnvironment for Env<A, FM, L, V, EX, WD, B, N, ERR>
459where
460    A: ArgumentsEnvironment,
461    A::Arg: Clone,
462    N: Hash + Eq,
463{
464    type Arg = A::Arg;
465
466    fn name(&self) -> &Self::Arg {
467        self.args_env.name()
468    }
469
470    fn arg(&self, idx: usize) -> Option<&Self::Arg> {
471        self.args_env.arg(idx)
472    }
473
474    fn args_len(&self) -> usize {
475        self.args_env.args_len()
476    }
477
478    fn args(&self) -> Cow<'_, [Self::Arg]> {
479        self.args_env.args()
480    }
481}
482
483impl<A, FM, L, V, EX, WD, B, N, ERR> SetArgumentsEnvironment for Env<A, FM, L, V, EX, WD, B, N, ERR>
484where
485    A: SetArgumentsEnvironment,
486    N: Hash + Eq,
487{
488    type Args = A::Args;
489
490    fn set_args(&mut self, new_args: Self::Args) -> Self::Args {
491        self.args_env.set_args(new_args)
492    }
493}
494
495impl<A, FM, L, V, EX, WD, B, N, ERR> ShiftArgumentsEnvironment
496    for Env<A, FM, L, V, EX, WD, B, N, ERR>
497where
498    A: ShiftArgumentsEnvironment,
499    N: Hash + Eq,
500{
501    fn shift_args(&mut self, amt: usize) {
502        self.args_env.shift_args(amt)
503    }
504}
505
506impl<A, FM, L, V, EX, WD, B, N, ERR> AsyncIoEnvironment for Env<A, FM, L, V, EX, WD, B, N, ERR>
507where
508    FM: AsyncIoEnvironment,
509    N: Hash + Eq,
510{
511    type IoHandle = FM::IoHandle;
512
513    fn read_all(&mut self, fd: Self::IoHandle) -> BoxFuture<'static, io::Result<Vec<u8>>> {
514        self.file_desc_manager_env.read_all(fd)
515    }
516
517    fn write_all<'a>(
518        &mut self,
519        fd: Self::IoHandle,
520        data: Cow<'a, [u8]>,
521    ) -> BoxFuture<'a, io::Result<()>> {
522        self.file_desc_manager_env.write_all(fd, data)
523    }
524
525    fn write_all_best_effort(&mut self, fd: Self::IoHandle, data: Vec<u8>) {
526        self.file_desc_manager_env.write_all_best_effort(fd, data);
527    }
528}
529
530impl<A, FM, L, V, EX, WD, B, N, ERR> FileDescEnvironment for Env<A, FM, L, V, EX, WD, B, N, ERR>
531where
532    FM: FileDescEnvironment,
533    N: Hash + Eq,
534{
535    type FileHandle = FM::FileHandle;
536
537    fn file_desc(&self, fd: Fd) -> Option<(&Self::FileHandle, Permissions)> {
538        self.file_desc_manager_env.file_desc(fd)
539    }
540
541    fn set_file_desc(&mut self, fd: Fd, fdes: Self::FileHandle, perms: Permissions) {
542        self.file_desc_manager_env.set_file_desc(fd, fdes, perms)
543    }
544
545    fn close_file_desc(&mut self, fd: Fd) {
546        self.file_desc_manager_env.close_file_desc(fd)
547    }
548}
549
550impl<A, FM, L, V, EX, WD, B, N, ERR> FileDescOpener for Env<A, FM, L, V, EX, WD, B, N, ERR>
551where
552    FM: FileDescOpener,
553    N: Hash + Eq,
554{
555    type OpenedFileHandle = FM::OpenedFileHandle;
556
557    fn open_path(&mut self, path: &Path, opts: &OpenOptions) -> io::Result<Self::OpenedFileHandle> {
558        self.file_desc_manager_env.open_path(path, opts)
559    }
560
561    fn open_pipe(&mut self) -> io::Result<Pipe<Self::OpenedFileHandle>> {
562        self.file_desc_manager_env.open_pipe()
563    }
564}
565
566impl<A, FM, L, V, EX, WD, B, N, ERR> ReportErrorEnvironment for Env<A, FM, L, V, EX, WD, B, N, ERR>
567where
568    A: ArgumentsEnvironment,
569    A::Arg: fmt::Display,
570    FM: AsyncIoEnvironment + FileDescEnvironment,
571    FM::FileHandle: Clone,
572    FM::IoHandle: From<FM::FileHandle>,
573    N: Hash + Eq,
574{
575    fn report_error<'a>(
576        &mut self,
577        fail: &'a (dyn Error + Sync + Send + 'static),
578    ) -> BoxFuture<'a, ()> {
579        let fd = match self.file_desc(STDERR_FILENO) {
580            Some((fdes, perms)) if perms.writable() => fdes.clone(),
581            _ => return Box::pin(async {}),
582        };
583
584        let data = format!("{}: {}\n", self.name(), fail).into_bytes();
585        let future = self.write_all(fd.into(), Cow::Owned(data));
586
587        Box::pin(async move {
588            let _ = future.await;
589        })
590    }
591}
592
593impl<A, FM, L, V, EX, WD, B, N, ERR> FunctionEnvironment for Env<A, FM, L, V, EX, WD, B, N, ERR>
594where
595    N: Hash + Eq + Clone,
596{
597    type FnName = N;
598    type Fn = Arc<dyn Spawn<Self, Error = ERR> + Send + Sync>;
599
600    fn function(&self, name: &Self::FnName) -> Option<&Self::Fn> {
601        self.fn_env.function(name)
602    }
603
604    fn set_function(&mut self, name: Self::FnName, func: Self::Fn) {
605        self.fn_env.set_function(name, func);
606    }
607
608    fn has_function(&self, name: &Self::FnName) -> bool {
609        self.fn_env.has_function(name)
610    }
611}
612
613impl<A, FM, L, V, EX, WD, B, N, ERR> UnsetFunctionEnvironment
614    for Env<A, FM, L, V, EX, WD, B, N, ERR>
615where
616    N: Hash + Eq + Clone,
617{
618    fn unset_function(&mut self, name: &Self::FnName) {
619        self.fn_env.unset_function(name);
620    }
621}
622
623impl<A, FM, L, V, EX, WD, B, N, ERR> FunctionFrameEnvironment
624    for Env<A, FM, L, V, EX, WD, B, N, ERR>
625where
626    N: Hash + Eq + Clone,
627{
628    fn push_fn_frame(&mut self) {
629        self.fn_frame_env.push_fn_frame()
630    }
631
632    fn pop_fn_frame(&mut self) {
633        self.fn_frame_env.pop_fn_frame()
634    }
635
636    fn is_fn_running(&self) -> bool {
637        self.fn_frame_env.is_fn_running()
638    }
639}
640
641impl<A, FM, L, V, EX, WD, B, N, ERR> LastStatusEnvironment for Env<A, FM, L, V, EX, WD, B, N, ERR>
642where
643    L: LastStatusEnvironment,
644    N: Hash + Eq,
645{
646    fn last_status(&self) -> ExitStatus {
647        self.last_status_env.last_status()
648    }
649
650    fn set_last_status(&mut self, status: ExitStatus) {
651        self.last_status_env.set_last_status(status);
652    }
653}
654
655impl<A, FM, L, V, EX, WD, B, N, ERR> VariableEnvironment for Env<A, FM, L, V, EX, WD, B, N, ERR>
656where
657    V: VariableEnvironment,
658    N: Hash + Eq,
659{
660    type VarName = V::VarName;
661    type Var = V::Var;
662
663    fn var<Q: ?Sized>(&self, name: &Q) -> Option<&Self::Var>
664    where
665        Self::VarName: Borrow<Q>,
666        Q: Hash + Eq,
667    {
668        self.var_env.var(name)
669    }
670
671    fn set_var(&mut self, name: Self::VarName, val: Self::Var) {
672        self.var_env.set_var(name, val);
673    }
674
675    fn env_vars(&self) -> Cow<'_, [(&Self::VarName, &Self::Var)]> {
676        self.var_env.env_vars()
677    }
678}
679
680impl<A, FM, L, V, EX, WD, B, N, ERR> ExportedVariableEnvironment
681    for Env<A, FM, L, V, EX, WD, B, N, ERR>
682where
683    V: ExportedVariableEnvironment,
684    N: Hash + Eq,
685{
686    fn exported_var(&self, name: &Self::VarName) -> Option<(&Self::Var, bool)> {
687        self.var_env.exported_var(name)
688    }
689
690    fn set_exported_var(&mut self, name: Self::VarName, val: Self::Var, exported: bool) {
691        self.var_env.set_exported_var(name, val, exported)
692    }
693}
694
695impl<A, FM, L, V, EX, WD, B, N, ERR> UnsetVariableEnvironment
696    for Env<A, FM, L, V, EX, WD, B, N, ERR>
697where
698    V: UnsetVariableEnvironment,
699    N: Hash + Eq,
700{
701    fn unset_var(&mut self, name: &V::VarName) {
702        self.var_env.unset_var(name)
703    }
704}
705
706impl<A, FM, L, V, EX, WD, B, N, ERR> ExecutableEnvironment for Env<A, FM, L, V, EX, WD, B, N, ERR>
707where
708    N: Hash + Eq,
709    EX: ExecutableEnvironment,
710{
711    fn spawn_executable(
712        &self,
713        data: ExecutableData<'_>,
714    ) -> Result<BoxFuture<'static, ExitStatus>, CommandError> {
715        self.exec_env.spawn_executable(data)
716    }
717}
718
719impl<A, FM, L, V, EX, WD, B, N, ERR> WorkingDirectoryEnvironment
720    for Env<A, FM, L, V, EX, WD, B, N, ERR>
721where
722    N: Hash + Eq,
723    WD: WorkingDirectoryEnvironment,
724{
725    fn path_relative_to_working_dir<'a>(&self, path: Cow<'a, Path>) -> Cow<'a, Path> {
726        self.working_dir_env.path_relative_to_working_dir(path)
727    }
728
729    fn current_working_dir(&self) -> &Path {
730        self.working_dir_env.current_working_dir()
731    }
732}
733
734impl<A, FM, L, V, EX, WD, B, N, ERR> ChangeWorkingDirectoryEnvironment
735    for Env<A, FM, L, V, EX, WD, B, N, ERR>
736where
737    N: Hash + Eq,
738    V: VariableEnvironment,
739    V::VarName: From<String>,
740    V::Var: From<String>,
741    WD: WorkingDirectoryEnvironment,
742    WD: ChangeWorkingDirectoryEnvironment,
743{
744    fn change_working_dir<'a>(&mut self, path: Cow<'a, Path>) -> io::Result<()> {
745        let old_cwd = self
746            .current_working_dir()
747            .to_string_lossy()
748            .into_owned()
749            .into();
750
751        self.working_dir_env.change_working_dir(path)?;
752
753        let new_cwd = self
754            .current_working_dir()
755            .to_string_lossy()
756            .into_owned()
757            .into();
758
759        self.set_var("PWD".to_owned().into(), new_cwd);
760        self.set_var("OLDPWD".to_owned().into(), old_cwd);
761
762        Ok(())
763    }
764}
765
766impl<A, FM, L, V, EX, WD, B, N, ERR> BuiltinEnvironment for Env<A, FM, L, V, EX, WD, B, N, ERR>
767where
768    N: Hash + Eq,
769    B: BuiltinEnvironment,
770{
771    type BuiltinName = B::BuiltinName;
772    type Builtin = B::Builtin;
773
774    fn builtin(&self, name: &Self::BuiltinName) -> Option<Self::Builtin> {
775        self.builtin_env.builtin(name)
776    }
777}
778
779/// A default environment configured with provided (non-atomic) implementations.
780///
781/// Generic over the representation of shell words, variables, function names, etc.
782pub type DefaultEnv<T> = Env<
783    ArgsEnv<T>,
784    TokioFileDescManagerEnv,
785    LastStatusEnv,
786    VarEnv<T, T>,
787    TokioExecEnv,
788    VirtualWorkingDirEnv,
789    BuiltinEnv<T>,
790    T,
791    RuntimeError,
792>;
793
794/// A default environment configured with provided  implementations,
795/// and uses `Arc<String>` to represent shell values.
796pub type DefaultEnvArc = DefaultEnv<Arc<String>>;
797
798impl<T> DefaultEnv<T>
799where
800    T: StringWrapper,
801{
802    /// Creates a new default environment.
803    ///
804    /// See the definition of `DefaultEnvConfig` for what configuration will be used.
805    pub fn new() -> io::Result<Self> {
806        DefaultEnvConfig::new().map(Self::with_config)
807    }
808}