sycamore_reactive/
maybe_dyn.rs1use std::borrow::Cow;
2use std::rc::Rc;
3
4use crate::*;
5
6#[derive(Clone)]
24pub enum MaybeDyn<T>
25where
26 T: Into<Self> + 'static,
27{
28 Static(T),
30 Signal(ReadSignal<T>),
32 Derived(Rc<dyn Fn() -> Self>),
34}
35
36impl<T: Into<Self> + 'static> MaybeDyn<T> {
37 pub fn evaluate(self) -> T
40 where
41 T: Clone,
42 {
43 match self {
44 Self::Static(value) => value,
45 Self::Signal(signal) => signal.get_clone(),
46 Self::Derived(f) => f().evaluate(),
47 }
48 }
49
50 pub fn get(&self) -> T
55 where
56 T: Copy,
57 {
58 match self {
59 Self::Static(value) => *value,
60 Self::Signal(value) => value.get(),
61 Self::Derived(f) => f().evaluate(),
62 }
63 }
64
65 pub fn get_clone(&self) -> T
69 where
70 T: Clone,
71 {
72 match self {
73 Self::Static(value) => value.clone(),
74 Self::Signal(value) => value.get_clone(),
75 Self::Derived(f) => f().evaluate(),
76 }
77 }
78
79 pub fn track(&self) {
81 match self {
82 Self::Static(_) => {}
83 Self::Signal(signal) => signal.track(),
84 Self::Derived(f) => f().track(),
85 }
86 }
87
88 pub fn as_static(&self) -> Option<&T> {
90 match self {
91 Self::Static(value) => Some(value),
92 _ => None,
93 }
94 }
95}
96
97impl<T: Into<Self>, U: Into<MaybeDyn<T>> + Clone> From<ReadSignal<U>> for MaybeDyn<T> {
98 fn from(val: ReadSignal<U>) -> Self {
99 if let Some(val) =
104 (&mut Some(val) as &mut dyn std::any::Any).downcast_mut::<Option<ReadSignal<T>>>()
105 {
106 MaybeDyn::Signal(val.unwrap())
107 } else {
108 MaybeDyn::Derived(Rc::new(move || val.get_clone().into()))
109 }
110 }
111}
112
113impl<T: Into<Self>, U: Into<MaybeDyn<T>> + Clone> From<Signal<U>> for MaybeDyn<T> {
114 fn from(val: Signal<U>) -> Self {
115 Self::from(*val)
116 }
117}
118
119impl<F, U, T: Into<Self>> From<F> for MaybeDyn<T>
121where
122 F: Fn() -> U + 'static,
123 U: Into<MaybeDyn<T>>,
124{
125 fn from(f: F) -> Self {
126 MaybeDyn::Derived(Rc::new(move || f().into()))
127 }
128}
129
130#[macro_export]
154macro_rules! impl_into_maybe_dyn {
155 ($ty:ty $(; $($from:ty),*)?) => {
156 impl From<$ty> for $crate::MaybeDyn<$ty> {
157 fn from(val: $ty) -> Self {
158 MaybeDyn::Static(val)
159 }
160 }
161
162 $crate::impl_into_maybe_dyn_with_convert!($ty; Into::into $(; $($from),*)?);
163 };
164}
165
166#[macro_export]
170macro_rules! impl_into_maybe_dyn_with_convert {
171 ($ty:ty; $convert:expr $(; $($from:ty),*)?) => {
172 $(
173 $(
174 impl From<$from> for $crate::MaybeDyn<$ty> {
175 fn from(val: $from) -> Self {
176 MaybeDyn::Static($convert(val))
177 }
178 }
179 )*
180 )?
181 };
182}
183
184impl_into_maybe_dyn!(Cow<'static, str>; &'static str, String);
185impl_into_maybe_dyn_with_convert!(
186 Option<Cow<'static, str>>; |x| Some(Into::into(x));
187 Cow<'static, str>, &'static str, String
188);
189impl_into_maybe_dyn_with_convert!(
190 Option<Cow<'static, str>>; |x| Option::map(x, Into::into);
191 Option<&'static str>, Option<String>
192);
193
194impl_into_maybe_dyn!(bool);
195
196impl_into_maybe_dyn!(f32);
197impl_into_maybe_dyn!(f64);
198
199impl_into_maybe_dyn!(i8);
200impl_into_maybe_dyn!(i16);
201impl_into_maybe_dyn!(i32);
202impl_into_maybe_dyn!(i64);
203impl_into_maybe_dyn!(i128);
204impl_into_maybe_dyn!(isize);
205impl_into_maybe_dyn!(u8);
206impl_into_maybe_dyn!(u16);
207impl_into_maybe_dyn!(u32);
208impl_into_maybe_dyn!(u64);
209impl_into_maybe_dyn!(u128);
210impl_into_maybe_dyn!(usize);
211
212impl<T> From<Option<T>> for MaybeDyn<Option<T>> {
213 fn from(val: Option<T>) -> Self {
214 MaybeDyn::Static(val)
215 }
216}
217
218impl<T> From<Vec<T>> for MaybeDyn<Vec<T>> {
219 fn from(val: Vec<T>) -> Self {
220 MaybeDyn::Static(val)
221 }
222}
223
224#[cfg(feature = "wasm-bindgen")]
225impl_into_maybe_dyn!(
226 wasm_bindgen::JsValue;
227 &'static str, String, bool, f32, f64, i8, i16, i32, i64, i128, isize, u8, u16, u32, u64, u128, usize
228);
229
230#[cfg(test)]
231mod tests {
232 use super::*;
233
234 #[test]
235 fn maybe_dyn_static() {
236 let value = MaybeDyn::<i32>::from(123);
237 assert!(value.as_static().is_some());
238 assert_eq!(value.get(), 123);
239 assert_eq!(value.get_clone(), 123);
240 assert_eq!(value.evaluate(), 123);
241 }
242
243 #[test]
244 fn maybe_dyn_signal() {
245 let _ = create_root(move || {
246 let signal = create_signal(123);
247 let value = MaybeDyn::<i32>::from(signal);
248 assert!(matches!(value, MaybeDyn::Signal(_)));
249
250 assert_eq!(value.get(), 123);
251 assert_eq!(value.get_clone(), 123);
252 assert_eq!(value.evaluate(), 123);
253 });
254 }
255
256 #[test]
257 fn maybe_dyn_signal_from() {
258 let _ = create_root(move || {
259 let signal = create_signal("abc");
260 let value = MaybeDyn::<Cow<'static, str>>::from(signal);
261 assert!(matches!(value, MaybeDyn::Derived(_)));
262
263 assert_eq!(value.get_clone(), "abc");
264 assert_eq!(value.evaluate(), "abc");
265 });
266 }
267
268 #[test]
269 fn maybe_dyn_derived() {
270 let value = MaybeDyn::<i32>::from(|| 123);
271 assert!(value.as_static().is_none());
272 assert_eq!(value.get(), 123);
273 assert_eq!(value.get_clone(), 123);
274 assert_eq!(value.evaluate(), 123);
275 }
276}