// CoreSignal - internal reactive value primitive
// alien-signals style API
///|
/// Create a new core signal with initial value
pub fn[T] core_signal(initial : T) -> CoreSignal[T] {
{
node: ReactiveNode::new(ReactiveFlags::Mutable.to_int()),
current_value: initial,
pending_value: initial,
}
}
///|
/// Get the current value of a core signal
/// Automatically tracks dependency if called inside an effect/computed
pub fn[T] CoreSignal::get(self : CoreSignal[T]) -> T {
let flags = self.node.flags.get_value()
let dirty_flag = ReactiveFlags::Dirty.to_int()
// If dirty, update current from pending
if (flags & dirty_flag) != 0 {
if not(physical_equal(self.current_value, self.pending_value)) {
self.current_value = self.pending_value
if self.node.subs is Some(subs) {
shallow_propagate(subs)
}
}
self.node.flags.set_value(ReactiveFlags::Mutable.to_int())
}
// Track dependency
let mut found_sub = get_active_sub()
while found_sub is Some(s) {
let sub_flags = s.flags.get_value()
let mutable_flag = ReactiveFlags::Mutable.to_int()
let watching_flag = ReactiveFlags::Watching.to_int()
if (sub_flags & (mutable_flag | watching_flag)) != 0 {
link(self.node, s, get_cycle())
break
}
found_sub = match s.subs {
Some(subs) => Some(subs.sub)
None => None
}
}
self.current_value
}
///|
/// Set a new value for the core signal
/// Notifies all subscribers if value changed
pub fn[T] CoreSignal::set(self : CoreSignal[T], value : T) -> Unit {
if not(physical_equal(self.pending_value, value)) {
self.pending_value = value
// Track when this signal was modified for diamond dependency resolution
inc_cycle()
self.node.last_modified_cycle = get_cycle()
self.node.flags.set_value(
ReactiveFlags::Mutable.to_int() | ReactiveFlags::Dirty.to_int(),
)
if self.node.subs is Some(subs) {
propagate(subs)
if get_batch_depth() == 0 {
flush()
}
}
}
}
///|
/// Update core signal value using a function
pub fn[T] CoreSignal::update(self : CoreSignal[T], f : (T) -> T) -> Unit {
self.set(f(self.get()))
}
///|
/// Get value without tracking (peek)
/// Returns the latest value (pending if dirty, otherwise current)
pub fn[T] CoreSignal::peek(self : CoreSignal[T]) -> T {
let flags = self.node.flags.get_value()
let dirty_flag = ReactiveFlags::Dirty.to_int()
if (flags & dirty_flag) != 0 {
self.pending_value
} else {
self.current_value
}
}
///|
/// Get number of subscribers (for debugging)
pub fn[T] CoreSignal::subscriber_count(self : CoreSignal[T]) -> Int {
let mut count = 0
let mut lnk = self.node.subs
while lnk is Some(l) {
count = count + 1
lnk = l.next_sub
}
count
}
///|
/// Clear all subscribers
pub fn[T] CoreSignal::clear_subscribers(self : CoreSignal[T]) -> Unit {
// Unlink all subs
let mut lnk = self.node.subs
while lnk is Some(l) {
let next = l.next_sub
let _ = unlink(l, l.sub)
lnk = next
}
self.node.subs = None
self.node.subs_tail = None
}