use {EXIT_ERROR, EXIT_SUCCESS, Spawn};
use error::IsFatalError;
use env::{LastStatusEnvironment, ReportErrorEnvironment, StringWrapper};
use eval::{Pattern, TildeExpansion, WordEval, WordEvalConfig};
use future::{Async, EnvFuture, Poll};
use glob::MatchOptions;
use spawn::{ExitResult, Sequence, sequence};
use std::fmt;
use std::mem;
#[derive(Debug, PartialEq, Eq, Clone)]
pub struct PatternBodyPair<W, C> {
pub patterns: W,
pub body: C,
}
pub fn case<IA, IW, IS, E: ?Sized>(word: IW::Item, arms: IA) -> Case<IA::IntoIter, IW, IS, E>
where IA: IntoIterator<Item = PatternBodyPair<IW, IS>>,
IW: IntoIterator,
IW::Item: WordEval<E>,
IS: IntoIterator,
IS::Item: Spawn<E>,
{
Case {
state: State::Init(Some(word), Some(arms.into_iter())),
}
}
#[must_use = "futures do nothing unless polled"]
pub struct Case<IA, IW, IS, E: ?Sized>
where IW: IntoIterator,
IW::Item: WordEval<E>,
IS: IntoIterator,
IS::Item: Spawn<E>,
{
state: State<IA, IW, IS, E>,
}
impl<W, S, IA, IW, IS, E: ?Sized> fmt::Debug for Case<IA, IW, IS, E>
where IA: fmt::Debug,
IW: IntoIterator<Item = W> + fmt::Debug,
IW::IntoIter: fmt::Debug,
W: WordEval<E> + fmt::Debug,
W::EvalResult: fmt::Debug,
W::EvalFuture: fmt::Debug,
IS: IntoIterator<Item = S> + fmt::Debug,
IS::IntoIter: fmt::Debug,
S: Spawn<E> + fmt::Debug,
S::EnvFuture: fmt::Debug,
S::Future: fmt::Debug,
{
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
fmt.debug_struct("Case")
.field("state", &self.state)
.finish()
}
}
enum State<IA, IW, IS, E: ?Sized>
where IW: IntoIterator,
IW::Item: WordEval<E>,
IS: IntoIterator,
IS::Item: Spawn<E>,
{
Init(Option<IW::Item>, Option<IA>),
Word(<IW::Item as WordEval<E>>::EvalFuture, Option<IA>),
Cases(Arm<IW::IntoIter, IS, E>, IA),
Body(Sequence<IS::IntoIter, E>),
}
impl<W, S, IA, IW, IS, E: ?Sized> fmt::Debug for State<IA, IW, IS, E>
where IA: fmt::Debug,
IW: IntoIterator<Item = W> + fmt::Debug,
IW::IntoIter: fmt::Debug,
W: WordEval<E> + fmt::Debug,
W::EvalResult: fmt::Debug,
W::EvalFuture: fmt::Debug,
IS: IntoIterator<Item = S> + fmt::Debug,
IS::IntoIter: fmt::Debug,
S: Spawn<E> + fmt::Debug,
S::EnvFuture: fmt::Debug,
S::Future: fmt::Debug,
{
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
match *self {
State::Init(ref word, ref arms) => {
fmt.debug_tuple("State::Init")
.field(word)
.field(arms)
.finish()
},
State::Word(ref word, ref arms) => {
fmt.debug_tuple("State::Word")
.field(word)
.field(arms)
.finish()
},
State::Cases(ref current, ref arms) => {
fmt.debug_tuple("State::Cases")
.field(current)
.field(arms)
.finish()
},
State::Body(ref b) => {
fmt.debug_tuple("State::Body")
.field(b)
.finish()
},
}
}
}
macro_rules! next_arm {
($word:expr, $arms:expr) => {{
match $arms.next() {
None => return Ok(Async::Ready(ExitResult::Ready(EXIT_SUCCESS))),
Some(pair) => Arm {
word: $word,
current: None,
pats: pair.patterns.into_iter(),
body: Some(pair.body)
},
}
}}
}
impl<W, S, IA, IW, IS, E: ?Sized> EnvFuture<E> for Case<IA, IW, IS, E>
where IA: Iterator<Item = PatternBodyPair<IW, IS>>,
IW: IntoIterator<Item = W>,
W: WordEval<E>,
W::Error: IsFatalError,
IS: IntoIterator<Item = S>,
S: Spawn<E>,
S::Error: From<W::Error> + IsFatalError,
E: LastStatusEnvironment + ReportErrorEnvironment,
{
type Item = ExitResult<S::Future>;
type Error = S::Error;
fn poll(&mut self, env: &mut E) -> Poll<Self::Item, Self::Error> {
loop {
let next_state = match self.state {
State::Init(ref mut word, ref mut arms) => {
let cfg = WordEvalConfig {
tilde_expansion: TildeExpansion::First,
split_fields_further: false,
};
let word = word.take().expect("polled twice").eval_with_config(env, cfg);
State::Word(word, arms.take())
},
State::Word(ref mut word, ref mut arms) => {
let word = match word.poll(env) {
Ok(Async::Ready(word)) => word.join().into_owned(),
Ok(Async::NotReady) => return Ok(Async::NotReady),
Err(e) => {
env.set_last_status(EXIT_ERROR);
return Err(e.into());
},
};
let mut arms = arms.take().expect("polled twice");
let current = next_arm!(word, arms);
State::Cases(current, arms)
},
State::Cases(ref mut current, ref mut arms) => {
match try_ready!(current.poll(env)) {
(_, Some(body)) => State::Body(sequence(body)),
(word, None) => {
*current = next_arm!(word, arms);
continue;
},
}
},
State::Body(ref mut f) => return f.poll(env),
};
self.state = next_state;
}
}
fn cancel(&mut self, env: &mut E) {
match self.state {
State::Init(_, _) => {},
State::Word(ref mut word, _) => word.cancel(env),
State::Cases(ref mut current, _) => current.cancel(env),
State::Body(ref mut f) => f.cancel(env),
}
}
}
#[must_use = "futures do nothing unless polled"]
struct Arm<I, B, E: ?Sized>
where I: Iterator,
I::Item: WordEval<E>,
{
word: String,
current: Option<Pattern<<I::Item as WordEval<E>>::EvalFuture>>,
pats: I,
body: Option<B>,
}
impl<W, I, B, E: ?Sized> fmt::Debug for Arm<I, B, E>
where I: Iterator<Item = W> + fmt::Debug,
W: WordEval<E> + fmt::Debug,
W::EvalFuture: fmt::Debug,
B: fmt::Debug,
{
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
fmt.debug_struct("Arm")
.field("word", &self.word)
.field("current", &self.current)
.field("pats", &self.pats)
.field("body", &self.body)
.finish()
}
}
impl<W, I, B, E: ?Sized> EnvFuture<E> for Arm<I, B, E>
where I: Iterator<Item = W>,
W: WordEval<E>,
W::Error: IsFatalError,
E: ReportErrorEnvironment,
{
type Item = (String, Option<B>);
type Error = W::Error;
fn poll(&mut self, env: &mut E) -> Poll<Self::Item, Self::Error> {
loop {
if self.current.is_none() {
self.current = self.pats.next().map(|p| p.eval_as_pattern(env));
}
let pat = match self.current.as_mut() {
Some(ref mut f) => match f.poll(env) {
Ok(Async::Ready(pat)) => Some(pat),
Ok(Async::NotReady) => return Ok(Async::NotReady),
Err(e) => {
if e.is_fatal() {
return Err(e);
} else {
env.report_error(&e);
None
}
},
},
None => {
let word = mem::replace(&mut self.word, String::new());
return Ok(Async::Ready((word, None)));
},
};
self.current.take();
if let Some(pat) = pat {
let match_opts = MatchOptions {
case_sensitive: true,
require_literal_separator: false,
require_literal_leading_dot: false,
};
if pat.matches_with(&self.word, &match_opts) {
assert!(self.body.is_some(), "polled twice");
let word = mem::replace(&mut self.word, String::new());
return Ok(Async::Ready((word, self.body.take())));
}
}
}
}
fn cancel(&mut self, env: &mut E) {
self.current.as_mut().map(|f| f.cancel(env));
}
}