use std::any::Any;
use std::cell::RefCell;
use std::marker::PhantomData;
use std::rc::Rc;
use super::node::NodeKind;
use super::runtime::{self, maybe_flush, with_rt, RawId, MSG_DISPOSED};
pub struct Signal<T> {
pub(crate) id: RawId,
pub(crate) _marker: PhantomData<fn() -> T>,
}
impl<T> Copy for Signal<T> {}
impl<T> Clone for Signal<T> {
fn clone(&self) -> Self {
*self
}
}
impl<T> std::fmt::Debug for Signal<T> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(
f,
"Signal({}, gen {})",
self.id.key.index, self.id.key.generation
)
}
}
impl<T> PartialEq for Signal<T> {
fn eq(&self, other: &Self) -> bool {
self.id == other.id
}
}
impl<T> Eq for Signal<T> {}
fn cell_of(id: RawId, track: bool) -> Rc<RefCell<Box<dyn Any>>> {
with_rt(|rt| {
rt.check_thread(id);
if track {
rt.track_read(id.key);
}
match &rt
.graph
.get(id.key)
.unwrap_or_else(|| panic!("{MSG_DISPOSED}"))
.kind
{
NodeKind::Signal { value } => value.clone(),
_ => unreachable!("typed handle points at a non-signal node"),
}
})
}
impl<T: 'static> Signal<T> {
pub fn with<R>(self, f: impl FnOnce(&T) -> R) -> R {
let cell = cell_of(self.id, true);
let guard = cell.borrow();
f(guard.downcast_ref::<T>().expect("signal value type"))
}
pub fn get(self) -> T
where
T: Clone,
{
self.with(T::clone)
}
pub fn with_untracked<R>(self, f: impl FnOnce(&T) -> R) -> R {
let cell = cell_of(self.id, false);
let guard = cell.borrow();
f(guard.downcast_ref::<T>().expect("signal value type"))
}
pub fn get_untracked(self) -> T
where
T: Clone,
{
self.with_untracked(T::clone)
}
pub fn try_get_untracked(self) -> Option<T>
where
T: Clone,
{
if !self.is_alive() {
return None;
}
Some(self.with_untracked(T::clone))
}
pub fn set(self, value: T) {
let cell = cell_of(self.id, false);
let old = {
let mut guard = cell.borrow_mut();
std::mem::replace(&mut *guard, Box::new(value) as Box<dyn Any>)
};
drop(old);
with_rt(|rt| rt.mark_written(self.id.key));
maybe_flush();
}
pub fn update(self, f: impl FnOnce(&mut T)) {
let cell = cell_of(self.id, false);
{
let mut guard = cell.borrow_mut();
f(guard.downcast_mut::<T>().expect("signal value type"));
}
with_rt(|rt| rt.mark_written(self.id.key));
maybe_flush();
}
pub fn set_if_changed(self, value: T) -> bool
where
T: PartialEq,
{
let cell = cell_of(self.id, false);
let old = {
let mut guard = cell.borrow_mut();
let current = guard.downcast_mut::<T>().expect("signal value type");
if *current == value {
return false;
}
std::mem::replace(&mut *guard, Box::new(value) as Box<dyn Any>)
};
drop(old);
with_rt(|rt| rt.mark_written(self.id.key));
maybe_flush();
true
}
pub fn is_alive(self) -> bool {
with_rt(|rt| rt.rt_id == self.id.rt && rt.graph.contains(self.id.key))
}
pub fn dispose(self) {
runtime::dispose_node(self.id);
}
}