Skip to main content

reactive_stores/
lib.rs

1#![forbid(unsafe_code)]
2#![deny(missing_docs)]
3
4//! Stores are a primitive for creating deeply-nested reactive state, based on [`reactive_graph`].
5//!
6//! Reactive signals allow you to define atomic units of reactive state. However, signals are
7//! imperfect as a mechanism for tracking reactive change in structs or collections, because
8//! they do not allow you to track access to individual struct fields or individual items in a
9//! collection, rather than the struct as a whole or the collection as a whole. Reactivity for
10//! individual fields can be achieved by creating a struct of signals, but this has issues; it
11//! means that a struct is no longer a plain data structure, but requires wrappers on each field.
12//!
13//! Stores attempt to solve this problem by allowing arbitrarily-deep access to the fields of some
14//! data structure, while still maintaining fine-grained reactivity.
15//!
16//! The [`Store`](macro@Store) macro adds getters and setters for the fields of a struct. Call those getters or
17//! setters on a reactive [`Store`](struct@Store) or [`ArcStore`], or to a subfield, gives you
18//! access to a reactive subfield. This value of this field can be accessed via the ordinary signal
19//! traits (`Get`, `Set`, and so on).
20//!
21//! The [`Patch`](macro@Patch) macro allows you to annotate a struct such that stores and fields have a
22//! [`.patch()`](Patch::patch) method, which allows you to provide an entirely new value, but only
23//! notify fields that have changed.
24//!
25//! Updating a field will notify its parents and children, but not its siblings.
26//!
27//! Stores can therefore
28//! 1) work with plain Rust data types, and
29//! 2) provide reactive access to individual fields
30//!
31//! ### Example
32//!
33//! ```rust
34//! use reactive_graph::{
35//!     effect::Effect,
36//!     traits::{Read, Write},
37//! };
38//! use reactive_stores::{Patch, Store};
39//!
40//! #[derive(Debug, Store, Patch, Default)]
41//! struct Todos {
42//!     user: String,
43//!     todos: Vec<Todo>,
44//! }
45//!
46//! #[derive(Debug, Store, Patch, Default)]
47//! struct Todo {
48//!     label: String,
49//!     completed: bool,
50//! }
51//!
52//! let store = Store::new(Todos {
53//!     user: "Alice".to_string(),
54//!     todos: Vec::new(),
55//! });
56//!
57//! # if false { // don't run effect in doctests
58//! Effect::new(move |_| {
59//!     // you can access individual store fields with a getter
60//!     println!("user: {:?}", &*store.user().read());
61//! });
62//! # }
63//!
64//! // won't notify the effect that listens to `user`
65//! store.todos().write().push(Todo {
66//!     label: "Test".to_string(),
67//!     completed: false,
68//! });
69//! ```
70//! ### Generated traits
71//! The [`Store`](macro@Store) macro generates traits for each `struct` to which it is applied.  When working
72//! within a single file or module, this is not an issue.  However, when working with multiple modules
73//! or files, one needs to `use` the generated traits.  The general pattern is that for each `struct`
74//! named `Foo`, the macro generates a trait named `FooStoreFields`.  For example:
75//! ```rust
76//! pub mod foo {
77//!   use reactive_stores::Store;
78
79//!   #[derive(Store)]
80//!   pub struct Foo {
81//!     field: i32,
82//!   }
83//! }
84//!
85//! pub mod user {
86//!   use leptos::prelude::*;
87//!   use reactive_stores::Field;
88//!   // Using FooStore fields here.
89//!   use crate::foo::{ Foo, FooStoreFields };
90//!
91//!   #[component]
92//!   pub fn UseFoo(foo: Field<Foo>) {
93//!     // Without FooStoreFields, foo.field() would fail to compile.
94//!     println!("field: {}", foo.field().read());
95//!   }
96//! }
97//!
98//! # fn main() {
99//! # }
100//! ```
101//! ### Additional field types
102//!
103//! Most of the time, your structs will have fields as in the example above: the struct is comprised
104//! of primitive types, builtin types like [String], or other structs that implement [Store](struct@Store) or [Field].
105//! However, there are some special cases that require some additional understanding.
106//!
107//! #### Option
108//! [`Option<T>`](std::option::Option) behaves pretty much as you would expect, utilizing [.is_some()](std::option::Option::is_some)
109//! and [.is_none()](std::option::Option::is_none) to check the value and  [.unwrap()](OptionStoreExt::unwrap) method to access the inner value.  The [OptionStoreExt]
110//! trait is required to use the [.unwrap()](OptionStoreExt::unwrap) method.  Here is a quick example:
111//! ```rust
112//! // Including the trait OptionStoreExt here is required to use unwrap()
113//! use reactive_stores::{OptionStoreExt, Store};
114//! use reactive_graph::traits::{Get, Read};
115//!
116//! #[derive(Store)]
117//! struct StructWithOption {
118//!     opt_field: Option<i32>,
119//! }
120//!
121//! fn describe(store: &Store<StructWithOption>) -> String {
122//!     if store.opt_field().read().is_some() {
123//!         // Note here we need to use OptionStoreExt or unwrap() would not compile
124//!         format!("store has a value {}", store.opt_field().unwrap().get())
125//!     } else {
126//!         format!("store has no value")
127//!     }
128//! }
129//! let none_store = Store::new(StructWithOption { opt_field: None });
130//! let some_store = Store::new(StructWithOption { opt_field: Some(42)});
131//!
132//! assert_eq!(describe(&none_store), "store has no value");
133//! assert_eq!(describe(&some_store), "store has a value 42");
134//! ```
135//! #### Vec
136//! [`Vec<T>`](std::vec::Vec) requires some special treatment when trying to access
137//! elements of the vector directly.  Use the [StoreFieldIterator::at_unkeyed()] method to
138//! access a particular value in a [struct@Store] or [Field] for a [std::vec::Vec].  For example:
139//! ```rust
140//! # use reactive_stores::Store;
141//! // Needed to use at_unkeyed() on Vec
142//! use reactive_stores::StoreFieldIter;
143//! use reactive_stores::StoreFieldIterator;
144//! use reactive_graph::traits::Read;
145//! use reactive_graph::traits::Get;
146//!
147//! #[derive(Store)]
148//! struct StructWithVec {
149//!     vec_field: Vec<i32>,
150//! }
151//!
152//! let store = Store::new(StructWithVec { vec_field: vec![1, 2, 3] });
153//!
154//! assert_eq!(store.vec_field().at_unkeyed(0).get(), 1);
155//! assert_eq!(store.vec_field().at_unkeyed(1).get(), 2);
156//! assert_eq!(store.vec_field().at_unkeyed(2).get(), 3);
157//! ```
158//! #### Enum
159//! Enumerated types behave a bit differently as the [`Store`](macro@Store) macro builds underlying traits instead of alternate
160//! enumerated structures.  Each element in an `Enum` generates methods to access it in the store: a
161//! method with the name of the field gives a boolean if the `Enum` is that variant, and possible accessor
162//! methods for anonymous fields of that variant.  For example:
163//! ```rust
164//! use reactive_stores::Store;
165//! use reactive_graph::traits::{Read, Get};
166//!
167//! #[derive(Store)]
168//! enum Choices {
169//!    First,
170//!    Second(String),
171//! }
172//!
173//! let choice_one = Store::new(Choices::First);
174//! let choice_two = Store::new(Choices::Second("hello".to_string()));
175//!
176//! assert!(choice_one.first());
177//! assert!(!choice_one.second());
178//! // Note the use of the accessor method here .second_0()
179//! assert_eq!(choice_two.second_0().unwrap().get(), "hello");
180//! ```
181//! #### Box
182//! [`Box<T>`](std::boxed::Box) also requires some special treatment in how you dereference elements of the Box, especially
183//! when trying to build a recursive data structure.  [DerefField](trait@DerefField) provides a [.deref_value()](DerefField::deref_field) method to access
184//! the inner value.  For example:
185//! ```rust
186//! // Note here we need to use DerefField to use deref_field() and OptionStoreExt to use unwrap()
187//! use reactive_stores::{Store, DerefField, OptionStoreExt};
188//! use reactive_graph::traits::{ Read, Get };
189//!
190//! #[derive(Store)]
191//! struct List {
192//!     value: i32,
193//!     #[store]
194//!     child: Option<Box<List>>,
195//! }
196//!
197//! let tree = Store::new(List {
198//!     value: 1,
199//!     child: Some(Box::new(List { value: 2, child: None })),
200//! });
201//!
202//! assert_eq!(tree.child().unwrap().deref_field().value().get(), 2);
203//! ```
204//! ### Implementation Notes
205//!
206//! Every struct field can be understood as an index. For example, given the following definition
207//! ```rust
208//! # use reactive_stores::{Store, Patch};
209//! #[derive(Debug, Store, Patch, Default)]
210//! struct Name {
211//!     first: String,
212//!     last: String,
213//! }
214//! ```
215//! We can think of `first` as `0` and `last` as `1`. This means that any deeply-nested field of a
216//! struct can be described as a path of indices. So, for example:
217//! ```rust
218//! # use reactive_stores::{Store, Patch};
219//! #[derive(Debug, Store, Patch, Default)]
220//! struct User {
221//!     user: Name,
222//! }
223//!
224//! #[derive(Debug, Store, Patch, Default)]
225//! struct Name {
226//!     first: String,
227//!     last: String,
228//! }
229//! ```
230//! Here, given a `User`, `first` can be understood as [`0`, `0`] and `last` is [`0`, `1`].
231//!
232//! This means we can implement a store as the combination of two things:
233//! 1) An `Arc<RwLock<T>>` that holds the actual value
234//! 2) A map from field paths to reactive "triggers," which are signals that have no value but
235//!    track reactivity
236//!
237//! Accessing a field via its getters returns an iterator-like data structure that describes how to
238//! get to that subfield. Calling `.read()` returns a guard that dereferences to the value of that
239//! field in the signal inner `Arc<RwLock<_>>`, and tracks the trigger that corresponds with its
240//! path; calling `.write()` returns a writeable guard, and notifies that same trigger.
241
242use or_poisoned::OrPoisoned;
243use reactive_graph::{
244    owner::{ArenaItem, LocalStorage, Storage, SyncStorage},
245    signal::{
246        guards::{Plain, ReadGuard, WriteGuard},
247        ArcTrigger,
248    },
249    traits::{
250        DefinedAt, Dispose, IsDisposed, Notify, ReadUntracked, Track,
251        UntrackableGuard, Write,
252    },
253};
254pub use reactive_stores_macro::{Patch, Store};
255use rustc_hash::FxHashMap;
256use std::{
257    any::Any,
258    fmt::Debug,
259    hash::Hash,
260    ops::DerefMut,
261    panic::Location,
262    sync::{Arc, RwLock},
263};
264
265mod arc_field;
266mod deref;
267mod field;
268mod iter;
269mod keyed;
270mod len;
271mod option;
272mod patch;
273mod path;
274#[cfg(feature = "serde")]
275mod serde;
276#[cfg(feature = "slotmap")]
277mod slotmap;
278mod store_field;
279mod subfield;
280
281pub use arc_field::ArcField;
282pub use deref::*;
283pub use field::Field;
284pub use iter::*;
285pub use keyed::*;
286pub use len::Len;
287pub use option::*;
288pub use patch::*;
289pub use path::{StorePath, StorePathSegment};
290pub use store_field::StoreField;
291pub use subfield::Subfield;
292
293#[derive(Debug, Default)]
294struct TriggerMap(FxHashMap<StorePath, StoreFieldTrigger>);
295
296/// The reactive trigger that can be used to track updates to a store field.
297#[derive(Debug, Clone, Default)]
298pub struct StoreFieldTrigger {
299    pub(crate) this: ArcTrigger,
300    pub(crate) children: ArcTrigger,
301}
302
303impl StoreFieldTrigger {
304    /// Creates a new trigger.
305    pub fn new() -> Self {
306        Self::default()
307    }
308}
309
310impl TriggerMap {
311    fn get_or_insert(&mut self, key: StorePath) -> StoreFieldTrigger {
312        if let Some(trigger) = self.0.get(&key) {
313            trigger.clone()
314        } else {
315            let new = StoreFieldTrigger::new();
316            self.0.insert(key, new.clone());
317            new
318        }
319    }
320
321    #[allow(unused)]
322    fn remove(&mut self, key: &StorePath) -> Option<StoreFieldTrigger> {
323        self.0.remove(key)
324    }
325}
326
327/// Manages the keys for a keyed field, including the ability to remove and reuse keys.
328pub struct FieldKeys<K> {
329    spare_keys: Vec<StorePathSegment>,
330    current_key: usize,
331    keys: FxHashMap<K, (StorePathSegment, usize)>,
332}
333
334impl<K> FieldKeys<K>
335where
336    K: Debug + Hash + PartialEq + Eq,
337{
338    /// Creates a new set of keys.
339    pub fn new(from_keys: Vec<K>) -> Self {
340        let mut keys = FxHashMap::with_capacity_and_hasher(
341            from_keys.len(),
342            Default::default(),
343        );
344        for (idx, key) in from_keys.into_iter().enumerate() {
345            let segment = idx.into();
346            keys.insert(key, (segment, idx));
347        }
348
349        Self {
350            spare_keys: Vec::new(),
351            current_key: keys.len().saturating_sub(1),
352            keys,
353        }
354    }
355}
356
357impl<K> FieldKeys<K>
358where
359    K: Hash + PartialEq + Eq,
360{
361    /// Returns a copy of the path segment to the value identified by the key
362    ///
363    /// # Usage
364    ///
365    /// You shouldn't call this method from your code, since it's a part of
366    /// implementation details of `reactive_stores`. This method was exposed
367    /// to implement the derive `Patch` macro for keyed fields.
368    #[doc(hidden)]
369    pub fn get(&self, key: &K) -> Option<(StorePathSegment, usize)> {
370        self.keys.get(key).copied()
371    }
372
373    fn next_key(&mut self) -> StorePathSegment {
374        self.spare_keys.pop().unwrap_or_else(|| {
375            self.current_key += 1;
376            self.current_key.into()
377        })
378    }
379
380    fn update(
381        &mut self,
382        iter: impl IntoIterator<Item = K>,
383    ) -> Vec<(usize, StorePathSegment)> {
384        let new_keys = iter
385            .into_iter()
386            .enumerate()
387            .map(|(idx, key)| (key, idx))
388            .collect::<FxHashMap<K, usize>>();
389
390        let mut index_keys = Vec::with_capacity(new_keys.len());
391
392        // remove old keys and recycle the slots
393        self.keys.retain(|key, old_entry| match new_keys.get(key) {
394            Some(idx) => {
395                old_entry.1 = *idx;
396                true
397            }
398            None => {
399                self.spare_keys.push(old_entry.0);
400                false
401            }
402        });
403
404        // add new keys
405        for (key, idx) in new_keys {
406            match self.keys.get(&key) {
407                Some((segment, idx)) => index_keys.push((*idx, *segment)),
408                None => {
409                    let path = self.next_key();
410                    self.keys.insert(key, (path, idx));
411                    index_keys.push((idx, path));
412                }
413            }
414        }
415
416        index_keys
417    }
418}
419
420impl<K> Default for FieldKeys<K> {
421    fn default() -> Self {
422        Self {
423            spare_keys: Default::default(),
424            current_key: Default::default(),
425            keys: Default::default(),
426        }
427    }
428}
429
430type Map<K, V> = Arc<std::sync::RwLock<std::collections::HashMap<K, V>>>;
431
432/// A map of the keys for a keyed subfield.
433#[derive(Clone, Default)]
434pub struct KeyMap(
435    /// Path to subfield -> Keys in keyed subfield
436    Map<StorePath, Box<dyn Any + Send + Sync>>,
437    /// Map index -> key
438    Map<(StorePath, usize), StorePathSegment>,
439);
440
441impl KeyMap {
442    /// Transforms the keys related to the field identified by `path`.
443    ///
444    /// # Arguments
445    ///
446    /// - **path** - path to the field with collection
447    /// - **fun** - Transforms an instance of [FieldKeys] into the result
448    ///   
449    ///   ## Return value
450    ///
451    ///   callback should return a tuple ( result, new_keys)
452    ///
453    ///   - **result** - this value will be passed as a result
454    ///   - **new_keys** - is a vector of new keys to be added into reverse mapping
455    ///     (path, idx) -> (path segment) map
456    ///     
457    ///     ### Entries
458    ///
459    ///     Entry in the vector is a tuple (idx, segment) where
460    ///
461    ///     - **idx** - index of the element in the collection
462    ///     - **segment** - key of the element in the collection
463    ///     
464    /// - **initialize** - it is the set of keys with which to initialize the
465    ///   KeyMap for this field, if there aren't keys listed yet. In all cases
466    ///   inside the library this is `|| self.latest_keys()` or `|| self.inner.latest_keys()`
467    ///
468    ///   This function will be called **only** if KeyMap doesn't have entry for
469    ///   `path`.
470    ///
471    ///   ## Returns
472    ///
473    ///   A vector of keys which will be used if KeyMap doesn't have an entry for
474    ///   given `path`
475    ///
476    /// # Returns
477    ///
478    /// - [None] if path doesn't point to the keyed field
479    /// - **result** value returned from `fun` callback
480    ///
481    /// # Usage
482    ///
483    /// You should not call this method directly from your code, as it's
484    /// an implementation detail of `reactive_stores`. This method was exposed
485    /// to implement the derive `Patch` macro for keyed fields.
486    #[doc(hidden)]
487    pub fn with_field_keys<K, T>(
488        &self,
489        path: StorePath,
490        fun: impl FnOnce(&mut FieldKeys<K>) -> (T, Vec<(usize, StorePathSegment)>),
491        initialize: impl FnOnce() -> Vec<K>,
492    ) -> Option<T>
493    where
494        K: Debug + Hash + PartialEq + Eq + Send + Sync + 'static,
495    {
496        let mut guard = self.0.write().or_poisoned();
497        let entry = guard
498            .entry(path.clone())
499            .or_insert_with(|| Box::new(FieldKeys::new(initialize())));
500
501        let entry = entry.downcast_mut::<FieldKeys<K>>()?;
502        let (result, new_keys) = fun(entry);
503        if !new_keys.is_empty() {
504            for (idx, segment) in new_keys {
505                self.1
506                    .write()
507                    .or_poisoned()
508                    .insert((path.clone(), idx), segment);
509            }
510        }
511        Some(result)
512    }
513
514    fn contains_key(&self, key: &StorePath) -> bool {
515        self.0.read().or_poisoned().contains_key(key)
516    }
517
518    fn get_key_for_index(
519        &self,
520        key: &(StorePath, usize),
521    ) -> Option<StorePathSegment> {
522        self.1.read().or_poisoned().get(key).copied()
523    }
524}
525
526/// A reference-counted container for a reactive store.
527///
528/// The type `T` should be a struct that has been annotated with `#[derive(Store)]`.
529///
530/// This adds a getter method for each field to `Store<T>`, which allow accessing reactive versions
531/// of each individual field of the struct.
532pub struct ArcStore<T> {
533    #[cfg(any(debug_assertions, leptos_debuginfo))]
534    defined_at: &'static Location<'static>,
535    pub(crate) value: Arc<RwLock<T>>,
536    signals: Arc<RwLock<TriggerMap>>,
537    keys: KeyMap,
538}
539
540impl<T> ArcStore<T> {
541    /// Creates a new store from the initial value.
542    pub fn new(value: T) -> Self {
543        Self {
544            #[cfg(any(debug_assertions, leptos_debuginfo))]
545            defined_at: Location::caller(),
546            value: Arc::new(RwLock::new(value)),
547            signals: Default::default(),
548            keys: Default::default(),
549        }
550    }
551}
552
553impl<T: Default> Default for ArcStore<T> {
554    fn default() -> Self {
555        Self::new(T::default())
556    }
557}
558
559impl<T: Debug> Debug for ArcStore<T> {
560    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
561        let mut f = f.debug_struct("ArcStore");
562        #[cfg(any(debug_assertions, leptos_debuginfo))]
563        let f = f.field("defined_at", &self.defined_at);
564        f.field("value", &self.value)
565            .field("signals", &self.signals)
566            .finish()
567    }
568}
569
570impl<T> Clone for ArcStore<T> {
571    fn clone(&self) -> Self {
572        Self {
573            #[cfg(any(debug_assertions, leptos_debuginfo))]
574            defined_at: self.defined_at,
575            value: Arc::clone(&self.value),
576            signals: Arc::clone(&self.signals),
577            keys: self.keys.clone(),
578        }
579    }
580}
581
582impl<T> DefinedAt for ArcStore<T> {
583    fn defined_at(&self) -> Option<&'static Location<'static>> {
584        #[cfg(any(debug_assertions, leptos_debuginfo))]
585        {
586            Some(self.defined_at)
587        }
588        #[cfg(not(any(debug_assertions, leptos_debuginfo)))]
589        {
590            None
591        }
592    }
593}
594
595impl<T> IsDisposed for ArcStore<T> {
596    #[inline(always)]
597    fn is_disposed(&self) -> bool {
598        false
599    }
600}
601
602impl<T> ReadUntracked for ArcStore<T>
603where
604    T: 'static,
605{
606    type Value = ReadGuard<T, Plain<T>>;
607
608    fn try_read_untracked(&self) -> Option<Self::Value> {
609        Plain::try_new(Arc::clone(&self.value)).map(ReadGuard::new)
610    }
611}
612
613impl<T> Write for ArcStore<T>
614where
615    T: 'static,
616{
617    type Value = T;
618
619    fn try_write(&self) -> Option<impl UntrackableGuard<Target = Self::Value>> {
620        self.writer()
621            .map(|writer| WriteGuard::new(self.clone(), writer))
622    }
623
624    fn try_write_untracked(
625        &self,
626    ) -> Option<impl DerefMut<Target = Self::Value>> {
627        let mut writer = self.writer()?;
628        writer.untrack();
629        Some(writer)
630    }
631}
632
633impl<T: 'static> Track for ArcStore<T> {
634    fn track(&self) {
635        self.track_field();
636    }
637}
638
639impl<T: 'static> Notify for ArcStore<T> {
640    fn notify(&self) {
641        let trigger = self.get_trigger(self.path().into_iter().collect());
642        trigger.this.notify();
643        trigger.children.notify();
644    }
645}
646
647/// An arena-allocated container for a reactive store.
648///
649/// The type `T` should be a struct that has been annotated with `#[derive(Store)]`.
650///
651/// This adds a getter method for each field to `Store<T>`, which allow accessing reactive versions
652/// of each individual field of the struct.
653///
654/// This follows the same ownership rules as arena-allocated types like
655/// [`RwSignal`](reactive_graph::signal::RwSignal).
656pub struct Store<T, S = SyncStorage> {
657    #[cfg(any(debug_assertions, leptos_debuginfo))]
658    defined_at: &'static Location<'static>,
659    inner: ArenaItem<ArcStore<T>, S>,
660}
661
662impl<T> Store<T>
663where
664    T: Send + Sync + 'static,
665{
666    /// Creates a new store with the initial value.
667    pub fn new(value: T) -> Self {
668        Self {
669            #[cfg(any(debug_assertions, leptos_debuginfo))]
670            defined_at: Location::caller(),
671            inner: ArenaItem::new_with_storage(ArcStore::new(value)),
672        }
673    }
674}
675
676impl<T, S> PartialEq for Store<T, S> {
677    fn eq(&self, other: &Self) -> bool {
678        self.inner == other.inner
679    }
680}
681
682impl<T, S> Eq for Store<T, S> {}
683
684impl<T> Store<T, LocalStorage>
685where
686    T: 'static,
687{
688    /// Creates a new store for a type that is `!Send`.
689    ///
690    /// This pins the value to the current thread. Accessing it from any other thread will panic.
691    pub fn new_local(value: T) -> Self {
692        Self {
693            #[cfg(any(debug_assertions, leptos_debuginfo))]
694            defined_at: Location::caller(),
695            inner: ArenaItem::new_with_storage(ArcStore::new(value)),
696        }
697    }
698}
699
700impl<T> Default for Store<T>
701where
702    T: Default + Send + Sync + 'static,
703{
704    fn default() -> Self {
705        Self::new(T::default())
706    }
707}
708
709impl<T> Default for Store<T, LocalStorage>
710where
711    T: Default + 'static,
712{
713    fn default() -> Self {
714        Self::new_local(T::default())
715    }
716}
717
718impl<T: Debug, S> Debug for Store<T, S>
719where
720    S: Debug,
721{
722    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
723        let mut f = f.debug_struct("Store");
724        #[cfg(any(debug_assertions, leptos_debuginfo))]
725        let f = f.field("defined_at", &self.defined_at);
726        f.field("inner", &self.inner).finish()
727    }
728}
729
730impl<T, S> Clone for Store<T, S> {
731    fn clone(&self) -> Self {
732        *self
733    }
734}
735
736impl<T, S> Copy for Store<T, S> {}
737
738impl<T, S> DefinedAt for Store<T, S> {
739    fn defined_at(&self) -> Option<&'static Location<'static>> {
740        #[cfg(any(debug_assertions, leptos_debuginfo))]
741        {
742            Some(self.defined_at)
743        }
744        #[cfg(not(any(debug_assertions, leptos_debuginfo)))]
745        {
746            None
747        }
748    }
749}
750
751impl<T, S> IsDisposed for Store<T, S>
752where
753    T: 'static,
754{
755    #[inline(always)]
756    fn is_disposed(&self) -> bool {
757        self.inner.is_disposed()
758    }
759}
760
761impl<T, S> Dispose for Store<T, S>
762where
763    T: 'static,
764{
765    fn dispose(self) {
766        self.inner.dispose();
767    }
768}
769
770impl<T, S> ReadUntracked for Store<T, S>
771where
772    T: 'static,
773    S: Storage<ArcStore<T>>,
774{
775    type Value = ReadGuard<T, Plain<T>>;
776
777    fn try_read_untracked(&self) -> Option<Self::Value> {
778        self.inner
779            .try_get_value()
780            .and_then(|inner| inner.try_read_untracked())
781    }
782}
783
784impl<T, S> Write for Store<T, S>
785where
786    T: 'static,
787    S: Storage<ArcStore<T>>,
788{
789    type Value = T;
790
791    fn try_write(&self) -> Option<impl UntrackableGuard<Target = Self::Value>> {
792        self.writer().map(|writer| WriteGuard::new(*self, writer))
793    }
794
795    fn try_write_untracked(
796        &self,
797    ) -> Option<impl DerefMut<Target = Self::Value>> {
798        let mut writer = self.writer()?;
799        writer.untrack();
800        Some(writer)
801    }
802}
803
804impl<T, S> Track for Store<T, S>
805where
806    T: 'static,
807    S: Storage<ArcStore<T>>,
808{
809    fn track(&self) {
810        if let Some(inner) = self.inner.try_get_value() {
811            inner.track();
812        }
813    }
814}
815
816impl<T, S> Notify for Store<T, S>
817where
818    T: 'static,
819    S: Storage<ArcStore<T>>,
820{
821    fn notify(&self) {
822        if let Some(inner) = self.inner.try_get_value() {
823            inner.notify();
824        }
825    }
826}
827
828impl<T, S> From<ArcStore<T>> for Store<T, S>
829where
830    T: 'static,
831    S: Storage<ArcStore<T>>,
832{
833    fn from(value: ArcStore<T>) -> Self {
834        Self {
835            #[cfg(any(debug_assertions, leptos_debuginfo))]
836            defined_at: value.defined_at,
837            inner: ArenaItem::new_with_storage(value),
838        }
839    }
840}
841
842#[cfg(test)]
843mod tests {
844    use crate::{self as reactive_stores, Patch, Store, StoreFieldIterator};
845    use reactive_graph::{
846        effect::Effect,
847        owner::StoredValue,
848        traits::{Read, ReadUntracked, Set, Track, Update, Write},
849    };
850    use std::sync::{
851        atomic::{AtomicUsize, Ordering},
852        Arc,
853    };
854
855    pub async fn tick() {
856        tokio::time::sleep(std::time::Duration::from_micros(1)).await;
857    }
858
859    #[derive(Debug, Store, Patch, Default)]
860    struct Todos {
861        user: String,
862        todos: Vec<Todo>,
863    }
864
865    #[derive(Debug, Store, Patch, Default)]
866    struct Todo {
867        label: String,
868        completed: bool,
869    }
870
871    impl Todo {
872        pub fn new(label: impl ToString) -> Self {
873            Self {
874                label: label.to_string(),
875                completed: false,
876            }
877        }
878    }
879
880    fn data() -> Todos {
881        Todos {
882            user: "Bob".to_string(),
883            todos: vec![
884                Todo {
885                    label: "Create reactive store".to_string(),
886                    completed: true,
887                },
888                Todo {
889                    label: "???".to_string(),
890                    completed: false,
891                },
892                Todo {
893                    label: "Profit".to_string(),
894                    completed: false,
895                },
896            ],
897        }
898    }
899
900    #[derive(Debug, Clone, Store, Patch, Default)]
901    struct Foo {
902        id: i32,
903        bar: Bar,
904    }
905
906    #[derive(Debug, Clone, Store, Patch, Default)]
907    struct Bar {
908        bar_signature: i32,
909        baz: Baz,
910    }
911
912    #[derive(Debug, Clone, Store, Patch, Default)]
913    struct Baz {
914        more_data: i32,
915        baw: Baw,
916    }
917
918    #[derive(Debug, Clone, Store, Patch, Default)]
919    struct Baw {
920        more_data: i32,
921        end: i32,
922    }
923
924    #[tokio::test]
925    async fn mutating_field_triggers_effect() {
926        _ = any_spawner::Executor::init_tokio();
927
928        let combined_count = Arc::new(AtomicUsize::new(0));
929
930        let store = Store::new(data());
931        assert_eq!(store.read_untracked().todos.len(), 3);
932        assert_eq!(store.user().read_untracked().as_str(), "Bob");
933        Effect::new_sync({
934            let combined_count = Arc::clone(&combined_count);
935            move |prev: Option<()>| {
936                if prev.is_none() {
937                    println!("first run");
938                } else {
939                    println!("next run");
940                }
941                println!("{:?}", *store.user().read());
942                combined_count.fetch_add(1, Ordering::Relaxed);
943            }
944        });
945        tick().await;
946        tick().await;
947        store.user().set("Greg".into());
948        tick().await;
949        store.user().set("Carol".into());
950        tick().await;
951        store.user().update(|name| name.push_str("!!!"));
952        tick().await;
953        // the effect reads from `user`, so it should trigger every time
954        assert_eq!(combined_count.load(Ordering::Relaxed), 4);
955    }
956
957    #[tokio::test]
958    async fn other_field_does_not_notify() {
959        _ = any_spawner::Executor::init_tokio();
960
961        let combined_count = Arc::new(AtomicUsize::new(0));
962
963        let store = Store::new(data());
964
965        Effect::new_sync({
966            let combined_count = Arc::clone(&combined_count);
967            move |prev: Option<()>| {
968                if prev.is_none() {
969                    println!("first run");
970                } else {
971                    println!("next run");
972                }
973                println!("{:?}", *store.todos().read());
974                combined_count.fetch_add(1, Ordering::Relaxed);
975            }
976        });
977        tick().await;
978        store.user().set("Greg".into());
979        tick().await;
980        store.user().set("Carol".into());
981        tick().await;
982        store.user().update(|name| name.push_str("!!!"));
983        tick().await;
984        // the effect reads from `todos`, so it shouldn't trigger every time
985        assert_eq!(combined_count.load(Ordering::Relaxed), 1);
986    }
987
988    #[tokio::test]
989    async fn parent_does_notify() {
990        _ = any_spawner::Executor::init_tokio();
991
992        let combined_count = Arc::new(AtomicUsize::new(0));
993
994        let store = Store::new(data());
995
996        Effect::new_sync({
997            let combined_count = Arc::clone(&combined_count);
998            move |prev: Option<()>| {
999                if prev.is_none() {
1000                    println!("first run");
1001                } else {
1002                    println!("next run");
1003                }
1004                println!("{:?}", *store.todos().read());
1005                combined_count.fetch_add(1, Ordering::Relaxed);
1006            }
1007        });
1008        tick().await;
1009        tick().await;
1010        store.set(Todos::default());
1011        tick().await;
1012        store.set(data());
1013        tick().await;
1014        assert_eq!(combined_count.load(Ordering::Relaxed), 3);
1015    }
1016
1017    #[tokio::test]
1018    async fn changes_do_notify_parent() {
1019        _ = any_spawner::Executor::init_tokio();
1020
1021        let combined_count = Arc::new(AtomicUsize::new(0));
1022
1023        let store = Store::new(data());
1024
1025        Effect::new_sync({
1026            let combined_count = Arc::clone(&combined_count);
1027            move |prev: Option<()>| {
1028                if prev.is_none() {
1029                    println!("first run");
1030                } else {
1031                    println!("next run");
1032                }
1033                println!("{:?}", *store.read());
1034                combined_count.fetch_add(1, Ordering::Relaxed);
1035            }
1036        });
1037        tick().await;
1038        tick().await;
1039        store.user().set("Greg".into());
1040        tick().await;
1041        store.user().set("Carol".into());
1042        tick().await;
1043        store.user().update(|name| name.push_str("!!!"));
1044        tick().await;
1045        store.todos().write().clear();
1046        tick().await;
1047        assert_eq!(combined_count.load(Ordering::Relaxed), 5);
1048    }
1049
1050    #[tokio::test]
1051    async fn iterator_tracks_the_field() {
1052        _ = any_spawner::Executor::init_tokio();
1053
1054        let combined_count = Arc::new(AtomicUsize::new(0));
1055
1056        let store = Store::new(data());
1057
1058        Effect::new_sync({
1059            let combined_count = Arc::clone(&combined_count);
1060            move |prev: Option<()>| {
1061                if prev.is_none() {
1062                    println!("first run");
1063                } else {
1064                    println!("next run");
1065                }
1066                println!(
1067                    "{:?}",
1068                    store.todos().iter_unkeyed().collect::<Vec<_>>()
1069                );
1070                combined_count.store(1, Ordering::Relaxed);
1071            }
1072        });
1073
1074        tick().await;
1075        store
1076            .todos()
1077            .write()
1078            .push(Todo::new("Create reactive store?"));
1079        tick().await;
1080        store.todos().write().push(Todo::new("???"));
1081        tick().await;
1082        store.todos().write().push(Todo::new("Profit!"));
1083        // the effect only reads from `todos`, so it should trigger only the first time
1084        assert_eq!(combined_count.load(Ordering::Relaxed), 1);
1085    }
1086
1087    #[tokio::test]
1088    async fn patching_only_notifies_changed_field() {
1089        _ = any_spawner::Executor::init_tokio();
1090
1091        let combined_count = Arc::new(AtomicUsize::new(0));
1092
1093        let store = Store::new(Todos {
1094            user: "Alice".into(),
1095            todos: vec![],
1096        });
1097
1098        Effect::new_sync({
1099            let combined_count = Arc::clone(&combined_count);
1100            move |prev: Option<()>| {
1101                if prev.is_none() {
1102                    println!("first run");
1103                } else {
1104                    println!("next run");
1105                }
1106                println!("{:?}", *store.todos().read());
1107                combined_count.fetch_add(1, Ordering::Relaxed);
1108            }
1109        });
1110        tick().await;
1111        tick().await;
1112        store.patch(Todos {
1113            user: "Bob".into(),
1114            todos: vec![],
1115        });
1116        tick().await;
1117        store.patch(Todos {
1118            user: "Carol".into(),
1119            todos: vec![],
1120        });
1121        tick().await;
1122        assert_eq!(combined_count.load(Ordering::Relaxed), 1);
1123
1124        store.patch(Todos {
1125            user: "Carol".into(),
1126            todos: vec![Todo {
1127                label: "First Todo".into(),
1128                completed: false,
1129            }],
1130        });
1131        tick().await;
1132        assert_eq!(combined_count.load(Ordering::Relaxed), 2);
1133    }
1134
1135    #[tokio::test]
1136    async fn patching_only_notifies_changed_field_with_custom_patch() {
1137        _ = any_spawner::Executor::init_tokio();
1138
1139        #[derive(Debug, Store, Patch, Default)]
1140        struct CustomTodos {
1141            #[patch(|this, new| *this = new)]
1142            user: String,
1143            todos: Vec<CustomTodo>,
1144        }
1145
1146        #[derive(Debug, Store, Patch, Default)]
1147        struct CustomTodo {
1148            label: String,
1149            completed: bool,
1150        }
1151
1152        let combined_count = Arc::new(AtomicUsize::new(0));
1153
1154        let store = Store::new(CustomTodos {
1155            user: "Alice".into(),
1156            todos: vec![],
1157        });
1158
1159        Effect::new_sync({
1160            let combined_count = Arc::clone(&combined_count);
1161            move |prev: Option<()>| {
1162                if prev.is_none() {
1163                    println!("first run");
1164                } else {
1165                    println!("next run");
1166                }
1167                println!("{:?}", *store.user().read());
1168                combined_count.fetch_add(1, Ordering::Relaxed);
1169            }
1170        });
1171        tick().await;
1172        tick().await;
1173        store.patch(CustomTodos {
1174            user: "Bob".into(),
1175            todos: vec![],
1176        });
1177        tick().await;
1178        assert_eq!(combined_count.load(Ordering::Relaxed), 2);
1179        store.patch(CustomTodos {
1180            user: "Carol".into(),
1181            todos: vec![],
1182        });
1183        tick().await;
1184        assert_eq!(combined_count.load(Ordering::Relaxed), 3);
1185
1186        store.patch(CustomTodos {
1187            user: "Carol".into(),
1188            todos: vec![CustomTodo {
1189                label: "First CustomTodo".into(),
1190                completed: false,
1191            }],
1192        });
1193        tick().await;
1194        assert_eq!(combined_count.load(Ordering::Relaxed), 3);
1195    }
1196
1197    // regression test for https://github.com/leptos-rs/leptos/issues/3523
1198    #[tokio::test]
1199    async fn notifying_all_descendants() {
1200        use reactive_graph::traits::*;
1201
1202        _ = any_spawner::Executor::init_tokio();
1203
1204        let store = Store::new(Foo {
1205            id: 42,
1206            bar: Bar {
1207                bar_signature: 69,
1208                baz: Baz {
1209                    more_data: 9999,
1210                    baw: Baw {
1211                        more_data: 22,
1212                        end: 1112,
1213                    },
1214                },
1215            },
1216        });
1217
1218        let store_runs = StoredValue::new(0);
1219        let id_runs = StoredValue::new(0);
1220        let bar_runs = StoredValue::new(0);
1221        let bar_signature_runs = StoredValue::new(0);
1222        let bar_baz_runs = StoredValue::new(0);
1223        let more_data_runs = StoredValue::new(0);
1224        let baz_baw_end_runs = StoredValue::new(0);
1225
1226        Effect::new_sync(move |_| {
1227            println!("foo: {:?}", store.get());
1228            *store_runs.write_value() += 1;
1229        });
1230
1231        Effect::new_sync(move |_| {
1232            println!("foo.id: {:?}", store.id().get());
1233            *id_runs.write_value() += 1;
1234        });
1235
1236        Effect::new_sync(move |_| {
1237            println!("foo.bar: {:?}", store.bar().get());
1238            *bar_runs.write_value() += 1;
1239        });
1240
1241        Effect::new_sync(move |_| {
1242            println!(
1243                "foo.bar.bar_signature: {:?}",
1244                store.bar().bar_signature().get()
1245            );
1246            *bar_signature_runs.write_value() += 1;
1247        });
1248
1249        Effect::new_sync(move |_| {
1250            println!("foo.bar.baz: {:?}", store.bar().baz().get());
1251            *bar_baz_runs.write_value() += 1;
1252        });
1253
1254        Effect::new_sync(move |_| {
1255            println!(
1256                "foo.bar.baz.more_data: {:?}",
1257                store.bar().baz().more_data().get()
1258            );
1259            *more_data_runs.write_value() += 1;
1260        });
1261
1262        Effect::new_sync(move |_| {
1263            println!(
1264                "foo.bar.baz.baw.end: {:?}",
1265                store.bar().baz().baw().end().get()
1266            );
1267            *baz_baw_end_runs.write_value() += 1;
1268        });
1269
1270        println!("[INITIAL EFFECT RUN]");
1271        tick().await;
1272        println!("\n\n[SETTING STORE]");
1273        store.set(Default::default());
1274        tick().await;
1275        println!("\n\n[SETTING STORE.BAR.BAZ]");
1276        store.bar().baz().set(Default::default());
1277        tick().await;
1278
1279        assert_eq!(store_runs.get_value(), 3);
1280        assert_eq!(id_runs.get_value(), 2);
1281        assert_eq!(bar_runs.get_value(), 3);
1282        assert_eq!(bar_signature_runs.get_value(), 2);
1283        assert_eq!(bar_baz_runs.get_value(), 3);
1284        assert_eq!(more_data_runs.get_value(), 3);
1285        assert_eq!(baz_baw_end_runs.get_value(), 3);
1286    }
1287
1288    #[tokio::test]
1289    async fn changing_parent_notifies_subfield() {
1290        _ = any_spawner::Executor::init_tokio();
1291
1292        let combined_count = Arc::new(AtomicUsize::new(0));
1293
1294        let store = Store::new(Foo {
1295            id: 42,
1296            bar: Bar {
1297                bar_signature: 69,
1298                baz: Baz {
1299                    more_data: 9999,
1300                    baw: Baw {
1301                        more_data: 22,
1302                        end: 1112,
1303                    },
1304                },
1305            },
1306        });
1307
1308        let tracked_field = store.bar().baz().more_data();
1309
1310        Effect::new_sync({
1311            let combined_count = Arc::clone(&combined_count);
1312            move |prev: Option<()>| {
1313                if prev.is_none() {
1314                    println!("first run");
1315                } else {
1316                    println!("next run");
1317                }
1318
1319                // we only track `more`, but this should still be notified
1320                // when its parent fields `bar` or `baz` change
1321                println!("{:?}", *tracked_field.read());
1322                combined_count.fetch_add(1, Ordering::Relaxed);
1323            }
1324        });
1325        tick().await;
1326        tick().await;
1327
1328        store.bar().baz().set(Baz {
1329            more_data: 42,
1330            baw: Baw {
1331                more_data: 11,
1332                end: 31,
1333            },
1334        });
1335        tick().await;
1336        store.bar().set(Bar {
1337            bar_signature: 23,
1338            baz: Baz {
1339                more_data: 32,
1340                baw: Baw {
1341                    more_data: 432,
1342                    end: 423,
1343                },
1344            },
1345        });
1346        tick().await;
1347
1348        assert_eq!(combined_count.load(Ordering::Relaxed), 3);
1349    }
1350
1351    #[tokio::test]
1352    async fn changing_parent_notifies_unkeyed_child() {
1353        _ = any_spawner::Executor::init_tokio();
1354
1355        let combined_count = Arc::new(AtomicUsize::new(0));
1356
1357        let store = Store::new(data());
1358
1359        let tracked_field = store.todos().at_unkeyed(0);
1360
1361        Effect::new_sync({
1362            let combined_count = Arc::clone(&combined_count);
1363            move |prev: Option<()>| {
1364                if prev.is_none() {
1365                    println!("first run");
1366                } else {
1367                    println!("next run");
1368                }
1369
1370                // we only track `more`, but this should still be notified
1371                // when its parent fields `bar` or `baz` change
1372                println!("{:?}", *tracked_field.read());
1373                combined_count.fetch_add(1, Ordering::Relaxed);
1374            }
1375        });
1376        tick().await;
1377        tick().await;
1378
1379        store.todos().write().pop();
1380        tick().await;
1381
1382        store.todos().write().push(Todo {
1383            label: "another one".into(),
1384            completed: false,
1385        });
1386        tick().await;
1387
1388        assert_eq!(combined_count.load(Ordering::Relaxed), 3);
1389    }
1390
1391    #[tokio::test]
1392    async fn untracked_write_on_subfield_shouldnt_notify() {
1393        _ = any_spawner::Executor::init_tokio();
1394
1395        let name_count = Arc::new(AtomicUsize::new(0));
1396
1397        let store = Store::new(data());
1398
1399        let tracked_field = store.user();
1400
1401        Effect::new_sync({
1402            let name_count = Arc::clone(&name_count);
1403            move |_| {
1404                tracked_field.track();
1405                name_count.fetch_add(1, Ordering::Relaxed);
1406            }
1407        });
1408
1409        tick().await;
1410        assert_eq!(name_count.load(Ordering::Relaxed), 1);
1411
1412        tracked_field.write().push('!');
1413        tick().await;
1414        assert_eq!(name_count.load(Ordering::Relaxed), 2);
1415
1416        tracked_field.write_untracked().push('!');
1417        tick().await;
1418        assert_eq!(name_count.load(Ordering::Relaxed), 2);
1419    }
1420}