1use std::{any::Any, ops::Deref};
2
3use dioxus_core::{IntoAttributeValue, IntoDynNode, Subscribers};
4use generational_box::{BorrowResult, Storage, SyncStorage, UnsyncStorage};
5
6use crate::{
7 read_impls, write_impls, CopyValue, Global, InitializeFromFunction, MappedMutSignal,
8 MappedSignal, Memo, Readable, ReadableExt, ReadableRef, Signal, SignalData, Writable,
9 WritableExt,
10};
11
12#[deprecated(
14 since = "0.7.0",
15 note = "Use `ReadSignal` instead. Will be removed in 0.8"
16)]
17pub type ReadOnlySignal<T, S = UnsyncStorage> = ReadSignal<T, S>;
18
19pub struct ReadSignal<T: ?Sized, S: BoxedSignalStorage<T> = UnsyncStorage> {
21 value: CopyValue<Box<S::DynReadable<sealed::SealedToken>>, S>,
22}
23
24impl<T: ?Sized + 'static> ReadSignal<T> {
25 pub fn new(value: impl Readable<Target = T, Storage = UnsyncStorage> + 'static) -> Self {
27 Self::new_maybe_sync(value)
28 }
29}
30
31impl<T: ?Sized + 'static, S: BoxedSignalStorage<T>> ReadSignal<T, S> {
32 pub fn new_maybe_sync<R>(value: R) -> Self
34 where
35 S: CreateBoxedSignalStorage<R>,
36 R: Readable<Target = T>,
37 {
38 Self {
39 value: CopyValue::new_maybe_sync(S::new_readable(value, sealed::SealedToken)),
40 }
41 }
42
43 pub fn point_to(&self, other: Self) -> BorrowResult {
45 let this_subscribers = self.subscribers();
46 let mut this_subscribers_vec = Vec::new();
47 this_subscribers.visit(|subscriber| this_subscribers_vec.push(*subscriber));
49 let other_subscribers = other.subscribers();
50 for subscriber in this_subscribers_vec {
51 subscriber.subscribe(other_subscribers.clone());
52 }
53 self.value.point_to(other.value)?;
54 Ok(())
55 }
56
57 #[doc(hidden)]
58 pub fn mark_dirty(&mut self) {
61 let subscribers = self.subscribers();
62 let mut this_subscribers_vec = Vec::new();
63 subscribers.visit(|subscriber| this_subscribers_vec.push(*subscriber));
64 for subscriber in this_subscribers_vec {
65 subscribers.remove(&subscriber);
66 subscriber.mark_dirty();
67 }
68 }
69}
70
71impl<T: ?Sized, S: BoxedSignalStorage<T>> Clone for ReadSignal<T, S> {
72 fn clone(&self) -> Self {
73 *self
74 }
75}
76
77impl<T: ?Sized, S: BoxedSignalStorage<T>> Copy for ReadSignal<T, S> {}
78
79impl<T: ?Sized, S: BoxedSignalStorage<T>> PartialEq for ReadSignal<T, S> {
80 fn eq(&self, other: &Self) -> bool {
81 self.value == other.value
82 }
83}
84
85impl<
86 T: Default + 'static,
87 S: CreateBoxedSignalStorage<Signal<T, S>> + BoxedSignalStorage<T> + Storage<SignalData<T>>,
88 > Default for ReadSignal<T, S>
89{
90 fn default() -> Self {
91 Self::new_maybe_sync(Signal::new_maybe_sync(T::default()))
92 }
93}
94
95read_impls!(ReadSignal<T, S: BoxedSignalStorage<T>>);
96
97impl<T, S: BoxedSignalStorage<T>> IntoAttributeValue for ReadSignal<T, S>
98where
99 T: Clone + IntoAttributeValue + 'static,
100{
101 fn into_value(self) -> dioxus_core::AttributeValue {
102 self.with(|f| f.clone().into_value())
103 }
104}
105
106impl<T, S> IntoDynNode for ReadSignal<T, S>
107where
108 T: Clone + IntoDynNode + 'static,
109 S: BoxedSignalStorage<T>,
110{
111 fn into_dyn_node(self) -> dioxus_core::DynamicNode {
112 self.with(|f| f.clone().into_dyn_node())
113 }
114}
115
116impl<T: Clone + 'static, S: BoxedSignalStorage<T>> Deref for ReadSignal<T, S> {
117 type Target = dyn Fn() -> T;
118
119 fn deref(&self) -> &Self::Target {
120 unsafe { ReadableExt::deref_impl(self) }
121 }
122}
123
124impl<T: ?Sized, S: BoxedSignalStorage<T>> Readable for ReadSignal<T, S> {
125 type Target = T;
126 type Storage = S;
127
128 #[track_caller]
129 fn try_read_unchecked(
130 &self,
131 ) -> Result<ReadableRef<'static, Self>, generational_box::BorrowError>
132 where
133 T: 'static,
134 {
135 self.value
136 .try_peek_unchecked()
137 .unwrap()
138 .try_read_unchecked()
139 }
140
141 #[track_caller]
142 fn try_peek_unchecked(&self) -> BorrowResult<ReadableRef<'static, Self>>
143 where
144 T: 'static,
145 {
146 self.value
147 .try_peek_unchecked()
148 .unwrap()
149 .try_peek_unchecked()
150 }
151
152 fn subscribers(&self) -> Subscribers
153 where
154 T: 'static,
155 {
156 self.value.try_peek_unchecked().unwrap().subscribers()
157 }
158}
159
160impl<
164 T: 'static,
165 S: CreateBoxedSignalStorage<Signal<T, S>> + BoxedSignalStorage<T> + Storage<SignalData<T>>,
166 > From<Signal<T, S>> for ReadSignal<T, S>
167{
168 fn from(value: Signal<T, S>) -> Self {
169 Self::new_maybe_sync(value)
170 }
171}
172impl<T: PartialEq + 'static> From<Memo<T>> for ReadSignal<T> {
173 fn from(value: Memo<T>) -> Self {
174 Self::new(value)
175 }
176}
177impl<
178 T: 'static,
179 S: CreateBoxedSignalStorage<CopyValue<T, S>> + BoxedSignalStorage<T> + Storage<T>,
180 > From<CopyValue<T, S>> for ReadSignal<T, S>
181{
182 fn from(value: CopyValue<T, S>) -> Self {
183 Self::new_maybe_sync(value)
184 }
185}
186impl<T, R> From<Global<T, R>> for ReadSignal<R>
187where
188 T: Readable<Target = R, Storage = UnsyncStorage> + InitializeFromFunction<R> + Clone + 'static,
189 R: 'static,
190{
191 fn from(value: Global<T, R>) -> Self {
192 Self::new(value)
193 }
194}
195impl<V, O, F, S> From<MappedSignal<O, V, F>> for ReadSignal<O, S>
196where
197 O: ?Sized + 'static,
198 V: Readable<Storage = S> + 'static,
199 F: Fn(&V::Target) -> &O + 'static,
200 S: BoxedSignalStorage<O> + CreateBoxedSignalStorage<MappedSignal<O, V, F>>,
201{
202 fn from(value: MappedSignal<O, V, F>) -> Self {
203 Self::new_maybe_sync(value)
204 }
205}
206impl<V, O, F, FMut, S> From<MappedMutSignal<O, V, F, FMut>> for ReadSignal<O, S>
207where
208 O: ?Sized + 'static,
209 V: Readable<Storage = S> + 'static,
210 F: Fn(&V::Target) -> &O + 'static,
211 FMut: 'static,
212 S: BoxedSignalStorage<O> + CreateBoxedSignalStorage<MappedMutSignal<O, V, F, FMut>>,
213{
214 fn from(value: MappedMutSignal<O, V, F, FMut>) -> Self {
215 Self::new_maybe_sync(value)
216 }
217}
218impl<T: ?Sized + 'static, S> From<WriteSignal<T, S>> for ReadSignal<T, S>
219where
220 S: BoxedSignalStorage<T> + CreateBoxedSignalStorage<WriteSignal<T, S>>,
221{
222 fn from(value: WriteSignal<T, S>) -> Self {
223 Self::new_maybe_sync(value)
224 }
225}
226
227pub struct WriteSignal<T: ?Sized, S: BoxedSignalStorage<T> = UnsyncStorage> {
229 value: CopyValue<Box<S::DynWritable<sealed::SealedToken>>, S>,
230}
231
232impl<T: ?Sized + 'static> WriteSignal<T> {
233 pub fn new(
235 value: impl Writable<Target = T, Storage = UnsyncStorage, WriteMetadata: 'static> + 'static,
236 ) -> Self {
237 Self::new_maybe_sync(value)
238 }
239}
240
241impl<T: ?Sized + 'static, S: BoxedSignalStorage<T>> WriteSignal<T, S> {
242 pub fn new_maybe_sync<R>(value: R) -> Self
244 where
245 R: Writable<Target = T, WriteMetadata: 'static>,
246 S: CreateBoxedSignalStorage<R>,
247 {
248 Self {
249 value: CopyValue::new_maybe_sync(S::new_writable(value, sealed::SealedToken)),
250 }
251 }
252}
253
254struct BoxWriteMetadata<W> {
255 value: W,
256}
257
258impl<W: Writable> BoxWriteMetadata<W> {
259 fn new(value: W) -> Self {
260 Self { value }
261 }
262}
263
264impl<W: Readable> Readable for BoxWriteMetadata<W> {
265 type Target = W::Target;
266
267 type Storage = W::Storage;
268
269 fn try_read_unchecked(
270 &self,
271 ) -> Result<ReadableRef<'static, Self>, generational_box::BorrowError>
272 where
273 W::Target: 'static,
274 {
275 self.value.try_read_unchecked()
276 }
277
278 fn try_peek_unchecked(
279 &self,
280 ) -> Result<ReadableRef<'static, Self>, generational_box::BorrowError>
281 where
282 W::Target: 'static,
283 {
284 self.value.try_peek_unchecked()
285 }
286
287 fn subscribers(&self) -> Subscribers
288 where
289 W::Target: 'static,
290 {
291 self.value.subscribers()
292 }
293}
294
295impl<W> Writable for BoxWriteMetadata<W>
296where
297 W: Writable,
298 W::WriteMetadata: 'static,
299{
300 type WriteMetadata = Box<dyn Any>;
301
302 fn try_write_unchecked(
303 &self,
304 ) -> Result<crate::WritableRef<'static, Self>, generational_box::BorrowMutError>
305 where
306 W::Target: 'static,
307 {
308 self.value
309 .try_write_unchecked()
310 .map(|w| w.map_metadata(|data| Box::new(data) as Box<dyn Any>))
311 }
312}
313
314impl<T: ?Sized, S: BoxedSignalStorage<T>> Clone for WriteSignal<T, S> {
315 fn clone(&self) -> Self {
316 *self
317 }
318}
319
320impl<T: ?Sized, S: BoxedSignalStorage<T>> Copy for WriteSignal<T, S> {}
321
322impl<T: ?Sized, S: BoxedSignalStorage<T>> PartialEq for WriteSignal<T, S> {
323 fn eq(&self, other: &Self) -> bool {
324 self.value == other.value
325 }
326}
327
328read_impls!(WriteSignal<T, S: BoxedSignalStorage<T>>);
329write_impls!(WriteSignal<T, S: BoxedSignalStorage<T>>);
330
331impl<T, S> IntoAttributeValue for WriteSignal<T, S>
332where
333 T: Clone + IntoAttributeValue + 'static,
334 S: BoxedSignalStorage<T>,
335{
336 fn into_value(self) -> dioxus_core::AttributeValue {
337 self.with(|f| f.clone().into_value())
338 }
339}
340
341impl<T, S> IntoDynNode for WriteSignal<T, S>
342where
343 T: Clone + IntoDynNode + 'static,
344 S: BoxedSignalStorage<T>,
345{
346 fn into_dyn_node(self) -> dioxus_core::DynamicNode {
347 self.with(|f| f.clone().into_dyn_node())
348 }
349}
350
351impl<T: Clone + 'static, S: BoxedSignalStorage<T>> Deref for WriteSignal<T, S> {
352 type Target = dyn Fn() -> T;
353
354 fn deref(&self) -> &Self::Target {
355 unsafe { ReadableExt::deref_impl(self) }
356 }
357}
358
359impl<T: ?Sized, S: BoxedSignalStorage<T>> Readable for WriteSignal<T, S> {
360 type Target = T;
361 type Storage = S;
362
363 #[track_caller]
364 fn try_read_unchecked(
365 &self,
366 ) -> Result<ReadableRef<'static, Self>, generational_box::BorrowError>
367 where
368 T: 'static,
369 {
370 self.value
371 .try_peek_unchecked()
372 .unwrap()
373 .try_read_unchecked()
374 }
375
376 #[track_caller]
377 fn try_peek_unchecked(&self) -> BorrowResult<ReadableRef<'static, Self>>
378 where
379 T: 'static,
380 {
381 self.value
382 .try_peek_unchecked()
383 .unwrap()
384 .try_peek_unchecked()
385 }
386
387 fn subscribers(&self) -> Subscribers
388 where
389 T: 'static,
390 {
391 self.value.try_peek_unchecked().unwrap().subscribers()
392 }
393}
394
395impl<T: ?Sized, S: BoxedSignalStorage<T>> Writable for WriteSignal<T, S> {
396 type WriteMetadata = Box<dyn Any>;
397
398 fn try_write_unchecked(
399 &self,
400 ) -> Result<crate::WritableRef<'static, Self>, generational_box::BorrowMutError>
401 where
402 T: 'static,
403 {
404 self.value
405 .try_peek_unchecked()
406 .unwrap()
407 .try_write_unchecked()
408 }
409}
410
411impl<
415 T: 'static,
416 S: CreateBoxedSignalStorage<Signal<T, S>> + BoxedSignalStorage<T> + Storage<SignalData<T>>,
417 > From<Signal<T, S>> for WriteSignal<T, S>
418{
419 fn from(value: Signal<T, S>) -> Self {
420 Self::new_maybe_sync(value)
421 }
422}
423impl<
424 T: 'static,
425 S: CreateBoxedSignalStorage<CopyValue<T, S>> + BoxedSignalStorage<T> + Storage<T>,
426 > From<CopyValue<T, S>> for WriteSignal<T, S>
427{
428 fn from(value: CopyValue<T, S>) -> Self {
429 Self::new_maybe_sync(value)
430 }
431}
432impl<T, R> From<Global<T, R>> for WriteSignal<R>
433where
434 T: Writable<Target = R, Storage = UnsyncStorage> + InitializeFromFunction<R> + Clone + 'static,
435 R: 'static,
436{
437 fn from(value: Global<T, R>) -> Self {
438 Self::new(value)
439 }
440}
441impl<V, O, F, FMut, S> From<MappedMutSignal<O, V, F, FMut>> for WriteSignal<O, S>
442where
443 O: ?Sized + 'static,
444 V: Writable<Storage = S> + 'static,
445 F: Fn(&V::Target) -> &O + 'static,
446 FMut: Fn(&mut V::Target) -> &mut O + 'static,
447 S: CreateBoxedSignalStorage<MappedMutSignal<O, V, F, FMut>> + BoxedSignalStorage<O>,
448{
449 fn from(value: MappedMutSignal<O, V, F, FMut>) -> Self {
450 Self::new_maybe_sync(value)
451 }
452}
453
454pub trait BoxedSignalStorage<T: ?Sized>:
460 Storage<Box<Self::DynReadable<sealed::SealedToken>>>
461 + Storage<Box<Self::DynWritable<sealed::SealedToken>>>
462 + sealed::Sealed
463 + 'static
464{
465 #[doc(hidden)]
467 type DynReadable<Seal: sealed::SealedTokenTrait>: Readable<Target = T, Storage = Self> + ?Sized;
468 #[doc(hidden)]
470 type DynWritable<Seal: sealed::SealedTokenTrait>: Writable<Target = T, Storage = Self, WriteMetadata = Box<dyn Any>>
471 + ?Sized;
472}
473
474pub trait CreateBoxedSignalStorage<T: Readable + ?Sized>:
483 BoxedSignalStorage<T::Target> + 'static
484{
485 #[doc(hidden)]
487 fn new_readable(
488 value: T,
489 _: sealed::SealedToken,
490 ) -> Box<Self::DynReadable<sealed::SealedToken>>
491 where
492 T: Sized;
493
494 #[doc(hidden)]
496 fn new_writable(
497 value: T,
498 _: sealed::SealedToken,
499 ) -> Box<Self::DynWritable<sealed::SealedToken>>
500 where
501 T: Writable + Sized;
502}
503
504impl<T: ?Sized + 'static> BoxedSignalStorage<T> for UnsyncStorage {
505 type DynReadable<Seal: sealed::SealedTokenTrait> = dyn Readable<Target = T, Storage = Self>;
506 type DynWritable<Seal: sealed::SealedTokenTrait> =
507 dyn Writable<Target = T, Storage = Self, WriteMetadata = Box<dyn Any>>;
508}
509
510impl<T: Readable<Storage = UnsyncStorage> + ?Sized + 'static> CreateBoxedSignalStorage<T>
511 for UnsyncStorage
512{
513 fn new_readable(value: T, _: sealed::SealedToken) -> Box<Self::DynReadable<sealed::SealedToken>>
514 where
515 T: Sized,
516 {
517 Box::new(value)
518 }
519
520 fn new_writable(value: T, _: sealed::SealedToken) -> Box<Self::DynWritable<sealed::SealedToken>>
521 where
522 T: Writable + Sized,
523 {
524 Box::new(BoxWriteMetadata::new(value))
525 }
526}
527
528impl<T: ?Sized + 'static> BoxedSignalStorage<T> for SyncStorage {
529 type DynReadable<Seal: sealed::SealedTokenTrait> =
530 dyn Readable<Target = T, Storage = Self> + Send + Sync;
531 type DynWritable<Seal: sealed::SealedTokenTrait> =
532 dyn Writable<Target = T, Storage = Self, WriteMetadata = Box<dyn Any>> + Send + Sync;
533}
534
535impl<T: Readable<Storage = SyncStorage> + Sync + Send + ?Sized + 'static>
536 CreateBoxedSignalStorage<T> for SyncStorage
537{
538 fn new_readable(value: T, _: sealed::SealedToken) -> Box<Self::DynReadable<sealed::SealedToken>>
539 where
540 T: Sized,
541 {
542 Box::new(value)
543 }
544
545 fn new_writable(value: T, _: sealed::SealedToken) -> Box<Self::DynWritable<sealed::SealedToken>>
546 where
547 T: Writable + Sized,
548 {
549 Box::new(BoxWriteMetadata::new(value))
550 }
551}
552
553mod sealed {
554 use generational_box::{SyncStorage, UnsyncStorage};
555
556 pub trait Sealed {}
557 impl Sealed for UnsyncStorage {}
558 impl Sealed for SyncStorage {}
559
560 pub struct SealedToken;
561
562 pub trait SealedTokenTrait {}
563 impl SealedTokenTrait for SealedToken {}
564}