bevy_persistent/
persistent.rs

1//! A persistent resource.
2
3use crate::prelude::*;
4
5/// A persistent resource.
6///
7/// Persistent resources are Bevy resources which are automatically synchronized with the disk.
8///
9/// They require a name for logging, a path to be saved to and loaded from, a storage format,
10/// and a default resource in case the persistent resource is created for the first time.
11///
12/// They are synchronized with the disk from the moment of their creation.
13#[derive(Component, Debug, Resource)]
14pub struct Persistent<R: Resource + Serialize + DeserializeOwned> {
15    pub(crate) name: String,
16    pub(crate) format: StorageFormat,
17    pub(crate) storage: Storage,
18    pub(crate) resource: Option<R>,
19    pub(crate) default: Option<Box<R>>,
20    pub(crate) revert_to_default_on_deserialization_errors: bool,
21}
22
23impl<R: Resource + Serialize + DeserializeOwned> Persistent<R> {
24    /// Creates a persistent resource builder.
25    pub fn builder() -> PersistentBuilder<R> {
26        PersistentBuilder {
27            name: None,
28            format: None,
29            path: None,
30            loaded: true,
31            default: None,
32            revertible: false,
33            revert_to_default_on_deserialization_errors: false,
34        }
35    }
36
37    /// Creates a persistent resource.
38    ///
39    /// # Panics
40    ///
41    /// Panics if `revert_to_default_on_deserialization_errors`
42    /// is set to `true` but `revertible` is set to `false`.
43    pub fn new(
44        name: impl ToString,
45        format: StorageFormat,
46        storage: Storage,
47        loaded: bool,
48        default: R,
49        revertible: bool,
50        revert_to_default_on_deserialization_errors: bool,
51    ) -> Result<Persistent<R>, PersistenceError> {
52        if revert_to_default_on_deserialization_errors && !revertible {
53            panic!(
54                "revert to default on deserialization errors \
55                is set for a non-revertible persistent resource"
56            );
57        }
58
59        let name = name.to_string();
60
61        if !storage.occupied() {
62            // first run
63
64            storage.initialize().map_err(|error| {
65                // initialize can only return error for filesystem storage
66                log::error!(
67                    "failed to create the parent directory for {} at {}: {}",
68                    name,
69                    storage,
70                    error,
71                );
72                error
73            })?;
74
75            storage
76                .write(&name, format, &default)
77                .map(|_| {
78                    log::info!("saved default {} to {}", name, storage);
79                })
80                .map_err(|error| {
81                    // serialization errors are already logged
82                    if !error.is_serde() {
83                        log::error!("failed to save default {} to {}: {}", name, storage, error);
84                    } else {
85                        log::error!(
86                            "failed to save default {} to {} due to a serialization error",
87                            name,
88                            storage,
89                        );
90                    }
91                    error
92                })?;
93
94            let resource = if loaded {
95                // we need to make a copy of the default resource without using clone
96                // this is because cloning can have special semantics
97                // e.g., cloning Persistent<Arc<RwLock<R>>> and changing it
98                // would change the default object, which is not desired
99                let serialized = format.serialize(&name, &default).inspect_err(|_| {
100                    log::error!("failed to clone default {} due to a serialization error", name);
101                })?;
102                let reconstructed = format.deserialize(&name, &serialized).inspect_err(|_| {
103                    log::error!("failed to clone default {} due to a deserialization error", name);
104                })?;
105
106                Some(reconstructed)
107            } else {
108                None
109            };
110            let default = if revertible { Some(Box::new(default)) } else { None };
111
112            return Ok(Persistent {
113                name,
114                format,
115                storage,
116                resource,
117                default,
118                revert_to_default_on_deserialization_errors,
119            });
120        }
121
122        let default = if revertible { Some(Box::new(default)) } else { None };
123
124        if !loaded {
125            return Ok(Persistent {
126                name,
127                format,
128                storage,
129                resource: None,
130                default,
131                revert_to_default_on_deserialization_errors,
132            });
133        }
134
135        let resource = match storage.read(&name, format) {
136            Ok(resource) => resource,
137            Err(error) => {
138                if !error.is_serde() {
139                    log::error!("failed to load {} from {}: {}", name, storage, error);
140                } else {
141                    log::error!(
142                        "failed to load {} from {} due to a deserialization error",
143                        name,
144                        storage,
145                    );
146
147                    if revert_to_default_on_deserialization_errors {
148                        log::info!(
149                            "attempting to revert {} to default in {} automatically",
150                            name,
151                            storage,
152                        );
153
154                        let mut result = Persistent {
155                            name,
156                            format,
157                            storage,
158                            resource: None,
159                            default,
160                            revert_to_default_on_deserialization_errors,
161                        };
162                        if result.revert_to_default().is_err() {
163                            // return the original deserialization error
164                            return Err(error);
165                        }
166                        if loaded && result.revert_to_default_in_memory().is_err() {
167                            // return the original deserialization error
168                            return Err(error);
169                        }
170
171                        return Ok(result);
172                    }
173                }
174                return Err(error);
175            },
176        };
177
178        log::info!("loaded {} from {}", name, storage);
179
180        Ok(Persistent {
181            name,
182            format,
183            storage,
184            resource: Some(resource),
185            default,
186            revert_to_default_on_deserialization_errors,
187        })
188    }
189}
190
191impl<R: Resource + Serialize + DeserializeOwned> Persistent<R> {
192    /// Gets the name of the resource.
193    pub fn name(&self) -> &str {
194        &self.name
195    }
196
197    /// Gets the storage format of the resource.
198    pub fn format(&self) -> StorageFormat {
199        self.format
200    }
201
202    /// Gets the storage of the resource.
203    pub fn storage(&self) -> &Storage {
204        &self.storage
205    }
206
207    /// Gets if the resource is revertible.
208    pub fn is_revertible(&self) -> bool {
209        self.default.is_some()
210    }
211
212    /// Gets if the resource is loaded.
213    pub fn is_loaded(&self) -> bool {
214        self.resource.is_some()
215    }
216
217    /// Gets if the resource is unloaded.
218    pub fn is_unloaded(&self) -> bool {
219        self.resource.is_none()
220    }
221
222    /// Gets the resource.
223    ///
224    /// # Panics
225    ///
226    /// Panics if the resource is unloaded.
227    pub fn get(&self) -> &R {
228        if let Some(resource) = &self.resource {
229            resource
230        } else {
231            panic!("tried to get unloaded {}", self.name);
232        }
233    }
234
235    /// Gets the resource mutably.
236    ///
237    /// # Panics
238    ///
239    /// Panics if the resource is unloaded.
240    pub fn get_mut(&mut self) -> &mut R {
241        if let Some(resource) = &mut self.resource {
242            resource
243        } else {
244            panic!("tried to get unloaded {} mutably", self.name);
245        }
246    }
247
248    /// Tries to get the resource.
249    pub fn try_get(&self) -> Option<&R> {
250        self.resource.as_ref()
251    }
252
253    /// Tries to get the resource mutably.
254    pub fn try_get_mut(&mut self) -> Option<&mut R> {
255        self.resource.as_mut()
256    }
257}
258
259impl<R: Resource + Serialize + DeserializeOwned> Persistent<R> {
260    /// Sets the resource.
261    ///
262    /// Changes are synchronized with the underlying storage immediately.
263    pub fn set(&mut self, new_resource: R) -> Result<(), PersistenceError> {
264        self.resource = Some(new_resource);
265        self.persist()
266    }
267
268    /// Updates the resource.
269    ///
270    /// Changes are synchronized with the underlying storage immediately.
271    ///
272    /// # Panics
273    ///
274    /// Panics if the resource is unloaded.
275    pub fn update(&mut self, updater: impl Fn(&mut R)) -> Result<(), PersistenceError> {
276        if let Some(resource) = self.resource.as_mut() {
277            updater(resource);
278            self.persist()
279        } else {
280            panic!("tried to update unloaded {}", self.name);
281        }
282    }
283
284    /// Unloads the resource from memory.
285    ///
286    /// Changes are synchronized with the underlying storage before unloading.
287    ///
288    /// # Panics
289    ///
290    /// Panics if the resource is unloaded.
291    pub fn unload(&mut self) -> Result<(), PersistenceError> {
292        if self.resource.is_some() {
293            self.persist().inspect_err(|_| {
294                log::error!(
295                    "failed to unload {} due to not being able to persist it before unloading",
296                    self.name,
297                );
298            })?;
299            self.resource = None;
300            log::info!("unloaded {}", self.name);
301        }
302        Ok(())
303    }
304
305    /// Unloads the resource from memory immediately.
306    ///
307    /// Changes are **not** synchronized with the underlying storage before unloading.
308    pub fn unload_without_persisting(&mut self) {
309        if self.resource.is_some() {
310            self.resource = None;
311            log::info!("unloaded {} without persisting", self.name);
312        }
313    }
314
315    /// Reloads the resource from the underlying storage.
316    ///
317    /// If reloading fails, the underlying resource is kept untouched.
318    pub fn reload(&mut self) -> Result<(), PersistenceError> {
319        match self.storage.read(&self.name, self.format) {
320            Ok(resource) => self.resource = Some(resource),
321            Err(error) => {
322                if !error.is_serde() {
323                    log::error!("failed to reload {} from {}: {}", self.name, self.storage, error);
324                } else {
325                    log::error!(
326                        "failed to reload {} from {} due to a deserialization error",
327                        self.storage,
328                        self.name,
329                    );
330
331                    if self.revert_to_default_on_deserialization_errors {
332                        log::info!(
333                            "attempting to revert {} to default in {} automatically",
334                            self.name,
335                            self.storage,
336                        );
337                        if self.revert_to_default().is_err() {
338                            // return the original deserialization error
339                            return Err(error);
340                        }
341                        return Ok(());
342                    }
343                }
344                return Err(error);
345            },
346        }
347        log::info!("reloaded {} from {}", self.name, self.storage);
348        Ok(())
349    }
350
351    /// Reverts the resource to it's default value.
352    ///
353    /// Loaded status is kept upon reloading.
354    ///
355    /// # Panics
356    ///
357    /// Panics if the resource is not revertible.
358    pub fn revert_to_default(&mut self) -> Result<(), PersistenceError> {
359        if !self.is_revertible() {
360            panic!("tried to revert non-revertible {}", self.name);
361        }
362
363        self.storage
364            .write(&self.name, self.format, self.default.as_ref().unwrap())
365            .map(|_| {
366                log::info!("reverted {} to default in {}", self.name, self.storage);
367            })
368            .map_err(|error| {
369                // serialization errors are logged in format module
370                if !error.is_serde() {
371                    log::error!(
372                        "failed to revert {} to default in {}: {}",
373                        self.name,
374                        self.storage,
375                        error,
376                    );
377                } else {
378                    log::error!(
379                        "failed to revert {} to default in {} due to a serialization error",
380                        self.name,
381                        self.storage,
382                    );
383                }
384                error
385            })?;
386
387        if self.is_loaded() {
388            self.revert_to_default_in_memory()?;
389        }
390
391        Ok(())
392    }
393
394    /// Reverts the resource to it's default value only in memory, not persistent storage.
395    ///
396    /// # Panics
397    ///
398    /// Panics if the resource is not revertible.
399    pub fn revert_to_default_in_memory(&mut self) -> Result<(), PersistenceError> {
400        if !self.is_revertible() {
401            panic!("tried to revert non-revertible {}", self.name);
402        }
403
404        // we need to make a copy of the default resource without using clone
405        // this is because cloning can have special semantics
406        // e.g., cloning Persistent<Arc<RwLock<R>>> and changing it
407        // would change the default object, which is not desired
408        let serialized = self
409            .format
410            .serialize(&self.name, self.default.as_ref().unwrap())
411            .inspect_err(|_| {
412                log::error!(
413                    "failed to revert {} to default in memory due to a serialization error",
414                    self.name,
415                );
416            })?;
417        let reconstructed = self.format.deserialize(&self.name, &serialized).inspect_err(|_| {
418            log::error!(
419                "failed to revert {} to default in memory due to a deserialization error",
420                self.name,
421            );
422        })?;
423
424        self.resource = Some(reconstructed);
425        log::info!("reverted {} to default in memory", self.name);
426        Ok(())
427    }
428}
429
430impl<R: Resource + Serialize + DeserializeOwned> Persistent<R> {
431    /// Writes the resource to the underlying storage.
432    ///
433    /// # Panics
434    ///
435    /// Panics if the resource is unloaded.
436    pub fn persist(&self) -> Result<(), PersistenceError> {
437        if let Some(resource) = &self.resource {
438            self.storage
439                .write(&self.name, self.format, resource)
440                .map(|_| {
441                    log::info!("saved new {} to {}", self.name, self.storage);
442                })
443                .map_err(|error| {
444                    // serialization errors are logged in format module
445                    if !error.is_serde() {
446                        log::error!(
447                            "failed to save new {} to {}: {}",
448                            self.name,
449                            self.storage,
450                            error,
451                        );
452                    } else {
453                        log::error!(
454                            "failed to save new {} to {} due to a serialization error",
455                            self.name,
456                            self.storage,
457                        );
458                    }
459                    error
460                })
461        } else {
462            panic!("tried to save unloaded {}", self.name);
463        }
464    }
465}
466
467impl<R: Resource + Serialize + DeserializeOwned> Deref for Persistent<R> {
468    type Target = R;
469
470    fn deref(&self) -> &R {
471        self.get()
472    }
473}
474
475impl<R: Resource + Serialize + DeserializeOwned> DerefMut for Persistent<R> {
476    fn deref_mut(&mut self) -> &mut R {
477        self.get_mut()
478    }
479}