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