persisted/lazy.rs
1use crate::{PersistedKey, PersistedStore};
2use core::{
3 fmt::{self, Debug, Display},
4 marker::PhantomData,
5 ops::{Deref, DerefMut},
6};
7
8/// Similar to [Persisted](crate::eager::Persisted), but the value that's sent
9/// to the store is not the same as the value stored in memory. Instead, the
10/// value is computed at save time by [PersistedContainer::get_to_persist].
11/// Similarly, the persisted value that's loaded at initialization isn't stored
12/// directly in the container. Instead, [PersistedContainer::restore_persisted]
13/// determines how to initialize state based on it.
14///
15/// This is useful if the value you want to store is some derivation of the
16/// value you keep in memory. For example, storing which item in a list is
17/// selected: if you store the index of the selected item in memory but want to
18/// persist the *ID* of the selected item so it's resilient to re-ordering, you
19/// can use this.
20///
21/// ## Generic Params
22///
23/// - `S`: The backend type used to persist data. While we don't need access to
24/// an instance of the backend, we do need to know its type so we can access
25/// its static functions on setup/save.
26/// - `K`: The type of the persistence key
27/// - `C`: The type of the wrapping container (see [PersistedContainer]). The
28/// type of the container's persisted value must match the expected value for
29/// the key. In other words, `K::Value` must equal `C::Value`.
30///
31/// ## Accessing
32///
33/// The inner value can be accessed immutably via [Deref]. To get mutable
34/// access, use [PersistedLazy::get_mut]. This wrapper method returns a guard
35/// that implements [DerefMut] (similar to `RefMut` or `MutexGuard` from `std`,
36/// without the internal mutability). When your mutable access is complete, this
37/// wrapper will be dropped and the value will be persisted to the store **only
38/// if it changed** (according to its [PartialEq] impl).
39///
40/// ## Cloning
41///
42/// This type intentionally does *not* implement [Clone]. Cloning would result
43/// in two containers with the same key. Whenever a modification is made to one
44/// it will overwrite the persistence slot. It's unlikely this is the desired
45/// behavior, and therefore is not provided.
46///
47/// ## Example
48///
49/// ```
50/// use persisted::{
51/// PersistedContainer, PersistedKey, PersistedLazy, PersistedStore,
52/// };
53/// use core::cell::Cell;
54///
55/// /// Persist just the stored ID
56/// #[derive(Default)]
57/// struct Store(Cell<Option<PersonId>>);
58///
59/// impl Store {
60/// thread_local! {
61/// static INSTANCE: Store = Default::default();
62/// }
63/// }
64///
65/// impl PersistedStore<SelectedIdKey> for Store {
66/// fn load_persisted(_key: &SelectedIdKey) -> Option<PersonId> {
67/// Self::INSTANCE.with(|store| store.0.get())
68/// }
69///
70/// fn store_persisted(_key: &SelectedIdKey, value: &PersonId) {
71/// Self::INSTANCE.with(|store| store.0.set(Some(*value)))
72/// }
73/// }
74///
75/// #[derive(Copy, Clone, Debug, PartialEq)]
76/// struct PersonId(u64);
77///
78/// #[derive(Clone, Debug)]
79/// #[allow(unused)]
80/// struct Person {
81/// id: PersonId,
82/// name: String,
83/// age: u32,
84/// }
85///
86/// #[derive(Debug, PersistedKey)]
87/// #[persisted(PersonId)]
88/// struct SelectedIdKey;
89///
90/// /// A list of items, with one item selected
91/// struct SelectList {
92/// values: Vec<Person>,
93/// selected_index: usize,
94/// }
95///
96/// impl SelectList {
97/// fn selected(&self) -> &Person {
98/// &self.values[self.selected_index]
99/// }
100/// }
101///
102/// impl PersistedContainer for SelectList {
103/// type Value = PersonId;
104///
105/// fn get_to_persist(&self) -> Self::Value {
106/// self.selected().id
107/// }
108///
109/// fn restore_persisted(&mut self, value: Self::Value) {
110/// // Find selected person by ID
111/// self.selected_index = self
112/// .values
113/// .iter()
114/// .enumerate()
115/// .find(|(_, person)| person.id == value)
116/// .map(|(i, _)| i)
117/// .unwrap_or_default();
118/// }
119/// }
120///
121/// let person_list = vec![
122/// Person {
123/// id: PersonId(23089),
124/// name: "Fred".into(),
125/// age: 17,
126/// },
127/// Person {
128/// id: PersonId(28833),
129/// name: "Susan".into(),
130/// age: 29,
131/// },
132/// Person {
133/// id: PersonId(93383),
134/// name: "Ulysses".into(),
135/// age: 40,
136/// },
137/// ];
138///
139/// let mut people = PersistedLazy::<Store, _, _>::new(
140/// SelectedIdKey,
141/// SelectList {
142/// values: person_list.clone(),
143/// selected_index: 0,
144/// },
145/// );
146/// people.get_mut().selected_index = 1;
147/// assert_eq!(people.selected().id.0, 28833);
148///
149/// let people = PersistedLazy::<Store, _, _>::new(
150/// SelectedIdKey,
151/// SelectList {
152/// values: person_list,
153/// selected_index: 0,
154/// },
155/// );
156/// // The previous value was restored
157/// assert_eq!(people.selected_index, 1);
158/// assert_eq!(people.selected().id.0, 28833);
159/// ```
160pub struct PersistedLazy<S, K, C>
161where
162 K: PersistedKey,
163{
164 backend: PhantomData<S>,
165 key: K,
166 /// Cache the most recently persisted value so we can check if it's changed
167 /// after each mutable access. When it does change, we'll persist.
168 last_persisted: Option<K::Value>,
169 container: C,
170}
171
172impl<S, K, C> PersistedLazy<S, K, C>
173where
174 S: PersistedStore<K>,
175 K: PersistedKey,
176 C: PersistedContainer<Value = K::Value>,
177{
178 /// Initialize a given container whose value will lazily be loaded and
179 /// persisted. If a persisted value is available in the store, it will be
180 /// loaded and used to initialize the container via
181 /// [PersistedContainer::restore_persisted].
182 pub fn new(key: K, mut container: C) -> Self {
183 // Fetch persisted value from the backend
184 if let Some(value) = S::load_persisted(&key) {
185 container.restore_persisted(value);
186 }
187
188 Self {
189 backend: PhantomData,
190 key,
191 container,
192 last_persisted: None,
193 }
194 }
195
196 /// Initialize a new default container whose value will lazily be loaded and
197 /// persisted. If a persisted value is available in the store, it will be
198 /// loaded and used to initialize the container via
199 /// [PersistedContainer::restore_persisted].
200 pub fn new_default(key: K) -> Self
201 where
202 C: Default,
203 {
204 Self::new(key, C::default())
205 }
206
207 /// Get a reference to this container's key
208 pub fn key(&self) -> &K {
209 &self.key
210 }
211
212 /// Get a mutable reference to the value. This is wrapped by a guard, so
213 /// that after mutation when the guard is dropped, the value can be
214 /// persisted. [PersistedStore::store_persisted] will only be called if the
215 /// persisted value actually changed, hence the `K::Value: PartialEq` bound.
216 /// This means [PersistedContainer::get_to_persist] will be called after
217 /// event mutable access, but the value will only be written to the store
218 /// when it's been modified.
219 pub fn get_mut(&mut self) -> PersistedLazyRefMut<S, K, C>
220 where
221 K::Value: PartialEq,
222 {
223 PersistedLazyRefMut { lazy: self }
224 }
225}
226
227// Needed to omit Default bound on S
228impl<S, K, C> Default for PersistedLazy<S, K, C>
229where
230 S: PersistedStore<K>,
231 K: PersistedKey + Default,
232 C: PersistedContainer<Value = K::Value> + Default,
233{
234 fn default() -> Self {
235 Self::new(Default::default(), Default::default())
236 }
237}
238
239// Needed to omit Debug bound on S
240impl<S, K, C> Debug for PersistedLazy<S, K, C>
241where
242 K: PersistedKey + Debug,
243 K::Value: Debug,
244 C: Debug,
245{
246 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
247 f.debug_struct("PersistedLazy")
248 .field("backend", &self.backend)
249 .field("key", &self.key)
250 .field("last_persisted", &self.last_persisted)
251 .field("container", &self.container)
252 .finish()
253 }
254}
255
256impl<S, K, C> Display for PersistedLazy<S, K, C>
257where
258 S: PersistedStore<K>,
259 K: PersistedKey,
260 C: PersistedContainer<Value = K::Value> + Display,
261{
262 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
263 self.container.fmt(f)
264 }
265}
266
267impl<S, K, C> Deref for PersistedLazy<S, K, C>
268where
269 S: PersistedStore<K>,
270 K: PersistedKey,
271 C: PersistedContainer<Value = K::Value>,
272{
273 type Target = C;
274
275 fn deref(&self) -> &Self::Target {
276 &self.container
277 }
278}
279
280/// A guard encompassing the lifespan of a mutable reference to a lazy
281/// container. The purpose of this is to save the value immediately after it is
282/// mutated. **The save will only occur if the value actually changed.** A copy
283/// of the previous value is saved before the mutable access, and compared after
284/// the access.
285pub struct PersistedLazyRefMut<'a, S, K, C>
286where
287 S: PersistedStore<K>,
288 K: PersistedKey,
289 K::Value: PartialEq,
290 C: PersistedContainer<Value = K::Value>,
291{
292 lazy: &'a mut PersistedLazy<S, K, C>,
293}
294
295// Needed to omit Debug bound on S
296impl<'a, S, K, C> Debug for PersistedLazyRefMut<'a, S, K, C>
297where
298 S: PersistedStore<K>,
299 K: PersistedKey + Debug,
300 K::Value: PartialEq + Debug,
301 C: PersistedContainer<Value = K::Value> + Debug,
302{
303 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
304 f.debug_struct("PersistedLazyRefMut")
305 .field("lazy", &self.lazy)
306 .finish()
307 }
308}
309
310impl<'a, S, K, C> Deref for PersistedLazyRefMut<'a, S, K, C>
311where
312 S: PersistedStore<K>,
313 K: PersistedKey,
314 K::Value: PartialEq,
315 C: PersistedContainer<Value = K::Value>,
316{
317 type Target = C;
318
319 fn deref(&self) -> &Self::Target {
320 &self.lazy.container
321 }
322}
323
324impl<'a, S, K, C> DerefMut for PersistedLazyRefMut<'a, S, K, C>
325where
326 S: PersistedStore<K>,
327 K: PersistedKey,
328 K::Value: PartialEq,
329 C: PersistedContainer<Value = K::Value>,
330{
331 fn deref_mut(&mut self) -> &mut Self::Target {
332 &mut self.lazy.container
333 }
334}
335
336/// Save value after modification **only if it changed**
337impl<'a, S, K, C> Drop for PersistedLazyRefMut<'a, S, K, C>
338where
339 S: PersistedStore<K>,
340 K: PersistedKey,
341 K::Value: PartialEq,
342 C: PersistedContainer<Value = K::Value>,
343{
344 fn drop(&mut self) {
345 let persisted_value = self.lazy.container.get_to_persist();
346 if !self
347 .lazy
348 .last_persisted
349 .as_ref()
350 .is_some_and(|last_persisted| last_persisted == &persisted_value)
351 {
352 S::store_persisted(&self.lazy.key, &persisted_value);
353 self.lazy.last_persisted = Some(persisted_value);
354 }
355 }
356}
357
358/// A container that can store and provide a persisted value. This is used in
359/// conjunction with [PersistedLazy] to define how to lazily get the value that
360/// should be persisted, and how to restore state when a persisted value is
361/// loaded during initialization.
362pub trait PersistedContainer {
363 /// The value to be persisted
364 type Value;
365
366 /// Get the current value to persist in the store
367 fn get_to_persist(&self) -> Self::Value;
368
369 /// Set the container's value, based on value loaded from the store
370 fn restore_persisted(&mut self, value: Self::Value);
371}