use std::sync::Arc;
use crate::color::Color;
use slate_reactive::Signal;
pub enum Reactive<T> {
Static(T),
Dynamic(Arc<dyn Fn() -> T + Send + Sync>),
}
impl<T> Reactive<T> {
pub fn dynamic(f: impl Fn() -> T + Send + Sync + 'static) -> Self {
Reactive::Dynamic(Arc::new(f))
}
}
impl<T: Clone> Reactive<T> {
pub fn resolve(&self) -> T {
match self {
Reactive::Static(v) => v.clone(),
Reactive::Dynamic(f) => f(),
}
}
}
impl<T: Clone> Clone for Reactive<T> {
fn clone(&self) -> Self {
match self {
Reactive::Static(v) => Reactive::Static(v.clone()),
Reactive::Dynamic(f) => Reactive::Dynamic(f.clone()),
}
}
}
impl<T> From<T> for Reactive<T> {
fn from(value: T) -> Self {
Reactive::Static(value)
}
}
impl From<[f32; 4]> for Reactive<Color> {
fn from(arr: [f32; 4]) -> Self {
Reactive::Static(Color(arr))
}
}
impl<T: Clone + Send + Sync + 'static> From<Signal<T>> for Reactive<T> {
fn from(signal: Signal<T>) -> Self {
Reactive::dynamic(move || signal.get())
}
}
#[cfg(test)]
mod tests {
use super::*;
use slate_reactive::Runtime;
#[test]
fn static_resolves_to_value() {
let r: Reactive<f32> = 8.0.into();
assert_eq!(r.resolve(), 8.0);
}
#[test]
fn from_color_is_static() {
let r: Reactive<Color> = Color::RED.into();
assert_eq!(r.resolve(), Color::RED);
}
#[test]
fn from_array_becomes_color() {
let r: Reactive<Color> = [1.0, 0.0, 0.0, 1.0].into();
assert_eq!(r.resolve(), Color([1.0, 0.0, 0.0, 1.0]));
}
#[test]
fn dynamic_reflects_signal_after_set() {
let rt = Runtime::new();
let signal = Signal::new(rt, Color::RED);
let r: Reactive<Color> = signal.clone().into();
assert_eq!(r.resolve(), Color::RED);
signal.set(Color::BLUE);
assert_eq!(r.resolve(), Color::BLUE);
}
#[test]
fn dynamic_closure_reresolves() {
let r = Reactive::dynamic(|| 3.0_f32);
assert_eq!(r.resolve(), 3.0);
}
}