use std::any::Any;
use std::cell::RefCell;
use std::marker::PhantomData;
use std::rc::Rc;
use super::runtime::{NodeData, NodeId, ReactiveNode, Scope};
use super::with_runtime;
pub struct StoredValue<T: 'static> {
id: NodeId,
_ty: PhantomData<fn() -> T>,
}
impl<T: 'static> Clone for StoredValue<T> {
fn clone(&self) -> Self {
*self
}
}
impl<T: 'static> Copy for StoredValue<T> {}
impl<T: 'static> StoredValue<T> {
pub fn new(initial: T) -> Self {
let value: Rc<RefCell<dyn Any>> = Rc::new(RefCell::new(initial));
let needs_warning = with_runtime(|rt| rt.current_owner().is_none());
if needs_warning {
super::warn_no_owner("StoredValue::new");
}
let id = with_runtime(|rt| {
let owner = rt.current_owner().unwrap_or_else(|| {
let detached = rt.owners.insert(Scope::new(None));
rt.owner_stack.push(detached);
detached
});
let id = rt.nodes.insert(ReactiveNode {
owner,
data: NodeData::Signal { value },
sources: Default::default(),
subscribers: Default::default(),
arc_sources: Vec::new(),
});
if let Some(o) = rt.owners.get_mut(owner) {
o.nodes.push(id);
}
id
});
Self {
id,
_ty: PhantomData,
}
}
pub fn with<R>(self, f: impl FnOnce(&T) -> R) -> R {
let value = with_runtime(|rt| {
rt.nodes
.get(self.id)
.and_then(|n| n.data.value().cloned())
.expect("StoredValue: disposed")
});
let borrow = value.borrow();
let typed = borrow
.downcast_ref::<T>()
.expect("StoredValue::with: type mismatch");
f(typed)
}
pub fn update<R>(self, f: impl FnOnce(&mut T) -> R) -> R {
let value = with_runtime(|rt| {
rt.nodes
.get(self.id)
.and_then(|n| n.data.value().cloned())
.expect("StoredValue: disposed")
});
let mut borrow = value.borrow_mut();
let typed = borrow
.downcast_mut::<T>()
.expect("StoredValue::update: type mismatch");
f(typed)
}
pub fn set(self, value: T) {
self.update(move |slot| *slot = value);
}
}
impl<T: 'static + Clone> StoredValue<T> {
pub fn get(self) -> T {
self.with(|v| v.clone())
}
}