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}