use crate::env::{LastStatusEnvironment, ReportErrorEnvironment};
use crate::error::IsFatalError;
use crate::spawn::swallow_non_fatal_errors;
use crate::{ExitStatus, Spawn};
use futures_core::future::BoxFuture;
use std::iter::Peekable;
#[derive(Debug, PartialEq, Eq, Clone)]
pub enum AndOr<T> {
And(T),
Or(T),
}
pub async fn and_or_list<T, I, E>(
first: T,
rest: I,
env: &mut E,
) -> Result<BoxFuture<'static, ExitStatus>, T::Error>
where
T: Spawn<E>,
T::Error: IsFatalError,
I: IntoIterator<Item = AndOr<T>>,
E: ?Sized + LastStatusEnvironment + ReportErrorEnvironment,
{
do_and_or_list(first, rest.into_iter().peekable(), env).await
}
async fn do_and_or_list<T, I, E>(
mut next: T,
mut rest: Peekable<I>,
env: &mut E,
) -> Result<BoxFuture<'static, ExitStatus>, T::Error>
where
T: Spawn<E>,
T::Error: IsFatalError,
I: Iterator<Item = AndOr<T>>,
E: ?Sized + LastStatusEnvironment + ReportErrorEnvironment,
{
loop {
let future = swallow_non_fatal_errors(&next, env).await?;
if rest.peek().is_none() {
return Ok(future);
}
let status = future.await;
env.set_last_status(status);
'find_next: loop {
match (rest.next(), status.success()) {
(None, _) => return Ok(Box::pin(async move { status })),
(Some(AndOr::And(cmd)), true) | (Some(AndOr::Or(cmd)), false) => {
next = cmd;
break 'find_next;
}
_ => {}
}
}
}
}