reax 0.2.0

A reactivity system for Rust that infers dependencies between functions.
Documentation
//! All the types and traits needed for storing and updating lazily computed
//! variables.
//!
//! The key type in this module is [`ComputedVar`](struct.ComputedVar.html).
//! Everything else only exists to be used within it.

use std::cell::{RefCell, Ref};
use std::fmt;
use std::marker::PhantomData;
use crate::{Variable, Node};

pub use crate::system::ComputationHandle;

/// A trait implemented by any item which can be wrapped in a `ComputedVar`.
pub trait ComputedValue {
    /// The value visible within the `ComputedVar`.
    type Value;

    /// The context used by the update function.
    type Context;

    /// Recompute the value of this item.
    fn update(&mut self, ctx: &mut Self::Context, comp: &ComputationHandle);

    /// Cheaply get a reference to the value of this item. This function is free
    /// to panic if `update` has never been called.
    fn read(&self) -> &Self::Value;
}

impl<'a, T, C> ComputedValue for Box<dyn ComputedValue<Value=T, Context=C> + 'a> {
    type Value = T;
    type Context = C;

    fn update(&mut self, ctx: &mut C, comp: &ComputationHandle) {
        ComputedValue::update(&mut **self, ctx, comp)
    }

    fn read(&self) -> &T {
        ComputedValue::read(&**self)
    }
}

/// A computed `T` which is produced by some function `F`.
///
/// Created by the [`ComputedVar::new`](struct.ComputedVar.html#method.new)
/// function or [`computed`](../macro.computed.html) macro.
pub struct FunctionComputed<T, F, C> {
    state: Option<T>,
    update: F,
    context: PhantomData<C>,
}

impl<T, F, C> FunctionComputed<T, F, C> where F: FnMut(&mut C) -> T {
    /// Creates the computed `T`.
    pub fn new(func: F) -> Self {
        FunctionComputed {
            state: None,
            update: func,
            context: PhantomData,
        }
    }
}

static UNUPDATED_VAR_MSG: &str = "computed var has never been updated";

impl<T, F, C> ComputedValue for FunctionComputed<T, F, C>
    where F: FnMut(&mut C) -> T
{
    type Value = T;
    type Context = C;

    fn update(&mut self, ctx: &mut C, _comp: &ComputationHandle) {
        self.state = Some((self.update)(ctx));
    }

    fn read(&self) -> &T {
        self.state.as_ref().expect(UNUPDATED_VAR_MSG)
    }
}

/// A computed `T` which is initialized to some default and then mutated by some
/// function `F`.
///
/// Created by the
/// [`ComputedVar::new_mutate`](struct.ComputedVar.html#method.new_mutate)
/// function or [`computed`](../macro.computed.html) macro.
pub struct MutatorComputed<T, F, C> {
    state: T,
    update: F,
    context: PhantomData<C>,
}

impl<T, F, C> MutatorComputed<T, F, C> where F: FnMut(&mut T, &mut C) {
    /// Creates the computed `T`.
    pub fn new(initial: T, func: F) -> Self {
        MutatorComputed {
            state: initial,
            update: func,
            context: PhantomData,
        }
    }
}

impl<T, F, C> ComputedValue for MutatorComputed<T, F, C>
    where F: FnMut(&mut T, &mut C)
{
    type Value = T;
    type Context = C;

    fn update(&mut self, ctx: &mut C, _comp: &ComputationHandle) {
        (self.update)(&mut self.state, ctx);
    }

    fn read(&self) -> &T {
        &self.state
    }
}

/// A computed `T` which is produced by some function `F` using the value of
/// `V`.
///
/// Created by the
/// [`Variable::map_mutate`](../trait.Variable.html#tymethod.map_mutate)
/// method.
pub struct FunctionMapped<V, T, F> {
    pub(crate) inner: V,
    pub(crate) state: Option<T>,
    pub(crate) update: F,
}

impl<V, T, F> ComputedValue for FunctionMapped<V, T, F>
where
    V: Variable,
    F: FnMut(&V::Value) -> T
{
    type Value = T;
    type Context = ();

    fn update(&mut self, _ctx: &mut (), comp: &ComputationHandle) {
        let mapped = self.inner.get();
        comp.set_reactive_area(false);
        self.state = Some((self.update)(&*mapped));
    }

    fn read(&self) -> &T {
        self.state.as_ref().expect(UNUPDATED_VAR_MSG)
    }
}

/// A computed `T` which is initalized to some default and then mutated by some
/// function `F` which also takes the value of `V`.
///
/// Created by the
/// [`Variable::map_mutate`](../trait.Variable.html#tymethod.map_mutate)
/// method.
pub struct MutatorMapped<V, T, F> {
    pub(crate) inner: V,
    pub(crate) state: T,
    pub(crate) update: F,
}

