use std::cell::{RefCell, Ref};
use std::fmt;
use std::marker::PhantomData;
use crate::{Variable, Node};
pub use crate::system::ComputationHandle;
pub trait ComputedValue {
type Value;
type Context;
fn update(&mut self, ctx: &mut Self::Context, comp: &ComputationHandle);
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)
}
}
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 {
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)
}
}
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) {
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
}
}
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)
}
}
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
}
}
#[must_use]
pub struct ComputedVar<C> {
node: Node,
cell: RefCell<C>,
}
impl<C> ComputedVar<C> {
pub fn new_raw(value: C) -> Self {
ComputedVar {
node: Node::next(),
cell: RefCell::new(value),
}
}
pub fn node(&self) -> &Node {
&self.node
}
}
impl<T, F, C> ComputedVar<FunctionComputed<T, F, C>>
where F: FnMut(&mut C) -> T
{
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)
{
pub fn new_mutate(initial: T, func: F) -> Self {
Self::new_raw(MutatorComputed::new(initial, func))
}
}
pub type BoxedComputedVar<'a, T, C = ()> = ComputedVar<Box<
dyn ComputedValue<Value=T, Context = C> + 'a
>>;
impl<C: ComputedValue> ComputedVar<C> {
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())
}
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())
}
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<_>)
}
}
pub fn force(&self, ctx: &mut C::Context) {
if let Ok(mut inner) = self.cell.try_borrow_mut() {
self.node.on_write(false);
self.node.computation(|comp| inner.update(ctx, comp));
}
}
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()
}
}