use alloc::rc::Rc;
use crate::{NodeId, Reactor, current, trace_targets};
#[track_caller]
pub fn source() -> Source {
current().source()
}
#[track_caller]
pub fn source_in(reactor: &Reactor) -> Source {
reactor.source()
}
#[derive(Clone)]
pub struct Source {
inner: Rc<SourceInner>,
}
impl Reactor {
#[track_caller]
pub fn source(&self) -> Source {
Source::new(self.clone())
}
}
impl Source {
#[track_caller]
fn new(reactor: Reactor) -> Self {
let id = reactor.allocate_node();
tracing::debug!(
target: trace_targets::GRAPH,
event = "create_source",
node_id = id.0,
"created low-level reactive source"
);
Self {
inner: Rc::new(SourceInner { reactor, id }),
}
}
pub fn observe(&self) {
self.inner.reactor.observe(self.inner.id);
}
pub fn trigger(&self) {
self.inner.reactor.trigger(self.inner.id);
}
pub fn id(&self) -> NodeId {
self.inner.id
}
pub fn is_observed(&self) -> bool {
self.inner.reactor.is_observed(self.inner.id)
}
}
impl core::fmt::Debug for Source {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_struct("Source")
.field("id", &self.inner.id)
.finish()
}
}
struct SourceInner {
reactor: Reactor,
id: NodeId,
}
impl Drop for SourceInner {
fn drop(&mut self) {
self.reactor.dispose(self.id);
}
}
#[cfg(test)]
mod tests {
use std::cell::{Cell, RefCell};
use std::rc::Rc;
use runite::{queue_macrotask, run};
use crate::{EffectHandle, Reactor, Source, source_in};
#[test]
fn sources_have_distinct_ids() {
let reactor = Reactor::new();
let one = source_in(&reactor);
let two = source_in(&reactor);
assert_ne!(one.id(), two.id());
}
#[test]
fn observe_and_trigger_drive_an_effect_around_external_state() {
let seen = Rc::new(RefCell::new(Vec::new()));
let keep_alive = Rc::new(RefCell::new(None::<(Source, EffectHandle)>));
queue_macrotask({
let seen = Rc::clone(&seen);
let keep_alive = Rc::clone(&keep_alive);
move || {
let reactor = Reactor::new();
let source = source_in(&reactor);
let external = Rc::new(Cell::new(1usize));
let effect = reactor.effect({
let source = source.clone();
let external = Rc::clone(&external);
let seen = Rc::clone(&seen);
move || {
source.observe();
seen.borrow_mut().push(external.get());
}
});
*keep_alive.borrow_mut() = Some((source.clone(), effect));
runite::queue_macrotask(move || {
external.set(2);
source.trigger();
});
}
});
run();
assert_eq!(&*seen.borrow(), &[1, 2]);
}
}