impl<V, T, F> ComputedValue for MutatorMapped<V, T, F>
where
    V: Variable,
    F: FnMut(&V::Value, &mut T)
{
    type Value = T;
    type Context = ();

    fn update(&mut self, _ctx: &mut (), comp: &ComputationHandle) {
        let mapped = self.inner.get();
        comp.set_reactive_area(false);
        (self.update)(&*mapped, &mut self.state);
    }

    fn read(&self) -> &T {
        &self.state
    }
}

/// A lazily computed variable that is tracked by the reax runtime.
#[must_use]
pub struct ComputedVar<C> {
    node: Node,
    cell: RefCell<C>,
}

impl<C> ComputedVar<C> {
    /// Constructs a computed variable around the given
    /// [`ComputedValue`](trait.ComputedValue.html). The dependencies of the
    /// variable will be determined from the accesses made by the
    /// [`update`](trait.ComputedValue.html#tymethod.update) function. Note
    /// that `update` will not be executed until someone retrieves the value of
    /// this cell.
    pub fn new_raw(value: C) -> Self {
        ComputedVar {
            node: Node::next(),
            cell: RefCell::new(value),
        }
    }
    
    /// Returns a handle to this computation's node in the dependency graph.
    pub fn node(&self) -> &Node {
        &self.node
    }
}

impl<T, F, C> ComputedVar<FunctionComputed<T, F, C>>
    where F: FnMut(&mut C) -> T
{
    /// Constructs a computed variable. The value of the variable will be the
    /// set by the given function when needed. The dependencies of the variable
    /// will be determined from the accesses made by the given function. Note
    /// that the function will not be executed until someone retrieves the value
    /// of this cell.
    ///
    /// To reuse buffers from past executions, use
    /// [`new_mutate`](#method.new_mutate).
    pub fn new(func: F) -> Self {
        Self::new_raw(FunctionComputed::new(func))
    }
}

impl<T, F, C> ComputedVar<MutatorComputed<T, F, C>>
    where F: FnMut(&mut T, &mut C)
{
    /// Constructs a computed variable. This is identical to
    /// [`new`](#method.new) except the value of the variable will be *mutated*
    /// by the given function when needed, not created from scratch.
    pub fn new_mutate(initial: T, func: F) -> Self {
        Self::new_raw(MutatorComputed::new(initial, func))
    }
}

/// A computed cell which is implementation agnostic. The value of the cell and
/// captures of the cell's `update` function are heap-allocated.
pub type BoxedComputedVar<'a, T, C = ()> = ComputedVar<Box<
    dyn ComputedValue<Value=T, Context = C> + 'a
>>;

impl<C: ComputedValue> ComputedVar<C> {
    /// Identical to [`Variable::get`](../trait.Variable#tymethod.get) but
    /// requires the update context.
    pub fn get_contextual(&self, ctx: &mut C::Context) -> Ref<C::Value> {
        self.check(ctx);
        self.node().on_read();
        Ref::map(self.cell.borrow(), |inner| inner.read())
    }

    /// Identical to
    /// [`Variable::get_non_reactive`](../trait.Variable#tymethod.get_non_reactive)
    /// but requires the update context.
    pub fn get_contextual_non_reactive(&self, ctx: &mut C::Context) -> Ref<C::Value> {
        self.check(ctx);
        Ref::map(self.cell.borrow(), |inner| inner.read())
    }

    /// Creates a boxed version of this cell who's type does not depend on how
    /// the cell is updated.
    pub fn boxed<'a>(self) -> BoxedComputedVar<'a, C::Value, C::Context> where Self: 'a {
        ComputedVar {
            node: self.node,
            cell: RefCell::new(Box::new(self.cell.into_inner()) as Box<_>)
        }
    }

    /// Forces the variable to re-compute no matter its current dirty status.
    pub fn force(&self, ctx: &mut C::Context) {
        // Do nothing if already computing.
        if let Ok(mut inner) = self.cell.try_borrow_mut() {
            self.node.on_write(false);
            self.node.computation(|comp| inner.update(ctx, comp));
        }
    }

    /// If changes to upstream variables have made this variable dirty,
    /// re-compute it. Otherwise, do nothing.
    pub fn check(&self, ctx: &mut C::Context) {
        if self.node.is_dirty() {
            self.force(ctx);
        }
    }
}

impl<C: ComputedValue<Context=()>> Variable for ComputedVar<C> {
    type Value = C::Value;

    #[inline(always)]
    fn node(&self) -> &Node { &self.node }

    fn get(&self) -> Ref<C::Value> {
        self.get_contextual(&mut ())
    }

    fn get_non_reactive(&self) -> Ref<C::Value> {
        self.get_contextual_non_reactive(&mut ())
    }
}

impl<C: ComputedValue<Context=()>> fmt::Debug for ComputedVar<C>
    where C::Value: fmt::Debug
{
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        let val = self.get();
        f.debug_struct("Cell")
            .field("id", self.node())
            .field("value", &*val)
            .finish()
    }
}