conch_runtime_pshaw/
spawn.rs

1//! Defines methods for spawning commands into futures.
2
3use crate::ExitStatus;
4use async_trait::async_trait;
5use futures_core::future::BoxFuture;
6
7mod and_or;
8mod case;
9mod for_cmd;
10mod func_exec;
11mod if_cmd;
12mod local_redirections;
13mod loop_cmd;
14mod pipeline;
15mod sequence;
16mod simple;
17mod subshell;
18mod substitution;
19mod swallow_non_fatal;
20
21#[cfg(feature = "conch-parser")]
22pub mod ast_impl;
23pub mod builtin;
24
25// Pub reexports
26pub use self::and_or::{and_or_list, AndOr};
27pub use self::case::{case, PatternBodyPair};
28pub use self::for_cmd::{for_args, for_loop, for_with_args};
29pub use self::func_exec::{function, function_body};
30pub use self::if_cmd::if_cmd;
31pub use self::local_redirections::spawn_with_local_redirections_and_restorer;
32pub use self::loop_cmd::loop_cmd;
33pub use self::pipeline::pipeline;
34pub use self::sequence::{sequence, sequence_exact, sequence_slice, SequenceSlice};
35pub use self::simple::{simple_command, simple_command_with_restorer};
36pub use self::subshell::subshell;
37pub use self::substitution::substitution;
38pub use self::swallow_non_fatal::swallow_non_fatal_errors;
39
40/// A trait for spawning commands.
41///
42/// Spawning a command is separated into two distinct parts: a future
43/// that requires a mutable environment to make progress, and a future
44/// which no longer needs any context and can make progress on its own.
45///
46/// This distinction allows a caller to drop an environment as soon as
47/// it is no longer needed, which will free up resources, and especially
48/// important in preventing deadlocks between pipelines (since the parent
49/// process will contain extra reader/writer ends of a pipe and may prevent
50/// processes from exiting).
51#[async_trait]
52pub trait Spawn<E: ?Sized> {
53    /// The type of error that can arise if there is an error during spawning.
54    type Error;
55
56    /// Spawn the command as a future which returns another future.
57    ///
58    /// The first, or "outer" future returned is allowed to maintain references
59    /// to both the type being spawned, and the environment itself. Once the command
60    /// no longer needs a reference to the environment or any other data, it should
61    /// return a second future which represents the final result of the command.
62    ///
63    /// This separation allows the caller (or poller) to drop the environment as son as
64    /// it is no longer needed, which will free up resources, and especially
65    /// important in preventing deadlocks between pipelines (since the parent
66    /// process will contain extra reader/writer ends of a pipe and may prevent
67    /// processes from exiting).
68    ///
69    /// Although the implementation is free to make any optimizations or
70    /// pre-computations, there should be no observable side-effects until the
71    /// very first call to `poll` or `.await` on the future. That way a constructed
72    /// future that was never `poll`ed could be dropped without the risk of unintended
73    /// side effects.
74    async fn spawn(&self, env: &mut E) -> Result<BoxFuture<'static, ExitStatus>, Self::Error>;
75}
76
77impl<'a, T, E> Spawn<E> for &'a T
78where
79    T: ?Sized + Spawn<E>,
80    E: ?Sized,
81{
82    type Error = T::Error;
83
84    fn spawn<'life0, 'life1, 'async_trait>(
85        &'life0 self,
86        env: &'life1 mut E,
87    ) -> BoxFuture<'async_trait, Result<BoxFuture<'static, ExitStatus>, Self::Error>>
88    where
89        'life0: 'async_trait,
90        'life1: 'async_trait,
91        Self: 'async_trait,
92    {
93        (**self).spawn(env)
94    }
95}
96
97impl<T, E> Spawn<E> for Box<T>
98where
99    T: ?Sized + Spawn<E>,
100    E: ?Sized,
101{
102    type Error = T::Error;
103
104    fn spawn<'life0, 'life1, 'async_trait>(
105        &'life0 self,
106        env: &'life1 mut E,
107    ) -> BoxFuture<'async_trait, Result<BoxFuture<'static, ExitStatus>, Self::Error>>
108    where
109        'life0: 'async_trait,
110        'life1: 'async_trait,
111        Self: 'async_trait,
112    {
113        (**self).spawn(env)
114    }
115}
116
117impl<T, E> Spawn<E> for std::sync::Arc<T>
118where
119    T: ?Sized + Spawn<E>,
120    E: ?Sized,
121{
122    type Error = T::Error;
123
124    fn spawn<'life0, 'life1, 'async_trait>(
125        &'life0 self,
126        env: &'life1 mut E,
127    ) -> BoxFuture<'async_trait, Result<BoxFuture<'static, ExitStatus>, Self::Error>>
128    where
129        'life0: 'async_trait,
130        'life1: 'async_trait,
131        Self: 'async_trait,
132    {
133        (**self).spawn(env)
134    }
135}
136
137/// A grouping of guard and body commands.
138#[derive(Debug, PartialEq, Eq, Clone)]
139pub struct GuardBodyPair<T> {
140    /// The guard commands, which if successful, should lead to the
141    /// execution of the body commands.
142    pub guard: T,
143    /// The body commands to execute if the guard is successful.
144    pub body: T,
145}
146
147#[cfg(test)]
148mod test {
149    use super::Spawn;
150    use crate::{ExitStatus, EXIT_SUCCESS};
151    use futures_core::future::BoxFuture;
152    use std::sync::Arc;
153
154    #[test]
155    fn check_spawn_impls() {
156        struct Dummy;
157
158        #[async_trait::async_trait]
159        impl<E> Spawn<E> for Dummy
160        where
161            E: ?Sized + Send,
162        {
163            type Error = ();
164
165            async fn spawn(
166                &self,
167                _: &mut E,
168            ) -> Result<BoxFuture<'static, ExitStatus>, Self::Error> {
169                Ok(Box::pin(async { EXIT_SUCCESS }))
170            }
171        }
172
173        fn assert_spawn<T: Spawn<()>>() {}
174
175        assert_spawn::<Dummy>();
176        assert_spawn::<&Dummy>();
177        assert_spawn::<&&Dummy>();
178
179        assert_spawn::<Box<Dummy>>();
180        assert_spawn::<Box<&Dummy>>();
181        assert_spawn::<Box<dyn Spawn<(), Error = ()>>>();
182
183        assert_spawn::<Arc<Dummy>>();
184        assert_spawn::<&Arc<Dummy>>();
185        assert_spawn::<Arc<dyn Spawn<(), Error = ()>>>();
186    }
187}