dioxus_signals/read.rs
1use std::collections::{HashMap, HashSet};
2use std::{
3    mem::MaybeUninit,
4    ops::{Deref, Index},
5};
6
7use crate::{ext_methods, MappedSignal, ReadSignal};
8use dioxus_core::Subscribers;
9use generational_box::{AnyStorage, UnsyncStorage};
10
11/// A reference to a value that can be read from.
12#[allow(type_alias_bounds)]
13pub type ReadableRef<'a, T: Readable, O = <T as Readable>::Target> =
14    <T::Storage as AnyStorage>::Ref<'a, O>;
15
16/// A trait for states that can be read from like [`crate::Signal`], [`crate::GlobalSignal`], or [`crate::ReadSignal`]. You may choose to accept this trait as a parameter instead of the concrete type to allow for more flexibility in your API. For example, instead of creating two functions, one that accepts a [`crate::Signal`] and one that accepts a [`crate::GlobalSignal`], you can create one function that accepts a [`Readable`] type.
17///
18/// # Example
19/// ```rust
20/// # use dioxus::prelude::*;
21/// fn double(something_readable: &impl Readable<Target = i32>) -> i32 {
22///     something_readable.cloned() * 2
23/// }
24///
25/// static COUNT: GlobalSignal<i32> = Signal::global(|| 0);
26///
27/// fn MyComponent(count: Signal<i32>) -> Element {
28///     // Since we defined the function in terms of the readable trait, we can use it with any readable type (Signal, GlobalSignal, ReadSignal, etc)
29///     let doubled = use_memo(move || double(&count));
30///     let global_count_doubled = use_memo(|| double(&COUNT));
31///     rsx! {
32///         div {
33///             "Count local: {count}"
34///             "Doubled local: {doubled}"
35///             "Count global: {COUNT}"
36///             "Doubled global: {global_count_doubled}"
37///         }
38///     }
39/// }
40/// ```
41pub trait Readable {
42    /// The target type of the reference.
43    type Target: ?Sized;
44
45    /// The type of the storage this readable uses.
46    type Storage: AnyStorage;
47
48    /// Try to get a reference to the value without checking the lifetime. This will subscribe the current scope to the signal.
49    ///
50    /// NOTE: This method is completely safe because borrow checking is done at runtime.
51    fn try_read_unchecked(
52        &self,
53    ) -> Result<ReadableRef<'static, Self>, generational_box::BorrowError>
54    where
55        Self::Target: 'static;
56
57    /// Try to peek the current value of the signal without subscribing to updates. If the value has
58    /// been dropped, this will return an error.
59    ///
60    /// NOTE: This method is completely safe because borrow checking is done at runtime.
61    fn try_peek_unchecked(
62        &self,
63    ) -> Result<ReadableRef<'static, Self>, generational_box::BorrowError>
64    where
65        Self::Target: 'static;
66
67    /// Get the underlying subscriber list for this readable. This is used to track when the value changes and notify subscribers.
68    fn subscribers(&self) -> Subscribers
69    where
70        Self::Target: 'static;
71}
72
73/// An extension trait for `Readable` types that provides some convenience methods.
74pub trait ReadableExt: Readable {
75    /// Get the current value of the state. If this is a signal, this will subscribe the current scope to the signal.
76    /// If the value has been dropped, this will panic. Calling this on a Signal is the same as
77    /// using the signal() syntax to read and subscribe to its value
78    #[track_caller]
79    fn read(&self) -> ReadableRef<'_, Self>
80    where
81        Self::Target: 'static,
82    {
83        self.try_read().unwrap()
84    }
85
86    /// Try to get the current value of the state. If this is a signal, this will subscribe the current scope to the signal.
87    #[track_caller]
88    fn try_read(&self) -> Result<ReadableRef<'_, Self>, generational_box::BorrowError>
89    where
90        Self::Target: 'static,
91    {
92        self.try_read_unchecked()
93            .map(Self::Storage::downcast_lifetime_ref)
94    }
95
96    /// Get a reference to the value without checking the lifetime. This will subscribe the current scope to the signal.
97    ///
98    /// NOTE: This method is completely safe because borrow checking is done at runtime.
99    #[track_caller]
100    fn read_unchecked(&self) -> ReadableRef<'static, Self>
101    where
102        Self::Target: 'static,
103    {
104        self.try_read_unchecked().unwrap()
105    }
106
107    /// Get the current value of the state without subscribing to updates. If the value has been dropped, this will panic.
108    ///
109    /// # Example
110    /// ```rust
111    /// # use dioxus::prelude::*;
112    /// fn MyComponent(mut count: Signal<i32>) -> Element {
113    ///     let mut event_source = use_signal(|| None);
114    ///     let doubled = use_memo(move || {
115    ///         // We want to log the value of the event_source, but we don't need to rerun the doubled value if the event_source changes (because the value of doubled doesn't depend on the event_source)
116    ///         // We can read the value with peek without subscribing to updates
117    ///         let source = event_source.peek();
118    ///         tracing::info!("Clicked: {source:?}");
119    ///         count() * 2
120    ///     });
121    ///     rsx! {
122    ///         div { "Count: {count}" }
123    ///         div { "Doubled: {doubled}" }
124    ///         button {
125    ///             onclick: move |_| {
126    ///                 event_source.set(Some("Click me button"));
127    ///                 count += 1;
128    ///             },
129    ///             "Click me"
130    ///         }
131    ///         button {
132    ///             onclick: move |_| {
133    ///                 event_source.set(Some("Double me button"));
134    ///                 count += 1;
135    ///             },
136    ///             "Double me"
137    ///         }
138    ///     }
139    /// }
140    /// ```
141    #[track_caller]
142    fn peek(&self) -> ReadableRef<'_, Self>
143    where
144        Self::Target: 'static,
145    {
146        Self::Storage::downcast_lifetime_ref(self.peek_unchecked())
147    }
148
149    /// Try to peek the current value of the signal without subscribing to updates. If the value has
150    /// been dropped, this will return an error.
151    #[track_caller]
152    fn try_peek(&self) -> Result<ReadableRef<'_, Self>, generational_box::BorrowError>
153    where
154        Self::Target: 'static,
155    {
156        self.try_peek_unchecked()
157            .map(Self::Storage::downcast_lifetime_ref)
158    }
159
160    /// Get the current value of the signal without checking the lifetime. **Unlike read, this will not subscribe the current scope to the signal which can cause parts of your UI to not update.**
161    ///
162    /// If the signal has been dropped, this will panic.
163    #[track_caller]
164    fn peek_unchecked(&self) -> ReadableRef<'static, Self>
165    where
166        Self::Target: 'static,
167    {
168        self.try_peek_unchecked().unwrap()
169    }
170
171    /// Map the references of the readable value to a new type. This lets you provide a view
172    /// into the readable value without creating a new signal or cloning the value.
173    ///
174    /// Anything that subscribes to the readable value will be rerun whenever the original value changes, even if the view does not
175    /// change. If you want to memorize the view, you can use a [`crate::Memo`] instead. For fine grained scoped updates, use
176    /// stores instead
177    ///
178    /// # Example
179    /// ```rust
180    /// # use dioxus::prelude::*;
181    /// fn List(list: Signal<Vec<i32>>) -> Element {
182    ///     rsx! {
183    ///         for index in 0..list.len() {
184    ///             // We can use the `map` method to provide a view into the single item in the list that the child component will render
185    ///             Item { item: list.map(move |v| &v[index]) }
186    ///         }
187    ///     }
188    /// }
189    ///
190    /// // The child component doesn't need to know that the mapped value is coming from a list
191    /// #[component]
192    /// fn Item(item: ReadSignal<i32>) -> Element {
193    ///     rsx! {
194    ///         div { "Item: {item}" }
195    ///     }
196    /// }
197    /// ```
198    fn map<F, O>(self, f: F) -> MappedSignal<O, Self, F>
199    where
200        Self: Clone + Sized,
201        F: Fn(&Self::Target) -> &O,
202    {
203        MappedSignal::new(self, f)
204    }
205
206    /// Clone the inner value and return it. If the value has been dropped, this will panic.
207    #[track_caller]
208    fn cloned(&self) -> Self::Target
209    where
210        Self::Target: Clone + 'static,
211    {
212        self.read().clone()
213    }
214
215    /// Run a function with a reference to the value. If the value has been dropped, this will panic.
216    #[track_caller]
217    fn with<O>(&self, f: impl FnOnce(&Self::Target) -> O) -> O
218    where
219        Self::Target: 'static,
220    {
221        f(&*self.read())
222    }
223
224    /// Run a function with a reference to the value. If the value has been dropped, this will panic.
225    #[track_caller]
226    fn with_peek<O>(&self, f: impl FnOnce(&Self::Target) -> O) -> O
227    where
228        Self::Target: 'static,
229    {
230        f(&*self.peek())
231    }
232
233    /// Index into the inner value and return a reference to the result. If the value has been dropped or the index is invalid, this will panic.
234    #[track_caller]
235    fn index<I>(
236        &self,
237        index: I,
238    ) -> ReadableRef<'_, Self, <Self::Target as std::ops::Index<I>>::Output>
239    where
240        Self::Target: std::ops::Index<I> + 'static,
241    {
242        <Self::Storage as AnyStorage>::map(self.read(), |v| v.index(index))
243    }
244
245    /// SAFETY: You must call this function directly with `self` as the argument.
246    /// This function relies on the size of the object you return from the deref
247    /// being the same as the object you pass in
248    #[doc(hidden)]
249    unsafe fn deref_impl<'a>(&self) -> &'a dyn Fn() -> Self::Target
250    where
251        Self: Sized + 'a,
252        Self::Target: Clone + 'static,
253    {
254        // https://github.com/dtolnay/case-studies/tree/master/callable-types
255
256        // First we create a closure that captures something with the Same in memory layout as Self (MaybeUninit<Self>).
257        let uninit_callable = MaybeUninit::<Self>::uninit();
258        // Then move that value into the closure. We assume that the closure now has a in memory layout of Self.
259        let uninit_closure = move || Self::read(unsafe { &*uninit_callable.as_ptr() }).clone();
260
261        // Check that the size of the closure is the same as the size of Self in case the compiler changed the layout of the closure.
262        let size_of_closure = std::mem::size_of_val(&uninit_closure);
263        assert_eq!(size_of_closure, std::mem::size_of::<Self>());
264
265        // Then cast the lifetime of the closure to the lifetime of &self.
266        fn cast_lifetime<'a, T>(_a: &T, b: &'a T) -> &'a T {
267            b
268        }
269        let reference_to_closure = cast_lifetime(
270            {
271                // The real closure that we will never use.
272                &uninit_closure
273            },
274            #[allow(clippy::missing_transmute_annotations)]
275            // We transmute self into a reference to the closure. This is safe because we know that the closure has the same memory layout as Self so &Closure == &Self.
276            unsafe {
277                std::mem::transmute(self)
278            },
279        );
280
281        // Cast the closure to a trait object.
282        reference_to_closure as &_
283    }
284}
285
286impl<R: Readable + ?Sized> ReadableExt for R {}
287
288/// An extension trait for `Readable` types that can be boxed into a trait object.
289pub trait ReadableBoxExt: Readable<Storage = UnsyncStorage> {
290    /// Box the readable value into a trait object. This is useful for passing around readable values without knowing their concrete type.
291    fn boxed(self) -> ReadSignal<Self::Target>
292    where
293        Self: Sized + 'static,
294    {
295        ReadSignal::new(self)
296    }
297}
298impl<R: Readable<Storage = UnsyncStorage> + ?Sized> ReadableBoxExt for R {}
299
300/// An extension trait for `Readable<Vec<T>>` that provides some convenience methods.
301pub trait ReadableVecExt<T>: Readable<Target = Vec<T>> {
302    /// Returns the length of the inner vector.
303    #[track_caller]
304    fn len(&self) -> usize
305    where
306        T: 'static,
307    {
308        self.with(|v| v.len())
309    }
310
311    /// Returns true if the inner vector is empty.
312    #[track_caller]
313    fn is_empty(&self) -> bool
314    where
315        T: 'static,
316    {
317        self.with(|v| v.is_empty())
318    }
319
320    /// Get the first element of the inner vector.
321    #[track_caller]
322    fn first(&self) -> Option<ReadableRef<'_, Self, T>>
323    where
324        T: 'static,
325    {
326        <Self::Storage as AnyStorage>::try_map(self.read(), |v| v.first())
327    }
328
329    /// Get the last element of the inner vector.
330    #[track_caller]
331    fn last(&self) -> Option<ReadableRef<'_, Self, T>>
332    where
333        T: 'static,
334    {
335        <Self::Storage as AnyStorage>::try_map(self.read(), |v| v.last())
336    }
337
338    /// Get the element at the given index of the inner vector.
339    #[track_caller]
340    fn get(&self, index: usize) -> Option<ReadableRef<'_, Self, T>>
341    where
342        T: 'static,
343    {
344        <Self::Storage as AnyStorage>::try_map(self.read(), |v| v.get(index))
345    }
346
347    /// Get an iterator over the values of the inner vector.
348    #[track_caller]
349    fn iter(&self) -> ReadableValueIterator<'_, Self>
350    where
351        Self: Sized,
352    {
353        ReadableValueIterator {
354            index: 0,
355            value: self,
356        }
357    }
358}
359
360/// An iterator over the values of a `Readable<Vec<T>>`.
361pub struct ReadableValueIterator<'a, R> {
362    index: usize,
363    value: &'a R,
364}
365
366impl<'a, T: 'static, R: Readable<Target = Vec<T>>> Iterator for ReadableValueIterator<'a, R> {
367    type Item = ReadableRef<'a, R, T>;
368
369    fn next(&mut self) -> Option<Self::Item> {
370        let index = self.index;
371        self.index += 1;
372        self.value.get(index)
373    }
374}
375
376impl<T, R> ReadableVecExt<T> for R where R: Readable<Target = Vec<T>> {}
377
378/// An extension trait for `Readable<Option<T>>` that provides some convenience methods.
379pub trait ReadableOptionExt<T>: Readable<Target = Option<T>> {
380    /// Unwraps the inner value and clones it.
381    #[track_caller]
382    fn unwrap(&self) -> T
383    where
384        T: Clone + 'static,
385    {
386        self.as_ref().unwrap().clone()
387    }
388
389    /// Attempts to read the inner value of the Option.
390    #[track_caller]
391    fn as_ref(&self) -> Option<ReadableRef<'_, Self, T>>
392    where
393        T: 'static,
394    {
395        <Self::Storage as AnyStorage>::try_map(self.read(), |v| v.as_ref())
396    }
397}
398
399impl<T, R> ReadableOptionExt<T> for R where R: Readable<Target = Option<T>> {}
400
401/// An extension trait for `Readable<Option<T>>` that provides some convenience methods.
402pub trait ReadableResultExt<T, E>: Readable<Target = Result<T, E>> {
403    /// Unwraps the inner value and clones it.
404    #[track_caller]
405    fn unwrap(&self) -> T
406    where
407        T: Clone + 'static,
408        E: 'static,
409    {
410        self.as_ref()
411            .unwrap_or_else(|_| panic!("Tried to unwrap a Result that was an error"))
412            .clone()
413    }
414
415    /// Attempts to read the inner value of the Option.
416    #[track_caller]
417    fn as_ref(&self) -> Result<ReadableRef<'_, Self, T>, ReadableRef<'_, Self, E>>
418    where
419        T: 'static,
420        E: 'static,
421    {
422        <Self::Storage as AnyStorage>::try_map(self.read(), |v| v.as_ref().ok()).ok_or(
423            <Self::Storage as AnyStorage>::map(self.read(), |v| v.as_ref().err().unwrap()),
424        )
425    }
426}
427
428impl<T, E, R> ReadableResultExt<T, E> for R where R: Readable<Target = Result<T, E>> {}
429
430/// An extension trait for [`Readable<String>`] that provides some convenience methods.
431pub trait ReadableStringExt: Readable<Target = String> {
432    ext_methods! {
433        /// Check the capacity of the string.
434        fn capacity(&self) -> usize = String::capacity;
435    }
436}
437
438impl<W> ReadableStringExt for W where W: Readable<Target = String> {}
439
440/// An extension trait for [`Readable<String>`] and [`Readable<str>`] that provides some convenience methods.
441pub trait ReadableStrExt: Readable<Target: Deref<Target = str> + 'static> {
442    ext_methods! {
443        /// Check if the string is empty.
444        fn is_empty(&self) -> bool = |s: &Self::Target| s.deref().is_empty();
445
446        /// Get the length of the string.
447        fn len(&self) -> usize = |s: &Self::Target| s.deref().len();
448
449        /// Check if the string contains the given pattern.
450        fn contains(&self, pat: &str) -> bool = |s: &Self::Target, pat| s.deref().contains(pat);
451    }
452}
453
454impl<W> ReadableStrExt for W where W: Readable<Target: Deref<Target = str> + 'static> {}
455
456/// An extension trait for [`Readable<HashMap<K, V, H>>`] that provides some convenience methods.
457pub trait ReadableHashMapExt<K: 'static, V: 'static, H: 'static>:
458    Readable<Target = HashMap<K, V, H>>
459{
460    ext_methods! {
461        /// Check if the hashmap is empty.
462        fn is_empty(&self) -> bool = HashMap::is_empty;
463
464        /// Get the length of the hashmap.
465        fn len(&self) -> usize = HashMap::len;
466
467        /// Get the capacity of the hashmap.
468        fn capacity(&self) -> usize = HashMap::capacity;
469    }
470
471    /// Get the value for the given key.
472    #[track_caller]
473    fn get(&self, key: &K) -> Option<ReadableRef<'_, Self, V>>
474    where
475        K: std::hash::Hash + Eq,
476        H: std::hash::BuildHasher,
477    {
478        <Self::Storage as AnyStorage>::try_map(self.read(), |v| v.get(key))
479    }
480
481    /// Check if the hashmap contains the given key.
482    #[track_caller]
483    fn contains_key(&self, key: &K) -> bool
484    where
485        K: std::hash::Hash + Eq,
486        H: std::hash::BuildHasher,
487    {
488        self.with(|v| v.contains_key(key))
489    }
490}
491
492impl<K: 'static, V: 'static, H: 'static, R> ReadableHashMapExt<K, V, H> for R where
493    R: Readable<Target = HashMap<K, V, H>>
494{
495}
496
497/// An extension trait for [`Readable<HashSet<V, H>>`] that provides some convenience methods.
498pub trait ReadableHashSetExt<V: 'static, H: 'static>: Readable<Target = HashSet<V, H>> {
499    ext_methods! {
500        /// Check if the hashset is empty.
501        fn is_empty(&self) -> bool = HashSet::is_empty;
502
503        /// Get the length of the hashset.
504        fn len(&self) -> usize = HashSet::len;
505
506        /// Get the capacity of the hashset.
507        fn capacity(&self) -> usize = HashSet::capacity;
508    }
509
510    /// Check if the hashset contains the given value.
511    #[track_caller]
512    fn contains(&self, value: &V) -> bool
513    where
514        V: std::hash::Hash + Eq,
515        H: std::hash::BuildHasher,
516    {
517        self.with(|v| v.contains(value))
518    }
519}
520
521impl<V: 'static, H: 'static, R> ReadableHashSetExt<V, H> for R where
522    R: Readable<Target = HashSet<V, H>>
523{
524}