deferred 1.1.0

Rust crate to help perform deferred execution of code logic.
Documentation
use crate::context::*;
use std::collections::VecDeque;

/// Alias for deferred logic part that takes current context and produces new one that will be
/// passed to next deferred step execution.
pub type Part<S> = fn(input: Context<S>) -> Context<S>;

/// Struct that holds parts and state of deferred logic to execute whenever you want to.
///
/// # Note
/// Everytime when you want to resume execution, you consume deferred context and produce new one
/// so keep in mind to restore it before `resume()` and store it again after `resume()`.
pub struct Deferred<S> {
    parts: VecDeque<Part<S>>,
    context: Context<S>,
}

impl<S> Deferred<S> {
    /// Creates new deferred execution.
    ///
    /// # Arguments
    /// * `state` - context initial state.
    /// * `parts` - vector of logic parts.
    ///
    /// # Example
    /// ```
    /// # #[macro_use] extern crate deferred;
    /// # use deferred::*;
    /// # fn main() {
    /// fn foo(v: i32) -> Deferred<i32> {
    ///     deferred!(v, [
    ///         |c| state!(c.state() + 1),
    ///         |c| state!(c.state() + 2)
    ///     ])
    /// }
    ///
    /// assert_eq!(foo(1).consume(), 4);
    /// # }
    /// ```
    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),
        }
    }

    /// Tells if deferred execution can be resumed.
    ///
    /// # Example
    /// ```
    /// # #[macro_use] extern crate deferred;
    /// # use deferred::*;
    /// # fn main() {
    /// fn foo(v: i32) -> Deferred<i32> {
    ///     deferred!(v, [
    ///         |c| state!(c.state() + 1),
    ///         |c| state!(c.state() + 2)
    ///     ])
    /// }
    ///
    /// let d = foo(1);
    /// assert_eq!(d.can_resume(), true);
    /// let d = d.resume().unwrap();
    /// assert_eq!(d.can_resume(), true);
    /// let d = d.resume().unwrap();
    /// assert_eq!(d.can_resume(), false);
    /// # }
    /// ```
    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(),
        }
    }

    /// Gets reference to current state stored in context.
    ///
    /// # Example
    /// ```
    /// # #[macro_use] extern crate deferred;
    /// # use deferred::*;
    /// # fn main() {
    /// fn foo(v: i32) -> Deferred<i32> {
    ///     deferred!(v, [
    ///         |c| state!(c.state() + 1),
    ///         |c| state!(c.state() + 2)
    ///     ])
    /// }
    ///
    /// let d = foo(1);
    /// assert_eq!(d.state(), Some(&1));
    /// let d = d.resume().unwrap();
    /// assert_eq!(d.state(), Some(&2));
    /// let d = d.resume().unwrap();
    /// assert_eq!(d.state(), Some(&4));
    /// # }
    /// ```
    pub fn state(&self) -> Option<&S> {
        self.context.get_state()
    }

    /// Resumes deferred execution, which means we execute next logic part and store its state.
    ///
    /// # Note
    /// While you resume execution, you consume it and return new one so keep in mind that you need
    /// to store it again or replace with old one after calling `resume()`.
    ///
    /// # Example
    /// ```
    /// # #[macro_use] extern crate deferred;
    /// # use deferred::*;
    /// # fn main() {
    /// fn foo(v: i32) -> Deferred<i32> {
    ///     deferred!(v, [
    ///         |c| state!(c.state() + 1),
    ///         |c| foo2(c.state()).into(),
    ///         |c| state!(c.state() + 2)
    ///     ])
    /// }
    ///
    /// fn foo2(v: i32) -> Deferred<i32> {
    ///     deferred!(v, [
    ///         |c| state!(c.state() * 2),
    ///         |c| state!(c.state() * 3)
    ///     ])
    /// }
    ///
    /// let d = foo(1);
    /// assert!(d.can_resume());
    /// assert_eq!(d.state(), Some(&1));
    ///
    /// let d = d.resume().unwrap();
    /// assert!(d.can_resume());
    /// assert_eq!(d.state(), Some(&2));
    ///
    /// let d = d.resume().unwrap();
    /// assert!(d.can_resume());
    /// assert_eq!(d.state(), Some(&4));
    ///
    /// let d = d.resume().unwrap();
    /// assert!(d.can_resume());
    /// assert_eq!(d.state(), Some(&12));
    ///
    /// let d = d.resume().unwrap();
    /// assert!(!d.can_resume());
    /// assert_eq!(d.state(), Some(&14));
    /// # }
    /// ```
    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()
                }
            }
        }
    }

    /// Consumes deferred execution, which means we execute all remaining logic parts and returns
    /// final state.
    ///
    /// # Example
    /// ```
    /// # #[macro_use] extern crate deferred;
    /// # use deferred::*;
    /// # fn main() {
    /// fn foo(v: i32) -> Deferred<i32> {
    ///     deferred!(v, [
    ///         |c| state!(c.state() + 1),
    ///         |c| state!(c.state() + 2)
    ///     ])
    /// }
    ///
    /// assert_eq!(foo(1).consume(), 4);
    /// # }
    /// ```
    pub fn consume(mut self) -> S {
        while self.can_resume() {
            self = self.resume().unwrap();
        }
        self.context.state()
    }

    /// Alias for `consume()` method.
    #[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))
    }
}