use {EXIT_SUCCESS, ExitStatus};
use error::IsFatalError;
use env::{LastStatusEnvironment, ReportErrorEnvironment};
use future::{Async, EnvFuture, Poll};
use futures::task;
use spawn::{GuardBodyPair, SpawnRef, VecSequence};
use std::fmt;
use std::mem;
pub fn loop_cmd<S, E: ?Sized>(
invert_guard_status: bool,
guard_body_pair: GuardBodyPair<Vec<S>>,
) -> Loop<S, E> where S: SpawnRef<E>,
{
Loop {
invert_guard_status: invert_guard_status,
guard: guard_body_pair.guard,
body: guard_body_pair.body,
has_run_body: false,
state: State::Init,
}
}
#[must_use = "futures do nothing unless polled"]
pub struct Loop<S, E: ?Sized> where S: SpawnRef<E>
{
invert_guard_status: bool,
guard: Vec<S>,
body: Vec<S>,
has_run_body: bool,
state: State<VecSequence<S, E>>,
}
impl<S, E: ?Sized> fmt::Debug for Loop<S, E>
where S: SpawnRef<E> + fmt::Debug,
S::EnvFuture: fmt::Debug,
S::Future: fmt::Debug,
{
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
fmt.debug_struct("Loop")
.field("invert_guard_status", &self.invert_guard_status)
.field("guard", &self.guard)
.field("body", &self.body)
.field("has_run_body", &self.has_run_body)
.field("state", &self.state)
.finish()
}
}
#[derive(Debug)]
enum State<V> {
Init,
Guard(V),
Body(V),
}
impl<S, E: ?Sized> EnvFuture<E> for Loop<S, E>
where S: SpawnRef<E>,
S::Error: IsFatalError,
E: LastStatusEnvironment + ReportErrorEnvironment,
{
type Item = ExitStatus;
type Error = S::Error;
fn poll(&mut self, env: &mut E) -> Poll<Self::Item, Self::Error> {
if self.guard.is_empty() && self.body.is_empty() {
return Ok(Async::Ready(EXIT_SUCCESS));
}
let mut num_tries = 0;
loop {
num_tries += 1;
if num_tries > 20 {
task::current().notify();
return Ok(Async::NotReady);
}
let next_state = match self.state {
State::Init => None,
State::Guard(ref mut f) => {
let (guard, status) = try_ready!(f.poll(env));
self.guard = guard;
let should_continue = status.success() ^ self.invert_guard_status;
if !should_continue {
if !self.has_run_body {
env.set_last_status(EXIT_SUCCESS);
}
return Ok(Async::Ready(env.last_status()));
}
env.set_last_status(status);
let body = mem::replace(&mut self.body, Vec::new());
Some(State::Body(VecSequence::new(body)))
},
State::Body(ref mut f) => {
let (body, status) = try_ready!(f.poll(env));
self.has_run_body = true;
self.body = body;
env.set_last_status(status);
None
},
};
self.state = next_state.unwrap_or_else(|| {
let guard = mem::replace(&mut self.guard, Vec::new());
State::Guard(VecSequence::new(guard))
});
}
}
fn cancel(&mut self, env: &mut E) {
match self.state {
State::Init => {},
State::Guard(ref mut f) |
State::Body(ref mut f) => f.cancel(env),
}
}
}