use crate::context::*;
use std::collections::VecDeque;
pub type Part<S> = fn(input: Context<S>) -> Context<S>;
pub struct Deferred<S> {
parts: VecDeque<Part<S>>,
context: Context<S>,
}
impl<S> Deferred<S> {
pub fn new(state: S, parts: Vec<Part<S>>) -> Self {
let mut p = VecDeque::new();
p.extend(parts);
Self {
parts: p,
context: Context::State(state),
}
}
pub fn can_resume(&self) -> bool {
match &self.context {
Context::State(_) => !self.parts.is_empty(),
Context::Deferred(d) => d.can_resume() || !self.parts.is_empty(),
}
}
pub fn state(&self) -> Option<&S> {
self.context.get_state()
}
pub fn resume(mut self) -> Option<Self> {
match self.context {
Context::State(state) => {
if let Some(part) = self.parts.pop_front() {
let context = part(Context::State(state));
if context.is_deferred() {
self.context = context;
self.resume()
} else {
self.context = context;
Some(self)
}
} else {
None
}
}
Context::Deferred(deferred) => {
if deferred.can_resume() {
if let Some(deferred) = deferred.resume() {
self.context = deferred.into();
Some(self)
} else {
None
}
} else {
self.context = Context::State(deferred.consume());
self.resume()
}
}
}
}
pub fn consume(mut self) -> S {
while self.can_resume() {
self = self.resume().unwrap();
}
self.context.state()
}
#[inline]
pub fn unwrap(self) -> S {
self.consume()
}
}
impl<S> Into<Context<S>> for Deferred<S> {
fn into(self) -> Context<S> {
Context::Deferred(Box::new(self))
}
}