use std::any::TypeId;
use std::collections::hash_map::Entry;
use super::owner::with_root_owner;
use super::signal::{RwSignal, create_signal};
use super::state::with_reactive;
pub(crate) struct GlobalSignal<T: 'static> {
init: fn() -> T,
}
impl<T: Clone + Send + 'static> GlobalSignal<T> {
pub(crate) const fn new(init: fn() -> T) -> Self {
Self { init }
}
pub(crate) fn get(&'static self) -> RwSignal<T> {
let key = self as *const Self as usize;
let cached = with_reactive(|reactive| reactive.globals.borrow().get(&key).copied());
if let Some((id, declared)) = cached {
debug_assert_eq!(
declared,
TypeId::of::<T>(),
"two globals share an address: one of them is reading the other's signal"
);
if crate::reactive::storage::has_signal(id) {
return RwSignal::from_id(id);
}
}
let signal = with_root_owner(|| create_signal((self.init)()));
with_reactive(|reactive| {
match reactive.globals.borrow_mut().entry(key) {
Entry::Occupied(e) => RwSignal::from_id(e.get().0),
Entry::Vacant(e) => {
e.insert((signal.id(), TypeId::of::<T>()));
signal
}
}
})
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::reactive::owner::{create_root_owner, dispose_owner_now, with_owner};
static COUNT: GlobalSignal<u32> = GlobalSignal::new(|| 7);
static OTHER_COUNT: GlobalSignal<u32> = GlobalSignal::new(|| 100);
#[test]
fn a_global_outlives_the_scope_that_read_it_first() {
create_root_owner();
let ((), first_reader) = with_owner(|| {
assert_eq!(COUNT.get().get_untracked(), 7);
});
dispose_owner_now(first_reader);
assert_eq!(COUNT.get().get_untracked(), 7, "still readable");
COUNT.get().set(9);
assert_eq!(COUNT.get().get_untracked(), 9, "and still writable");
}
#[test]
fn every_read_of_one_global_is_the_same_signal() {
create_root_owner();
assert!(COUNT.get() == COUNT.get());
}
#[test]
fn a_global_whose_signal_is_gone_makes_another() {
create_root_owner();
COUNT.get().set(3);
assert_eq!(COUNT.get().get_untracked(), 3);
crate::reactive::storage::discard_all_signals();
assert_eq!(
COUNT.get().get_untracked(),
7,
"back to what the declaration says, not a panic"
);
}
#[test]
fn two_globals_of_one_type_are_two_globals() {
create_root_owner();
COUNT.get().set(1);
OTHER_COUNT.get().set(2);
assert_eq!(COUNT.get().get_untracked(), 1);
assert_eq!(OTHER_COUNT.get().get_untracked(), 2);
}
}