use std::borrow::Cow;
use std::rc::Rc;
use crate::*;
#[derive(Clone)]
pub enum MaybeDyn<T>
where
T: Into<Self> + 'static,
{
Static(T),
Signal(ReadSignal<T>),
Derived(Rc<dyn Fn() -> Self>),
}
impl<T: Into<Self> + 'static> MaybeDyn<T> {
pub fn evaluate(self) -> T
where
T: Clone,
{
match self {
Self::Static(value) => value,
Self::Signal(signal) => signal.get_clone(),
Self::Derived(f) => f().evaluate(),
}
}
pub fn get(&self) -> T
where
T: Copy,
{
match self {
Self::Static(value) => *value,
Self::Signal(value) => value.get(),
Self::Derived(f) => f().evaluate(),
}
}
pub fn get_clone(&self) -> T
where
T: Clone,
{
match self {
Self::Static(value) => value.clone(),
Self::Signal(value) => value.get_clone(),
Self::Derived(f) => f().evaluate(),
}
}
pub fn track(&self) {
match self {
Self::Static(_) => {}
Self::Signal(signal) => signal.track(),
Self::Derived(f) => f().track(),
}
}
pub fn as_static(&self) -> Option<&T> {
match self {
Self::Static(value) => Some(value),
_ => None,
}
}
}
impl<T: Into<Self>, U: Into<MaybeDyn<T>> + Clone> From<ReadSignal<U>> for MaybeDyn<T> {
fn from(val: ReadSignal<U>) -> Self {
if let Some(val) =
(&mut Some(val) as &mut dyn std::any::Any).downcast_mut::<Option<ReadSignal<T>>>()
{
MaybeDyn::Signal(val.unwrap())
} else {
MaybeDyn::Derived(Rc::new(move || val.get_clone().into()))
}
}
}
impl<T: Into<Self>, U: Into<MaybeDyn<T>> + Clone> From<Signal<U>> for MaybeDyn<T> {
fn from(val: Signal<U>) -> Self {
Self::from(*val)
}
}
impl<F, U, T: Into<Self>> From<F> for MaybeDyn<T>
where
F: Fn() -> U + 'static,
U: Into<MaybeDyn<T>>,
{
fn from(f: F) -> Self {
MaybeDyn::Derived(Rc::new(move || f().into()))
}
}
#[macro_export]
macro_rules! impl_into_maybe_dyn {
($ty:ty $(; $($from:ty),*)?) => {
impl From<$ty> for $crate::MaybeDyn<$ty> {
fn from(val: $ty) -> Self {
MaybeDyn::Static(val)
}
}
$crate::impl_into_maybe_dyn_with_convert!($ty; Into::into $(; $($from),*)?);
};
}
#[macro_export]
macro_rules! impl_into_maybe_dyn_with_convert {
($ty:ty; $convert:expr $(; $($from:ty),*)?) => {
$(
$(
impl From<$from> for $crate::MaybeDyn<$ty> {
fn from(val: $from) -> Self {
MaybeDyn::Static($convert(val))
}
}
)*
)?
};
}
impl_into_maybe_dyn!(Cow<'static, str>; &'static str, String);
impl_into_maybe_dyn_with_convert!(
Option<Cow<'static, str>>; |x| Some(Into::into(x));
Cow<'static, str>, &'static str, String
);
impl_into_maybe_dyn_with_convert!(
Option<Cow<'static, str>>; |x| Option::map(x, Into::into);
Option<&'static str>, Option<String>
);
impl_into_maybe_dyn!(bool);
impl_into_maybe_dyn!(f32);
impl_into_maybe_dyn!(f64);
impl_into_maybe_dyn!(i8);
impl_into_maybe_dyn!(i16);
impl_into_maybe_dyn!(i32);
impl_into_maybe_dyn!(i64);
impl_into_maybe_dyn!(i128);
impl_into_maybe_dyn!(isize);
impl_into_maybe_dyn!(u8);
impl_into_maybe_dyn!(u16);
impl_into_maybe_dyn!(u32);
impl_into_maybe_dyn!(u64);
impl_into_maybe_dyn!(u128);
impl_into_maybe_dyn!(usize);
impl<T> From<Option<T>> for MaybeDyn<Option<T>> {
fn from(val: Option<T>) -> Self {
MaybeDyn::Static(val)
}
}
impl<T> From<Vec<T>> for MaybeDyn<Vec<T>> {
fn from(val: Vec<T>) -> Self {
MaybeDyn::Static(val)
}
}
#[cfg(feature = "wasm-bindgen")]
impl_into_maybe_dyn!(
wasm_bindgen::JsValue;
&'static str, String, bool, f32, f64, i8, i16, i32, i64, i128, isize, u8, u16, u32, u64, u128, usize
);
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn maybe_dyn_static() {
let value = MaybeDyn::<i32>::from(123);
assert!(value.as_static().is_some());
assert_eq!(value.get(), 123);
assert_eq!(value.get_clone(), 123);
assert_eq!(value.evaluate(), 123);
}
#[test]
fn maybe_dyn_signal() {
let _ = create_root(move || {
let signal = create_signal(123);
let value = MaybeDyn::<i32>::from(signal);
assert!(matches!(value, MaybeDyn::Signal(_)));
assert_eq!(value.get(), 123);
assert_eq!(value.get_clone(), 123);
assert_eq!(value.evaluate(), 123);
});
}
#[test]
fn maybe_dyn_signal_from() {
let _ = create_root(move || {
let signal = create_signal("abc");
let value = MaybeDyn::<Cow<'static, str>>::from(signal);
assert!(matches!(value, MaybeDyn::Derived(_)));
assert_eq!(value.get_clone(), "abc");
assert_eq!(value.evaluate(), "abc");
});
}
#[test]
fn maybe_dyn_derived() {
let value = MaybeDyn::<i32>::from(|| 123);
assert!(value.as_static().is_none());
assert_eq!(value.get(), 123);
assert_eq!(value.get_clone(), 123);
assert_eq!(value.evaluate(), 123);
}
}