use super::signal::{ReadSignal, RwSignal};
use super::stored::StoredValue;
pub enum Signal<T: 'static> {
Stored(StoredValue<T>),
Dynamic(ReadSignal<T>),
}
impl<T: 'static> Clone for Signal<T> {
fn clone(&self) -> Self {
*self
}
}
impl<T: 'static> Copy for Signal<T> {}
impl<T: 'static + Clone> Signal<T> {
pub fn get(&self) -> T {
match self {
Signal::Stored(v) => v.get(),
Signal::Dynamic(sig) => sig.get(),
}
}
}
impl<T: 'static> From<T> for Signal<T> {
fn from(v: T) -> Self {
Signal::Stored(StoredValue::new(v))
}
}
impl<T: 'static + Default> Default for Signal<T> {
fn default() -> Self {
Signal::Stored(StoredValue::new(T::default()))
}
}
impl<T: 'static + Clone> From<ReadSignal<T>> for Signal<T> {
fn from(s: ReadSignal<T>) -> Self {
Signal::Dynamic(s)
}
}
impl<T: 'static + Clone> From<RwSignal<T>> for Signal<T> {
fn from(s: RwSignal<T>) -> Self {
Signal::Dynamic(s.read_only())
}
}
impl From<&str> for Signal<String> {
fn from(s: &str) -> Self {
Signal::Stored(StoredValue::new(s.to_string()))
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::reactive::{__reset_for_tests, computed, effect, flush, signal, RwSignal};
use std::cell::RefCell;
use std::rc::Rc;
#[test]
fn static_variant_returns_held_value() {
__reset_for_tests();
let s: Signal<&'static str> = "hello".into();
assert!(matches!(s, Signal::Stored(_)));
assert_eq!(s.get(), "hello");
}
#[test]
fn signal_is_copy() {
__reset_for_tests();
fn assert_copy<C: Copy>(_: &C) {}
let s: Signal<String> = "abc".into();
assert_copy(&s);
let first = move || s.get();
let second = move || s.get();
assert_eq!(first(), "abc");
assert_eq!(second(), "abc");
let (count, _set) = signal(5_i32);
let d: Signal<i32> = count.into();
assert_copy(&d);
let a = move || d.get();
let b = move || d.get();
assert_eq!(a(), 5);
assert_eq!(b(), 5);
}
#[test]
fn read_signal_variant_returns_current_value() {
__reset_for_tests();
let (count, set_count) = signal(0_i32);
let s: Signal<i32> = count.into();
assert!(matches!(s, Signal::Dynamic(_)));
assert_eq!(s.get(), 0);
set_count.set(7);
flush();
assert_eq!(s.get(), 7);
}
#[test]
fn rw_signal_converts_to_dynamic_variant() {
__reset_for_tests();
let rw = RwSignal::new(42_i32);
let s: Signal<i32> = rw.into();
assert!(matches!(s, Signal::Dynamic(_)));
assert_eq!(s.get(), 42);
}
#[test]
fn dynamic_signal_get_inside_effect_registers_dep() {
__reset_for_tests();
let (count, set_count) = signal(0_i32);
let s: Signal<i32> = count.into();
let log: Rc<RefCell<Vec<i32>>> = Rc::new(RefCell::new(Vec::new()));
let log_clone = log.clone();
effect(move || {
log_clone.borrow_mut().push(s.get());
});
assert_eq!(&*log.borrow(), &[0]);
set_count.set(1);
flush();
set_count.set(2);
flush();
assert_eq!(&*log.borrow(), &[0, 1, 2]);
}
#[test]
fn static_signal_get_inside_effect_does_not_subscribe() {
__reset_for_tests();
let s: Signal<i32> = 100.into();
let log: Rc<RefCell<Vec<i32>>> = Rc::new(RefCell::new(Vec::new()));
let log_clone = log.clone();
effect(move || {
log_clone.borrow_mut().push(s.get());
});
assert_eq!(&*log.borrow(), &[100]);
}
#[test]
fn computed_return_value_converts_into_dynamic_signal() {
__reset_for_tests();
let (count, set_count) = signal(0_i32);
let doubled = computed(move || count.get() * 2);
let s: Signal<i32> = doubled.into();
assert!(matches!(s, Signal::Dynamic(_)));
assert_eq!(s.get(), 0);
set_count.set(5);
flush();
assert_eq!(s.get(), 10);
}
}