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::scheduler;
use super::with_runtime;
pub fn signal<T: 'static>(initial: T) -> (ReadSignal<T>, WriteSignal<T>) {
let id = alloc_signal_node(initial);
(
ReadSignal {
id,
_ty: PhantomData,
},
WriteSignal {
id,
_ty: PhantomData,
},
)
}
fn alloc_signal_node<T: 'static>(initial: T) -> NodeId {
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("signal()");
}
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
})
}
pub struct ReadSignal<T: 'static> {
pub(crate) id: NodeId,
pub(crate) _ty: PhantomData<fn() -> T>,
}
impl<T: 'static> Clone for ReadSignal<T> {
fn clone(&self) -> Self {
*self
}
}
impl<T: 'static> Copy for ReadSignal<T> {}
impl<T: 'static + Clone> ReadSignal<T> {
pub fn get(self) -> T {
self.with(|v| v.clone())
}
pub fn get_untracked(self) -> T {
self.with_untracked(|v| v.clone())
}
}
impl<T: 'static> ReadSignal<T> {
pub fn with<R>(self, f: impl FnOnce(&T) -> R) -> R {
let value = fetch_value(self.id);
let arc_handle: Option<super::arc_signal::ArcRwSignal<T>> = {
let borrow = value.borrow();
borrow
.downcast_ref::<super::arc_signal::ArcRwSignal<T>>()
.cloned()
};
if let Some(arc) = arc_handle {
return arc.with(f);
}
track_node(self.id);
let borrow = value.borrow();
let typed = borrow
.downcast_ref::<T>()
.expect("ReadSignal::with: type mismatch — signal storage corrupted");
f(typed)
}
pub fn with_untracked<R>(self, f: impl FnOnce(&T) -> R) -> R {
let value = fetch_value(self.id);
let arc_handle: Option<super::arc_signal::ArcRwSignal<T>> = {
let borrow = value.borrow();
borrow
.downcast_ref::<super::arc_signal::ArcRwSignal<T>>()
.cloned()
};
if let Some(arc) = arc_handle {
return arc.with_untracked(f);
}
let borrow = value.borrow();
let typed = borrow
.downcast_ref::<T>()
.expect("ReadSignal::with_untracked: type mismatch — signal storage corrupted");
f(typed)
}
}
pub struct WriteSignal<T: 'static> {
pub(crate) id: NodeId,
pub(crate) _ty: PhantomData<fn(T)>,
}
impl<T: 'static> Clone for WriteSignal<T> {
fn clone(&self) -> Self {
*self
}
}
impl<T: 'static> Copy for WriteSignal<T> {}
impl<T: 'static> WriteSignal<T> {
pub fn set(self, value: T) {
self.update(move |slot| *slot = value);
}
pub fn update(self, f: impl FnOnce(&mut T)) {
write_and_notify(self.id, f, true);
}
pub fn update_untracked(self, f: impl FnOnce(&mut T)) {
write_and_notify(self.id, f, false);
}
}
pub struct RwSignal<T: 'static> {
id: NodeId,
_ty: PhantomData<fn(T) -> T>,
}
impl<T: 'static> Clone for RwSignal<T> {
fn clone(&self) -> Self {
*self
}
}
impl<T: 'static> Copy for RwSignal<T> {}
impl<T: 'static> RwSignal<T> {
pub fn new(initial: T) -> Self {
let id = alloc_signal_node(initial);
Self {
id,
_ty: PhantomData,
}
}
pub fn read_only(self) -> ReadSignal<T> {
ReadSignal {
id: self.id,
_ty: PhantomData,
}
}
pub fn split(self) -> (ReadSignal<T>, WriteSignal<T>) {
(
ReadSignal {
id: self.id,
_ty: PhantomData,
},
WriteSignal {
id: self.id,
_ty: PhantomData,
},
)
}
pub fn with<R>(self, f: impl FnOnce(&T) -> R) -> R {
ReadSignal::<T> {
id: self.id,
_ty: PhantomData,
}
.with(f)
}
pub fn with_untracked<R>(self, f: impl FnOnce(&T) -> R) -> R {
ReadSignal::<T> {
id: self.id,
_ty: PhantomData,
}
.with_untracked(f)
}
pub fn update(self, f: impl FnOnce(&mut T)) {
WriteSignal::<T> {
id: self.id,
_ty: PhantomData,
}
.update(f);
}
pub fn update_untracked(self, f: impl FnOnce(&mut T)) {
WriteSignal::<T> {
id: self.id,
_ty: PhantomData,
}
.update_untracked(f);
}
pub fn set(self, value: T) {
WriteSignal::<T> {
id: self.id,
_ty: PhantomData,
}
.set(value);
}
pub fn try_set(self, value: T) -> bool {
try_write_and_notify(self.id, move |slot| *slot = value, true)
}
pub fn try_update(self, f: impl FnOnce(&mut T)) -> bool {
try_write_and_notify(self.id, f, true)
}
}
impl<T: 'static + Clone> RwSignal<T> {
pub fn get(self) -> T {
ReadSignal::<T> {
id: self.id,
_ty: PhantomData,
}
.get()
}
pub fn get_untracked(self) -> T {
ReadSignal::<T> {
id: self.id,
_ty: PhantomData,
}
.get_untracked()
}
}
fn register_arc_in_current_owner<T: 'static>(arc: super::arc_signal::ArcRwSignal<T>) -> NodeId {
let value: Rc<RefCell<dyn Any>> = Rc::new(RefCell::new(arc));
let needs_warning = with_runtime(|rt| rt.current_owner().is_none());
if needs_warning {
super::warn_no_owner("ArcRwSignal::into::<RwSignal>");
}
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
})
}
impl<T: 'static> From<super::arc_signal::ArcRwSignal<T>> for RwSignal<T> {
fn from(arc: super::arc_signal::ArcRwSignal<T>) -> Self {
let id = register_arc_in_current_owner(arc);
RwSignal {
id,
_ty: PhantomData,
}
}
}
impl<T: 'static> From<super::arc_signal::ArcReadSignal<T>> for ReadSignal<T> {
fn from(arc_r: super::arc_signal::ArcReadSignal<T>) -> Self {
let arc = super::arc_signal::ArcRwSignal { inner: arc_r.inner };
let id = register_arc_in_current_owner(arc);
ReadSignal {
id,
_ty: PhantomData,
}
}
}
impl<T: 'static> From<super::arc_signal::ArcWriteSignal<T>> for WriteSignal<T> {
fn from(arc_w: super::arc_signal::ArcWriteSignal<T>) -> Self {
let arc = super::arc_signal::ArcRwSignal { inner: arc_w.inner };
let id = register_arc_in_current_owner(arc);
WriteSignal {
id,
_ty: PhantomData,
}
}
}
fn track_node(id: NodeId) {
with_runtime(|rt| {
if let Some(tracker) = rt.current_tracker {
if tracker != id {
if let Some(node) = rt.nodes.get_mut(id) {
node.subscribers.insert(tracker);
}
if let Some(track_node) = rt.nodes.get_mut(tracker) {
track_node.sources.insert(id);
}
}
}
})
}
fn fetch_value(id: NodeId) -> Rc<RefCell<dyn Any>> {
with_runtime(|rt| {
rt.nodes
.get(id)
.and_then(|n| n.data.value().cloned())
.expect("ReadSignal: signal disposed or not a value-bearing node")
})
}
fn write_and_notify<T: 'static>(id: NodeId, f: impl FnOnce(&mut T), notify: bool) {
let _ = try_write_and_notify(id, f, notify);
}
fn try_write_and_notify<T: 'static>(id: NodeId, f: impl FnOnce(&mut T), notify: bool) -> bool {
let Some(value) = with_runtime(|rt| rt.nodes.get(id).and_then(|n| n.data.value().cloned()))
else {
return false;
};
let arc_handle: Option<super::arc_signal::ArcRwSignal<T>> = {
let borrow = value.borrow();
borrow
.downcast_ref::<super::arc_signal::ArcRwSignal<T>>()
.cloned()
};
if let Some(arc) = arc_handle {
if notify {
arc.update(f);
} else {
arc.update_untracked(f);
}
return true;
}
{
let mut borrow = value.borrow_mut();
let typed = borrow
.downcast_mut::<T>()
.expect("WriteSignal: type mismatch — signal storage corrupted");
f(typed);
}
if notify {
let subscribers: Vec<NodeId> = with_runtime(|rt| {
rt.nodes
.get(id)
.map(|n| n.subscribers.iter().copied().collect())
.unwrap_or_default()
});
for sub in subscribers {
scheduler::schedule(sub);
}
}
true
}