conch_runtime_pshaw/spawn/
and_or.rs

1use crate::env::{LastStatusEnvironment, ReportErrorEnvironment};
2use crate::error::IsFatalError;
3use crate::spawn::swallow_non_fatal_errors;
4use crate::{ExitStatus, Spawn};
5use futures_core::future::BoxFuture;
6use std::iter::Peekable;
7
8/// A command which conditionally runs based on the exit status of the previous command.
9#[derive(Debug, PartialEq, Eq, Clone)]
10pub enum AndOr<T> {
11    /// A compound command which should run only if the previously run command succeeded.
12    And(T),
13    /// A compound command which should run only if the previously run command failed.
14    Or(T),
15}
16
17/// Spawns an `And`/`Or` list of commands from an initial command and an iterator.
18pub async fn and_or_list<T, I, E>(
19    first: T,
20    rest: I,
21    env: &mut E,
22) -> Result<BoxFuture<'static, ExitStatus>, T::Error>
23where
24    T: Spawn<E>,
25    T::Error: IsFatalError,
26    I: IntoIterator<Item = AndOr<T>>,
27    E: ?Sized + LastStatusEnvironment + ReportErrorEnvironment,
28{
29    do_and_or_list(first, rest.into_iter().peekable(), env).await
30}
31
32async fn do_and_or_list<T, I, E>(
33    mut next: T,
34    mut rest: Peekable<I>,
35    env: &mut E,
36) -> Result<BoxFuture<'static, ExitStatus>, T::Error>
37where
38    T: Spawn<E>,
39    T::Error: IsFatalError,
40    I: Iterator<Item = AndOr<T>>,
41    E: ?Sized + LastStatusEnvironment + ReportErrorEnvironment,
42{
43    loop {
44        let future = swallow_non_fatal_errors(&next, env).await?;
45
46        // If we have no further commands to process, we can return the
47        // current command's future (so the caller may drop the environment)
48        if rest.peek().is_none() {
49            return Ok(future);
50        }
51
52        let status = future.await;
53        env.set_last_status(status);
54
55        'find_next: loop {
56            match (rest.next(), status.success()) {
57                (None, _) => return Ok(Box::pin(async move { status })),
58
59                (Some(AndOr::And(cmd)), true) | (Some(AndOr::Or(cmd)), false) => {
60                    next = cmd;
61                    break 'find_next;
62                }
63
64                // Keep looping until we find a command we can spawn
65                _ => {}
66            }
67        }
68    }
69}