Skip to main content

reactive_stores/
store_field.rs

1use crate::{
2    ArcStore, KeyMap, Store, StoreFieldTrigger,
3    path::{StorePath, StorePathSegment},
4};
5use or_poisoned::OrPoisoned;
6use reactive_graph::{
7    owner::Storage,
8    signal::{
9        ArcTrigger,
10        guards::{Plain, UntrackedWriteGuard, WriteGuard},
11    },
12    traits::{Track, UntrackableGuard},
13};
14use std::{iter, ops::Deref, sync::Arc};
15
16/// Describes a type that can be accessed as a reactive store field.
17pub trait StoreField: Sized {
18    /// The value this field contains.
19    type Value;
20    /// A read guard to access this field.
21    type Reader: Deref<Target = Self::Value>;
22    /// A write guard to update this field.
23    type Writer: UntrackableGuard<Target = Self::Value>;
24
25    /// Returns the trigger that tracks access and updates for this field.
26    #[track_caller]
27    fn get_trigger(&self, path: StorePath) -> StoreFieldTrigger;
28
29    /// Returns the trigger that tracks access and updates for this field.
30    ///
31    /// This uses *unkeyed* paths: i.e., if any field in the path is keyed, it will
32    /// try to look up the key for the item at the index given in the path, rather than
33    /// the keyed item.
34    #[track_caller]
35    fn get_trigger_unkeyed(&self, path: StorePath) -> StoreFieldTrigger;
36
37    /// The path of this field (see [`StorePath`]).
38    #[track_caller]
39    fn path(&self) -> impl IntoIterator<Item = StorePathSegment>;
40
41    /// The path of this field (see [`StorePath`]). Uses unkeyed indices for any keyed fields.
42    #[track_caller]
43    fn path_unkeyed(&self) -> impl IntoIterator<Item = StorePathSegment> {
44        // TODO remove default impl next time we do a breaking release
45        self.path()
46    }
47
48    /// Reactively tracks this field.
49    #[track_caller]
50    fn track_field(&self) {
51        let path = self.path().into_iter().collect();
52        let trigger = self.get_trigger(path);
53        trigger.this.track();
54        trigger.children.track();
55    }
56
57    /// Returns a read guard to access this field.
58    #[track_caller]
59    fn reader(&self) -> Option<Self::Reader>;
60
61    /// Returns a write guard to update this field.
62    #[track_caller]
63    fn writer(&self) -> Option<Self::Writer>;
64
65    /// The keys for this field, if it is a keyed field.
66    #[track_caller]
67    fn keys(&self) -> Option<KeyMap>;
68
69    /// Returns triggers for this field, and all parent fields.
70    fn triggers_for_current_path(&self) -> Vec<ArcTrigger> {
71        self.triggers_for_path(self.path().into_iter().collect())
72    }
73
74    /// Returns triggers for the field at the given path, and all parent fields
75    fn triggers_for_path(&self, path: StorePath) -> Vec<ArcTrigger> {
76        let trigger = self.get_trigger(path.clone());
77        let mut full_path = path;
78
79        // build a list of triggers, starting with the full path to this node and ending with the root
80        // this will mean that the root is the final item, and this path is first
81        let mut triggers = Vec::with_capacity(full_path.len() + 2);
82        triggers.push(trigger.this.clone());
83        triggers.push(trigger.children.clone());
84        while !full_path.is_empty() {
85            full_path.pop();
86            let inner = self.get_trigger(full_path.clone());
87            triggers.push(inner.children.clone());
88        }
89
90        // when the WriteGuard is dropped, each trigger will be notified, in order
91        // reversing the list will cause the triggers to be notified starting from the root,
92        // then to each child down to this one
93        //
94        // notifying from the root down is important for things like OptionStoreExt::map()/unwrap(),
95        // where it's really important that any effects that subscribe to .is_some() run before effects
96        // that subscribe to the inner value, so that the inner effect can be canceled if the outer switches to `None`
97        // (see https://github.com/leptos-rs/leptos/issues/3704)
98        triggers.reverse();
99
100        triggers
101    }
102
103    /// Returns triggers for the field at the given path, and all parent fields
104    fn triggers_for_path_unkeyed(&self, path: StorePath) -> Vec<ArcTrigger> {
105        // see notes on triggers_for_path() for additional comments on implementation
106
107        let trigger = self.get_trigger_unkeyed(path.clone());
108        let mut full_path = path;
109
110        let mut triggers = Vec::with_capacity(full_path.len() + 2);
111        triggers.push(trigger.this.clone());
112        triggers.push(trigger.children.clone());
113        while !full_path.is_empty() {
114            full_path.pop();
115            let inner = self.get_trigger_unkeyed(full_path.clone());
116            triggers.push(inner.children.clone());
117        }
118        triggers.reverse();
119
120        triggers
121    }
122}
123
124impl<T> StoreField for ArcStore<T>
125where
126    T: 'static,
127{
128    type Value = T;
129    type Reader = Plain<T>;
130    type Writer = WriteGuard<ArcTrigger, UntrackedWriteGuard<T>>;
131
132    #[track_caller]
133    fn get_trigger(&self, path: StorePath) -> StoreFieldTrigger {
134        let triggers = &self.signals;
135
136        triggers.write().or_poisoned().get_or_insert(path)
137    }
138
139    #[track_caller]
140    fn get_trigger_unkeyed(&self, path: StorePath) -> StoreFieldTrigger {
141        let caller = std::panic::Location::caller();
142        let orig_path = path.clone();
143
144        let mut path = StorePath::with_capacity(orig_path.len());
145        for segment in &orig_path {
146            let parent_is_keyed = self.keys.contains_key(&path);
147
148            if parent_is_keyed {
149                let key = self
150                    .keys
151                    .get_key_for_index(&(path.clone(), segment.0))
152                    .unwrap_or_else(|| {
153                        panic!(
154                            "could not find key for index {:?} at {}",
155                            (path.clone(), segment.0),
156                            caller
157                        )
158                    });
159                path.push(key);
160            } else {
161                path.push(*segment);
162            }
163        }
164        self.get_trigger(path)
165    }
166
167    #[track_caller]
168    fn path(&self) -> impl IntoIterator<Item = StorePathSegment> {
169        iter::empty()
170    }
171
172    #[track_caller]
173    fn path_unkeyed(&self) -> impl IntoIterator<Item = StorePathSegment> {
174        iter::empty()
175    }
176
177    #[track_caller]
178    fn reader(&self) -> Option<Self::Reader> {
179        Plain::try_new(Arc::clone(&self.value))
180    }
181
182    #[track_caller]
183    fn writer(&self) -> Option<Self::Writer> {
184        let trigger = self.get_trigger(Default::default());
185        let guard = UntrackedWriteGuard::try_new(Arc::clone(&self.value))?;
186        Some(WriteGuard::new(trigger.children, guard))
187    }
188
189    #[track_caller]
190    fn keys(&self) -> Option<KeyMap> {
191        Some(self.keys.clone())
192    }
193}
194
195impl<T, S> StoreField for Store<T, S>
196where
197    T: 'static,
198    S: Storage<ArcStore<T>>,
199{
200    type Value = T;
201    type Reader = Plain<T>;
202    type Writer = WriteGuard<ArcTrigger, UntrackedWriteGuard<T>>;
203
204    #[track_caller]
205    fn get_trigger(&self, path: StorePath) -> StoreFieldTrigger {
206        self.inner
207            .try_get_value()
208            .map(|n| n.get_trigger(path))
209            .unwrap_or_default()
210    }
211
212    #[track_caller]
213    fn get_trigger_unkeyed(&self, path: StorePath) -> StoreFieldTrigger {
214        self.inner
215            .try_get_value()
216            .map(|n| n.get_trigger_unkeyed(path))
217            .unwrap_or_default()
218    }
219
220    #[track_caller]
221    fn path(&self) -> impl IntoIterator<Item = StorePathSegment> {
222        self.inner
223            .try_get_value()
224            .map(|n| n.path().into_iter().collect::<Vec<_>>())
225            .unwrap_or_default()
226    }
227
228    #[track_caller]
229    fn path_unkeyed(&self) -> impl IntoIterator<Item = StorePathSegment> {
230        self.inner
231            .try_get_value()
232            .map(|n| n.path_unkeyed().into_iter().collect::<Vec<_>>())
233            .unwrap_or_default()
234    }
235
236    #[track_caller]
237    fn reader(&self) -> Option<Self::Reader> {
238        self.inner.try_get_value().and_then(|n| n.reader())
239    }
240
241    #[track_caller]
242    fn writer(&self) -> Option<Self::Writer> {
243        self.inner.try_get_value().and_then(|n| n.writer())
244    }
245
246    #[track_caller]
247    fn keys(&self) -> Option<KeyMap> {
248        self.inner.try_get_value().and_then(|inner| inner.keys())
249    }
250}