use super::runtime::{RuntimeHandle, SignalId};
use std::fmt;
use std::marker::PhantomData;
pub struct Signal<T> {
pub(crate) id: SignalId,
pub(crate) rt: RuntimeHandle,
pub(crate) _marker: PhantomData<T>,
}
impl<T: Clone + 'static> Signal<T> {
pub fn get(&self) -> T {
self.rt.get_signal(self.id)
}
}
impl<T: 'static> Signal<T> {
pub fn set(&self, value: T) {
self.rt.set_signal(self.id, value);
}
pub fn id(&self) -> SignalId {
self.id
}
}
impl<T: Clone + 'static> Signal<T> {
pub fn update<F>(&self, f: F)
where
F: FnOnce(T) -> T,
{
let current = self.get();
self.set(f(current));
}
}
impl<T> Clone for Signal<T> {
fn clone(&self) -> Self {
Self {
id: self.id,
rt: self.rt.clone(),
_marker: PhantomData,
}
}
}
impl<T> fmt::Debug for Signal<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("Signal")
.field("id", &self.id)
.finish_non_exhaustive()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_signal_get_set() {
let rt = RuntimeHandle::new();
let id = rt.create_signal(42i32);
let signal: Signal<i32> = Signal {
id,
rt,
_marker: PhantomData,
};
assert_eq!(signal.get(), 42);
signal.set(100);
assert_eq!(signal.get(), 100);
}
#[test]
fn test_signal_update() {
let rt = RuntimeHandle::new();
let id = rt.create_signal(5i32);
let signal: Signal<i32> = Signal {
id,
rt,
_marker: PhantomData,
};
signal.update(|n| n * 2);
assert_eq!(signal.get(), 10);
}
#[test]
fn test_signal_clone() {
let rt = RuntimeHandle::new();
let id = rt.create_signal(0i32);
let signal1: Signal<i32> = Signal {
id,
rt,
_marker: PhantomData,
};
let signal2 = signal1.clone();
signal1.set(42);
assert_eq!(signal2.get(), 42);
}
#[test]
fn test_signal_multiple_clones_share_state() {
let rt = RuntimeHandle::new();
let id = rt.create_signal(0i32);
let signal1: Signal<i32> = Signal {
id,
rt,
_marker: PhantomData,
};
let signal2 = signal1.clone();
signal1.set(10);
assert_eq!(signal2.get(), 10);
}
#[test]
fn test_signal_string() {
let rt = RuntimeHandle::new();
let id = rt.create_signal(String::from("hello"));
let signal: Signal<String> = Signal {
id,
rt,
_marker: PhantomData,
};
assert_eq!(signal.get(), "hello");
signal.set(String::from("world"));
assert_eq!(signal.get(), "world");
}
#[test]
fn test_signal_vec() {
let rt = RuntimeHandle::new();
let id = rt.create_signal(vec![1, 2, 3]);
let signal: Signal<Vec<i32>> = Signal {
id,
rt,
_marker: PhantomData,
};
assert_eq!(signal.get(), vec![1, 2, 3]);
signal.update(|mut v| {
v.push(4);
v
});
assert_eq!(signal.get(), vec![1, 2, 3, 4]);
}
#[test]
fn test_signal_debug() {
let rt = RuntimeHandle::new();
let id = rt.create_signal(0i32);
let signal: Signal<i32> = Signal {
id,
rt,
_marker: PhantomData,
};
let debug_str = format!("{:?}", signal);
assert!(debug_str.contains("Signal"));
}
#[test]
fn test_signal_marks_dirty() {
let rt = RuntimeHandle::new();
let id = rt.create_signal(0i32);
let signal: Signal<i32> = Signal {
id,
rt: rt.clone(),
_marker: PhantomData,
};
assert!(!rt.needs_render());
signal.set(1);
assert!(rt.needs_render());
}
}