conch_runtime_pshaw/spawn/ast_impl/
compound.rs

1use crate::env::{
2    ArgumentsEnvironment, AsyncIoEnvironment, EnvRestorer, ExportedVariableEnvironment,
3    FileDescEnvironment, FileDescOpener, LastStatusEnvironment, ReportErrorEnvironment,
4    SubEnvironment, UnsetVariableEnvironment, VariableEnvironment,
5};
6use crate::error::{IsFatalError, RedirectionError};
7use crate::eval::{RedirectEval, WordEval};
8use crate::spawn::{
9    case, for_args, for_loop, if_cmd, loop_cmd, sequence_exact, sequence_slice,
10    spawn_with_local_redirections_and_restorer, subshell, GuardBodyPair, PatternBodyPair, Spawn,
11};
12use crate::{ExitStatus, EXIT_SUCCESS};
13use conch_parser::ast;
14use futures_core::future::BoxFuture;
15
16#[async_trait::async_trait]
17impl<S, R, E> Spawn<E> for ast::CompoundCommand<S, R>
18where
19    S: Send + Sync + Spawn<E>,
20    S::Error: From<RedirectionError> + From<R::Error>,
21    R: Send + Sync + RedirectEval<E, Handle = E::FileHandle>,
22    E: ?Sized
23        + Sync
24        + Send
25        + AsyncIoEnvironment
26        + ExportedVariableEnvironment
27        + FileDescEnvironment
28        + FileDescOpener
29        + UnsetVariableEnvironment,
30    E::FileHandle: Clone + Send + From<E::OpenedFileHandle>,
31    E::IoHandle: Send + From<E::FileHandle>,
32    E::VarName: Send + Clone,
33    E::Var: Send + Clone,
34{
35    type Error = S::Error;
36
37    async fn spawn(&self, env: &mut E) -> Result<BoxFuture<'static, ExitStatus>, Self::Error> {
38        spawn_with_local_redirections_and_restorer(&self.io, &self.kind, &mut EnvRestorer::new(env))
39            .await
40    }
41}
42
43#[async_trait::async_trait]
44impl<V, W, S, E> Spawn<E> for ast::CompoundCommandKind<V, W, S>
45where
46    V: Send + Sync + Clone,
47    W: Sync + WordEval<E>,
48    W::Error: Send + IsFatalError,
49    S: Send + Sync + Spawn<E>,
50    S::Error: From<W::Error> + IsFatalError,
51    E: ?Sized
52        + Send
53        + Sync
54        + ArgumentsEnvironment
55        + LastStatusEnvironment
56        + ReportErrorEnvironment
57        + SubEnvironment
58        + VariableEnvironment,
59    E::Var: Send + From<E::Arg> + From<W::EvalResult>,
60    E::VarName: Send + Clone + From<V>,
61{
62    type Error = S::Error;
63
64    async fn spawn(&self, env: &mut E) -> Result<BoxFuture<'static, ExitStatus>, Self::Error> {
65        use ast::CompoundCommandKind::*;
66        match self {
67            Brace(cmds) => sequence_exact(cmds, env).await,
68
69            If {
70                conditionals,
71                else_branch,
72            } => {
73                if_cmd(
74                    conditionals.iter().map(|gbp| GuardBodyPair {
75                        guard: sequence_slice(&gbp.guard),
76                        body: sequence_slice(&gbp.body),
77                    }),
78                    else_branch.as_ref().map(|e| sequence_slice(e)),
79                    env,
80                )
81                .await
82            }
83
84            For { var, words, body } => match words {
85                Some(words) => for_loop(var.clone().into(), words, sequence_slice(body), env).await,
86                None => for_args(var.clone().into(), sequence_slice(body), env).await,
87            },
88
89            Case { word, arms } => {
90                case(
91                    word,
92                    arms.iter().map(|pbp| PatternBodyPair {
93                        patterns: pbp.patterns.as_slice(),
94                        body: sequence_slice(&pbp.body),
95                    }),
96                    env,
97                )
98                .await
99            }
100
101            While(ast::GuardBodyPair { guard, body }) => spawn_loop(false, guard, body, env).await,
102            Until(ast::GuardBodyPair { guard, body }) => spawn_loop(true, guard, body, env).await,
103
104            Subshell(cmds) => {
105                let ret = subshell(sequence_slice(cmds), env).await;
106                Ok(Box::pin(async move { ret }))
107            }
108        }
109    }
110}
111
112async fn spawn_loop<S, E>(
113    invert_guard_status: bool,
114    guard: &[S],
115    body: &[S],
116    env: &mut E,
117) -> Result<BoxFuture<'static, ExitStatus>, S::Error>
118where
119    S: Send + Sync + Spawn<E>,
120    S::Error: IsFatalError,
121    E: ?Sized + Send + Sync + LastStatusEnvironment + ReportErrorEnvironment,
122{
123    let ret = if guard.is_empty() && body.is_empty() {
124        // Not a well formed command, rather than burning CPU and spinning
125        // here, we'll just bail out.
126        EXIT_SUCCESS
127    } else {
128        loop_cmd(
129            invert_guard_status,
130            sequence_slice(guard),
131            sequence_slice(body),
132            env,
133        )
134        .await?
135    };
136
137    Ok(Box::pin(async move { ret }))
138}