rustbreak/
lib.rs

1/* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
4
5#![deny(
6    missing_docs,
7    non_camel_case_types,
8    non_snake_case,
9    path_statements,
10    trivial_casts,
11    trivial_numeric_casts,
12    unsafe_code,
13    unstable_features,
14    unused_allocation,
15    unused_import_braces,
16    unused_imports,
17    unused_must_use,
18    unused_mut,
19    while_true,
20    clippy::panic,
21    clippy::print_stdout,
22    clippy::todo,
23    //clippy::unwrap_used, // not yet in stable
24    clippy::wrong_pub_self_convention
25)]
26#![warn(clippy::pedantic)]
27// part of `clippy::pedantic`, causing many warnings
28#![allow(clippy::missing_errors_doc, clippy::module_name_repetitions)]
29
30//! # Rustbreak
31//!
32//! Rustbreak was a [Daybreak][daybreak] inspired single file Database.
33//! It has now since evolved into something else. Please check v1 for a more
34//! similar version.
35//!
36//! You will find an overview here in the docs, but to give you a more complete
37//! tale of how this is used please check the [examples][examples].
38//!
39//! At its core, Rustbreak is an attempt at making a configurable
40//! general-purpose store Database. It features the possibility of:
41//!
42//! - Choosing what kind of Data is stored in it
43//! - Which kind of Serialization is used for persistence
44//! - Which kind of persistence is used
45//!
46//! This means you can take any struct you can serialize and deserialize and
47//! stick it into this Database. It is then encoded with Ron, Yaml, JSON,
48//! Bincode, anything really that uses Serde operations!
49//!
50//! There are three helper type aliases [`MemoryDatabase`], [`FileDatabase`],
51//! and [`PathDatabase`], each backed by their respective backend.
52//!
53//! The [`MemoryBackend`] saves its data into a `Vec<u8>`, which is not that
54//! useful on its own, but is needed for compatibility with the rest of the
55//! Library.
56//!
57//! The [`FileDatabase`] is a classical file based database. You give it a path
58//! or a file, and it will use it as its storage. You still get to pick what
59//! encoding it uses.
60//!
61//! The [`PathDatabase`] is very similar, but always requires a path for
62//! creation. It features atomic saves, so that the old database contents won't
63//! be lost when panicing during the save. It should therefore be preferred to a
64//! [`FileDatabase`].
65//!
66//! Using the [`Database::with_deser`] and [`Database::with_backend`] one can
67//! switch between the representations one needs. Even at runtime! However this
68//! is only useful in a few scenarios.
69//!
70//! If you have any questions feel free to ask at the main [repo][repo].
71//!
72//! ## Quickstart
73//!
74//! Add this to your `Cargo.toml`:
75//!
76//! ```toml
77//! [dependencies.rustbreak]
78//! version = "2"
79//! features = ["ron_enc"] # You can also use "yaml_enc" or "bin_enc"
80//!                        # Check the documentation to add your own!
81//! ```
82//!
83//! ```rust
84//! # extern crate rustbreak;
85//! # use std::collections::HashMap;
86//! use rustbreak::{deser::Ron, MemoryDatabase};
87//!
88//! # fn main() {
89//! # let func = || -> Result<(), Box<dyn std::error::Error>> {
90//! let db = MemoryDatabase::<HashMap<u32, String>, Ron>::memory(HashMap::new())?;
91//!
92//! println!("Writing to Database");
93//! db.write(|db| {
94//!     db.insert(0, String::from("world"));
95//!     db.insert(1, String::from("bar"));
96//! });
97//!
98//! db.read(|db| {
99//!     // db.insert("foo".into(), String::from("bar"));
100//!     // The above line will not compile since we are only reading
101//!     println!("Hello: {:?}", db.get(&0));
102//! })?;
103//! # return Ok(()); };
104//! # func().unwrap();
105//! # }
106//! ```
107//!
108//! Or alternatively:
109//! ```rust
110//! # extern crate rustbreak;
111//! # use std::collections::HashMap;
112//! use rustbreak::{deser::Ron, MemoryDatabase};
113//!
114//! # fn main() {
115//! # let func = || -> Result<(), Box<dyn std::error::Error>> {
116//! let db = MemoryDatabase::<HashMap<u32, String>, Ron>::memory(HashMap::new())?;
117//!
118//! println!("Writing to Database");
119//! {
120//!     let mut data = db.borrow_data_mut()?;
121//!     data.insert(0, String::from("world"));
122//!     data.insert(1, String::from("bar"));
123//! }
124//!
125//! let data = db.borrow_data()?;
126//! println!("Hello: {:?}", data.get(&0));
127//! # return Ok(()); };
128//! # func().unwrap();
129//! # }
130//! ```
131//!
132//! ## Error Handling
133//!
134//! Handling errors in Rustbreak is straightforward. Every `Result` has as its
135//! fail case as [`error::RustbreakError`]. This means that you can now either
136//! continue bubbling up said error case, or handle it yourself.
137//!
138//! ```rust
139//! use rustbreak::{deser::Ron, error::RustbreakError, MemoryDatabase};
140//! let db = match MemoryDatabase::<usize, Ron>::memory(0) {
141//!     Ok(db) => db,
142//!     Err(e) => {
143//!         // Do something with `e` here
144//!         std::process::exit(1);
145//!     }
146//! };
147//! ```
148//!
149//! ## Panics
150//!
151//! This Database implementation uses [`RwLock`] and [`Mutex`] under the hood.
152//! If either the closures given to [`Database::write`] or any of the Backend
153//! implementation methods panic the respective objects are then poisoned. This
154//! means that you *cannot panic* under any circumstances in your closures or
155//! custom backends.
156//!
157//! Currently there is no way to recover from a poisoned `Database` other than
158//! re-creating it.
159//!
160//! ## Examples
161//!
162//! There are several more or less in-depth example programs you can check out!
163//! Check them out here: [Examples][examples]
164//!
165//! - `config.rs` shows you how a possible configuration file could be managed
166//!   with rustbreak
167//! - `full.rs` shows you how the database can be used as a hashmap store
168//! - `switching.rs` show you how you can easily swap out different parts of the
169//!   Database *Note*: To run this example you need to enable the feature `yaml`
170//!   like so: `cargo run --example switching --features yaml`
171//! - `server/` is a fully fledged example app written with the Rocket framework
172//!   to make a form of micro-blogging website. You will need rust nightly to
173//!   start it.
174//!
175//! ## Features
176//!
177//! Rustbreak comes with following optional features:
178//!
179//! - `ron_enc` which enables the [Ron][ron] de/serialization
180//! - `yaml_enc` which enables the Yaml de/serialization
181//! - `bin_enc` which enables the Bincode de/serialization
182//! - 'mmap' whhich enables memory map backend.
183//!
184//! [Enable them in your `Cargo.toml` file to use them.][features] You can
185//! safely have them all turned on per-default.
186//!
187//!
188//! [repo]: https://github.com/TheNeikos/rustbreak
189//! [daybreak]: https://propublica.github.io/daybreak
190//! [examples]: https://github.com/TheNeikos/rustbreak/tree/master/examples
191//! [ron]: https://github.com/ron-rs/ron
192//! [features]: https://doc.rust-lang.org/cargo/reference/specifying-dependencies.html#choosing-features
193
194pub mod backend;
195/// Different serialization and deserialization methods one can use
196pub mod deser;
197/// The rustbreak errors that can be returned
198pub mod error;
199
200/// The `DeSerializer` trait used by serialization structs
201pub use crate::deser::DeSerializer;
202/// The general error used by the Rustbreak Module
203use std::fmt::Debug;
204use std::ops::Deref;
205use std::path::PathBuf;
206use std::sync::{Mutex, RwLock, RwLockReadGuard, RwLockWriteGuard};
207
208use serde::de::DeserializeOwned;
209use serde::Serialize;
210
211#[cfg(feature = "mmap")]
212use crate::backend::MmapStorage;
213use crate::backend::{Backend, FileBackend, MemoryBackend, PathBackend};
214
215pub use crate::error::*;
216
217/// The Central Database to Rustbreak.
218///
219/// It has 3 Type Generics:
220///
221/// - `Data`: Is the Data, you must specify this
222/// - `Back`: The storage backend.
223/// - `DeSer`: The Serializer/Deserializer or short `DeSer`. Check the [`deser`]
224///   module for other strategies.
225///
226/// # Panics
227///
228/// If the backend or the de/serialization panics, the database is poisoned.
229/// This means that any subsequent writes/reads will fail with an
230/// [`error::RustbreakError::Poison`]. You can only recover from this by
231/// re-creating the Database Object.
232#[derive(Debug)]
233pub struct Database<Data, Back, DeSer> {
234    data: RwLock<Data>,
235    backend: Mutex<Back>,
236    deser: DeSer,
237}
238
239impl<Data, Back, DeSer> Database<Data, Back, DeSer>
240where
241    Data: Serialize + DeserializeOwned + Clone + Send,
242    Back: Backend,
243    DeSer: DeSerializer<Data> + Send + Sync + Clone,
244{
245    /// Write lock the database and get write access to the `Data` container.
246    ///
247    /// This gives you an exclusive lock on the memory object. Trying to open
248    /// the database in writing will block if it is currently being written
249    /// to.
250    ///
251    /// # Panics
252    ///
253    /// If you panic in the closure, the database is poisoned. This means that
254    /// any subsequent writes/reads will fail with an
255    /// [`error::RustbreakError::Poison`]. You can only recover from
256    /// this by re-creating the Database Object.
257    ///
258    /// If you do not have full control over the code being written, and cannot
259    /// incur the cost of having a single operation panicking then use
260    /// [`Database::write_safe`].
261    ///
262    /// # Examples
263    ///
264    /// ```rust
265    /// # #[macro_use] extern crate serde_derive;
266    /// # extern crate rustbreak;
267    /// # extern crate serde;
268    /// # extern crate tempfile;
269    /// use rustbreak::{deser::Ron, FileDatabase};
270    ///
271    /// #[derive(Debug, Serialize, Deserialize, Clone)]
272    /// struct Data {
273    ///     level: u32,
274    /// }
275    ///
276    /// # fn main() {
277    /// # let func = || -> Result<(), Box<dyn std::error::Error>> {
278    /// # let file = tempfile::tempfile()?;
279    /// let db = FileDatabase::<Data, Ron>::from_file(file, Data { level: 0 })?;
280    ///
281    /// db.write(|db| {
282    ///     db.level = 42;
283    /// })?;
284    ///
285    /// // You can also return from a `.read()`. But don't forget that you cannot return references
286    /// // into the structure
287    /// let value = db.read(|db| db.level)?;
288    /// assert_eq!(42, value);
289    /// # return Ok(());
290    /// # };
291    /// # func().unwrap();
292    /// # }
293    /// ```
294    pub fn write<T, R>(&self, task: T) -> error::Result<R>
295    where
296        T: FnOnce(&mut Data) -> R,
297    {
298        let mut lock = self.data.write().map_err(|_| RustbreakError::Poison)?;
299        Ok(task(&mut lock))
300    }
301
302    /// Write lock the database and get write access to the `Data` container in
303    /// a safe way.
304    ///
305    /// This gives you an exclusive lock on the memory object. Trying to open
306    /// the database in writing will block if it is currently being written
307    /// to.
308    ///
309    /// This differs to `Database::write` in that a clone of the internal data
310    /// is made, which is then passed to the closure. Only if the closure
311    /// doesn't panic is the internal model updated.
312    ///
313    /// Depending on the size of the database this can be very costly. This is a
314    /// tradeoff to make for panic safety.
315    ///
316    /// You should read the documentation about this:
317    /// [`UnwindSafe`](https://doc.rust-lang.org/std/panic/trait.UnwindSafe.html)
318    ///
319    /// # Panics
320    ///
321    /// When the closure panics, it is caught and a
322    /// [`error::RustbreakError::WritePanic`] will be returned.
323    ///
324    /// # Examples
325    ///
326    /// ```rust
327    /// # #[macro_use] extern crate serde_derive;
328    /// # extern crate rustbreak;
329    /// # extern crate serde;
330    /// # extern crate tempfile;
331    /// use rustbreak::{
332    ///     deser::Ron,
333    ///     error::RustbreakError,
334    ///     FileDatabase,
335    /// };
336    ///
337    /// #[derive(Debug, Serialize, Deserialize, Clone)]
338    /// struct Data {
339    ///     level: u32,
340    /// }
341    ///
342    /// # fn main() {
343    /// # let func = || -> Result<(), Box<dyn std::error::Error>> {
344    /// # let file = tempfile::tempfile()?;
345    /// let db = FileDatabase::<Data, Ron>::from_file(file, Data { level: 0 })?;
346    ///
347    /// let result = db
348    ///     .write_safe(|db| {
349    ///         db.level = 42;
350    ///         panic!("We panic inside the write code.");
351    ///     })
352    ///     .expect_err("This should have been caught");
353    ///
354    /// match result {
355    ///     RustbreakError::WritePanic => {
356    ///         // We can now handle this, in this example we will just ignore it
357    ///     }
358    ///     e => {
359    ///         println!("{:#?}", e);
360    ///         // You should always have generic error catching here.
361    ///         // This future-proofs your code, and makes your code more robust.
362    ///         // In this example this is unreachable though, and to assert that we have this
363    ///         // macro here
364    ///         unreachable!();
365    ///     }
366    /// }
367    ///
368    /// // We read it back out again, it has not changed
369    /// let value = db.read(|db| db.level)?;
370    /// assert_eq!(0, value);
371    /// # return Ok(());
372    /// # };
373    /// # func().unwrap();
374    /// # }
375    /// ```
376    pub fn write_safe<T>(&self, task: T) -> error::Result<()>
377    where
378        T: FnOnce(&mut Data) + std::panic::UnwindSafe,
379    {
380        let mut lock = self.data.write().map_err(|_| RustbreakError::Poison)?;
381        let mut data = lock.clone();
382        std::panic::catch_unwind(::std::panic::AssertUnwindSafe(|| {
383            task(&mut data);
384        }))
385        .map_err(|_| RustbreakError::WritePanic)?;
386        *lock = data;
387        Ok(())
388    }
389
390    /// Read lock the database and get read access to the `Data` container.
391    ///
392    /// This gives you a read-only lock on the database. You can have as many
393    /// readers in parallel as you wish.
394    ///
395    /// # Errors
396    ///
397    /// May return:
398    ///
399    /// - [`error::RustbreakError::Backend`]
400    ///
401    /// # Panics
402    ///
403    /// If you panic in the closure, the database is poisoned. This means that
404    /// any subsequent writes/reads will fail with an
405    /// [`error::RustbreakError::Poison`]. You can only recover from
406    /// this by re-creating the Database Object.
407    pub fn read<T, R>(&self, task: T) -> error::Result<R>
408    where
409        T: FnOnce(&Data) -> R,
410    {
411        let mut lock = self.data.read().map_err(|_| RustbreakError::Poison)?;
412        Ok(task(&mut lock))
413    }
414
415    /// Read lock the database and get access to the underlying struct.
416    ///
417    /// This gives you access to the underlying struct, allowing for simple read
418    /// only operations on it.
419    ///
420    /// # Examples
421    ///
422    /// ```rust
423    /// # #[macro_use] extern crate serde_derive;
424    /// # extern crate rustbreak;
425    /// # extern crate serde;
426    /// # extern crate tempfile;
427    /// use rustbreak::{deser::Ron, FileDatabase};
428    ///
429    /// #[derive(Debug, Serialize, Deserialize, Clone)]
430    /// struct Data {
431    ///     level: u32,
432    /// }
433    ///
434    /// # fn main() {
435    /// # let func = || -> Result<(), Box<dyn std::error::Error>> {
436    /// # let file = tempfile::tempfile()?;
437    /// let db = FileDatabase::<Data, Ron>::from_file(file, Data { level: 0 })?;
438    ///
439    /// db.write(|db| {
440    ///     db.level = 42;
441    /// })?;
442    ///
443    /// let data = db.borrow_data()?;
444    ///
445    /// assert_eq!(42, data.level);
446    /// # return Ok(());
447    /// # };
448    /// # func().unwrap();
449    /// # }
450    /// ```
451    pub fn borrow_data<'a>(&'a self) -> error::Result<RwLockReadGuard<'a, Data>> {
452        self.data.read().map_err(|_| RustbreakError::Poison)
453    }
454
455    /// Write lock the database and get access to the underlying struct.
456    ///
457    /// This gives you access to the underlying struct, allowing you to modify
458    /// it.
459    ///
460    /// # Panics
461    ///
462    /// If you panic while holding this reference, the database is poisoned.
463    /// This means that any subsequent writes/reads will fail with an
464    /// [`error::RustbreakError::Poison`]. You can only recover from
465    /// this by re-creating the Database Object.
466    ///
467    /// If you do not have full control over the code being written, and cannot
468    /// incur the cost of having a single operation panicking then use
469    /// [`Database::write_safe`].
470    ///
471    /// # Examples
472    ///
473    /// ```rust
474    /// # #[macro_use] extern crate serde_derive;
475    /// # extern crate rustbreak;
476    /// # extern crate serde;
477    /// # extern crate tempfile;
478    /// use rustbreak::{deser::Ron, FileDatabase};
479    ///
480    /// #[derive(Debug, Serialize, Deserialize, Clone)]
481    /// struct Data {
482    ///     level: u32,
483    /// }
484    ///
485    /// # fn main() {
486    /// # let func = || -> Result<(), Box<dyn std::error::Error>> {
487    /// # let file = tempfile::tempfile()?;
488    /// let db = FileDatabase::<Data, Ron>::from_file(file, Data { level: 0 })?;
489    ///
490    /// {
491    ///     let mut data = db.borrow_data_mut()?;
492    ///     data.level = 42;
493    /// }
494    ///
495    /// let data = db.borrow_data()?;
496    ///
497    /// assert_eq!(42, data.level);
498    /// # return Ok(());
499    /// # };
500    /// # func().unwrap();
501    /// # }
502    /// ```
503    pub fn borrow_data_mut<'a>(&'a self) -> error::Result<RwLockWriteGuard<'a, Data>> {
504        self.data.write().map_err(|_| RustbreakError::Poison)
505    }
506
507    /// Load data from backend and return this data.
508    fn load_from_backend(backend: &mut Back, deser: &DeSer) -> error::Result<Data> {
509        let new_data = deser.deserialize(&backend.get_data()?[..])?;
510
511        Ok(new_data)
512    }
513
514    /// Like [`Self::load`] but returns the write lock to data it used.
515    fn load_get_data_lock(&self) -> error::Result<RwLockWriteGuard<'_, Data>> {
516        let mut backend_lock = self.backend.lock().map_err(|_| RustbreakError::Poison)?;
517
518        let fresh_data = Self::load_from_backend(&mut backend_lock, &self.deser)?;
519        drop(backend_lock);
520
521        let mut data_write_lock = self.data.write().map_err(|_| RustbreakError::Poison)?;
522        *data_write_lock = fresh_data;
523        Ok(data_write_lock)
524    }
525
526    /// Load the data from the backend.
527    pub fn load(&self) -> error::Result<()> {
528        self.load_get_data_lock().map(|_| ())
529    }
530
531    /// Like [`Self::save`] but with explicit read (or write) lock to data.
532    fn save_data_locked<L: Deref<Target = Data>>(&self, lock: L) -> error::Result<()> {
533        let ser = self.deser.serialize(lock.deref())?;
534        drop(lock);
535
536        let mut backend = self.backend.lock().map_err(|_| RustbreakError::Poison)?;
537        backend.put_data(&ser)?;
538        Ok(())
539    }
540
541    /// Flush the data structure to the backend.
542    pub fn save(&self) -> error::Result<()> {
543        let data = self.data.read().map_err(|_| RustbreakError::Poison)?;
544        self.save_data_locked(data)
545    }
546
547    /// Get a clone of the data as it is in memory right now.
548    ///
549    /// To make sure you have the latest data, call this method with `load`
550    /// true.
551    pub fn get_data(&self, load: bool) -> error::Result<Data> {
552        let data = if load {
553            self.load_get_data_lock()?
554        } else {
555            self.data.write().map_err(|_| RustbreakError::Poison)?
556        };
557        Ok(data.clone())
558    }
559
560    /// Puts the data as is into memory.
561    ///
562    /// To save the data afterwards, call with `save` true.
563    pub fn put_data(&self, new_data: Data, save: bool) -> error::Result<()> {
564        let mut data = self.data.write().map_err(|_| RustbreakError::Poison)?;
565        *data = new_data;
566        if save {
567            self.save_data_locked(data)
568        } else {
569            Ok(())
570        }
571    }
572
573    /// Create a database from its constituents.
574    pub fn from_parts(data: Data, backend: Back, deser: DeSer) -> Self {
575        Self {
576            data: RwLock::new(data),
577            backend: Mutex::new(backend),
578            deser,
579        }
580    }
581
582    /// Break a database into its individual parts.
583    pub fn into_inner(self) -> error::Result<(Data, Back, DeSer)> {
584        Ok((
585            self.data.into_inner().map_err(|_| RustbreakError::Poison)?,
586            self.backend
587                .into_inner()
588                .map_err(|_| RustbreakError::Poison)?,
589            self.deser,
590        ))
591    }
592
593    /// Tries to clone the Data in the Database.
594    ///
595    /// This method returns a `MemoryDatabase` which has an empty vector as a
596    /// backend initially. This means that the user is responsible for assigning
597    /// a new backend if an alternative is wanted.
598    ///
599    /// # Examples
600    ///
601    /// ```rust
602    /// # #[macro_use] extern crate serde_derive;
603    /// # extern crate rustbreak;
604    /// # extern crate serde;
605    /// # extern crate tempfile;
606    /// use rustbreak::{deser::Ron, FileDatabase};
607    ///
608    /// #[derive(Debug, Serialize, Deserialize, Clone)]
609    /// struct Data {
610    ///     level: u32,
611    /// }
612    ///
613    /// # fn main() {
614    /// # let func = || -> Result<(), Box<dyn std::error::Error>> {
615    /// # let file = tempfile::tempfile()?;
616    /// let db = FileDatabase::<Data, Ron>::from_file(file, Data { level: 0 })?;
617    ///
618    /// db.write(|db| {
619    ///     db.level = 42;
620    /// })?;
621    ///
622    /// db.save()?;
623    ///
624    /// let other_db = db.try_clone()?;
625    ///
626    /// // You can also return from a `.read()`. But don't forget that you cannot return references
627    /// // into the structure
628    /// let value = other_db.read(|db| db.level)?;
629    /// assert_eq!(42, value);
630    /// # return Ok(());
631    /// # };
632    /// # func().unwrap();
633    /// # }
634    /// ```
635    pub fn try_clone(&self) -> error::Result<MemoryDatabase<Data, DeSer>> {
636        let lock = self.data.read().map_err(|_| RustbreakError::Poison)?;
637
638        Ok(Database {
639            data: RwLock::new(lock.clone()),
640            backend: Mutex::new(MemoryBackend::new()),
641            deser: self.deser.clone(),
642        })
643    }
644}
645
646/// A database backed by a file.
647pub type FileDatabase<D, DS> = Database<D, FileBackend, DS>;
648
649impl<Data, DeSer> Database<Data, FileBackend, DeSer>
650where
651    Data: Serialize + DeserializeOwned + Clone + Send,
652    DeSer: DeSerializer<Data> + Send + Sync + Clone,
653{
654    /// Create new [`FileDatabase`] from the file at [`Path`](std::path::Path),
655    /// and load the contents.
656    pub fn load_from_path<S>(path: S) -> error::Result<Self>
657    where
658        S: AsRef<std::path::Path>,
659    {
660        let mut backend = FileBackend::from_path_or_fail(path)?;
661        let deser = DeSer::default();
662        let data = Self::load_from_backend(&mut backend, &deser)?;
663
664        let db = Self {
665            data: RwLock::new(data),
666            backend: Mutex::new(backend),
667            deser,
668        };
669        Ok(db)
670    }
671
672    /// Load [`FileDatabase`] at `path` or initialise with `data`.
673    ///
674    /// Create new [`FileDatabase`] from the file at [`Path`](std::path::Path),
675    /// and load the contents. If the file does not exist, initialise with
676    /// `data`.
677    pub fn load_from_path_or<S>(path: S, data: Data) -> error::Result<Self>
678    where
679        S: AsRef<std::path::Path>,
680    {
681        let (mut backend, exists) = FileBackend::from_path_or_create(path)?;
682        let deser = DeSer::default();
683        if !exists {
684            let ser = deser.serialize(&data)?;
685            backend.put_data(&ser)?;
686        }
687
688        let db = Self {
689            data: RwLock::new(data),
690            backend: Mutex::new(backend),
691            deser,
692        };
693
694        if exists {
695            db.load()?;
696        }
697
698        Ok(db)
699    }
700
701    /// Load [`FileDatabase`] at `path` or initialise with `closure`.
702    ///
703    /// Create new [`FileDatabase`] from the file at [`Path`](std::path::Path),
704    /// and load the contents. If the file does not exist, `closure` is
705    /// called and the database is initialised with it's return value.
706    pub fn load_from_path_or_else<S, C>(path: S, closure: C) -> error::Result<Self>
707    where
708        S: AsRef<std::path::Path>,
709        C: FnOnce() -> Data,
710    {
711        let (mut backend, exists) = FileBackend::from_path_or_create(path)?;
712        let deser = DeSer::default();
713        let data = if exists {
714            Self::load_from_backend(&mut backend, &deser)?
715        } else {
716            let data = closure();
717
718            let ser = deser.serialize(&data)?;
719            backend.put_data(&ser)?;
720
721            data
722        };
723
724        let db = Self {
725            data: RwLock::new(data),
726            backend: Mutex::new(backend),
727            deser,
728        };
729        Ok(db)
730    }
731
732    /// Create [`FileDatabase`] at `path`. Initialise with `data` if the file
733    /// doesn't exist.
734    ///
735    /// Create new [`FileDatabase`] from the file at [`Path`](std::path::Path).
736    /// Contents are not loaded. If the file does not exist, it is
737    /// initialised with `data`. Frontend is always initialised with `data`.
738    pub fn create_at_path<S>(path: S, data: Data) -> error::Result<Self>
739    where
740        S: AsRef<std::path::Path>,
741    {
742        let (mut backend, exists) = FileBackend::from_path_or_create(path)?;
743        let deser = DeSer::default();
744        if !exists {
745            let ser = deser.serialize(&data)?;
746            backend.put_data(&ser)?;
747        }
748
749        let db = Self {
750            data: RwLock::new(data),
751            backend: Mutex::new(backend),
752            deser,
753        };
754        Ok(db)
755    }
756
757    /// Create new [`FileDatabase`] from a file.
758    pub fn from_file(file: std::fs::File, data: Data) -> error::Result<Self> {
759        let backend = FileBackend::from_file(file);
760
761        Ok(Self {
762            data: RwLock::new(data),
763            backend: Mutex::new(backend),
764            deser: DeSer::default(),
765        })
766    }
767}
768
769impl<Data, DeSer> Database<Data, FileBackend, DeSer>
770where
771    Data: Serialize + DeserializeOwned + Clone + Send + Default,
772    DeSer: DeSerializer<Data> + Send + Sync + Clone,
773{
774    /// Load [`FileDatabase`] at `path` or initialise with `Data::default()`.
775    ///
776    /// Create new [`FileDatabase`] from the file at [`Path`](std::path::Path),
777    /// and load the contents. If the file does not exist, initialise with
778    /// `Data::default`.
779    pub fn load_from_path_or_default<S>(path: S) -> error::Result<Self>
780    where
781        S: AsRef<std::path::Path>,
782    {
783        Self::load_from_path_or_else(path, Data::default)
784    }
785}
786
787/// A database backed by a file, using atomic saves.
788pub type PathDatabase<D, DS> = Database<D, PathBackend, DS>;
789
790impl<Data, DeSer> Database<Data, PathBackend, DeSer>
791where
792    Data: Serialize + DeserializeOwned + Clone + Send,
793    DeSer: DeSerializer<Data> + Send + Sync + Clone,
794{
795    /// Create new [`PathDatabase`] from the file at [`Path`](std::path::Path),
796    /// and load the contents.
797    pub fn load_from_path(path: PathBuf) -> error::Result<Self> {
798        let mut backend = PathBackend::from_path_or_fail(path)?;
799        let deser = DeSer::default();
800        let data = Self::load_from_backend(&mut backend, &deser)?;
801
802        let db = Self {
803            data: RwLock::new(data),
804            backend: Mutex::new(backend),
805            deser,
806        };
807        Ok(db)
808    }
809
810    /// Load [`PathDatabase`] at `path` or initialise with `data`.
811    ///
812    /// Create new [`PathDatabase`] from the file at [`Path`](std::path::Path),
813    /// and load the contents. If the file does not exist, initialise with
814    /// `data`.
815    pub fn load_from_path_or(path: PathBuf, data: Data) -> error::Result<Self> {
816        let (mut backend, exists) = PathBackend::from_path_or_create(path)?;
817        let deser = DeSer::default();
818        if !exists {
819            let ser = deser.serialize(&data)?;
820            backend.put_data(&ser)?;
821        }
822
823        let db = Self {
824            data: RwLock::new(data),
825            backend: Mutex::new(backend),
826            deser,
827        };
828
829        if exists {
830            db.load()?;
831        }
832
833        Ok(db)
834    }
835
836    /// Load [`PathDatabase`] at `path` or initialise with `closure`.
837    ///
838    /// Create new [`PathDatabase`] from the file at [`Path`](std::path::Path),
839    /// and load the contents. If the file does not exist, `closure` is
840    /// called and the database is initialised with it's return value.
841    pub fn load_from_path_or_else<C>(path: PathBuf, closure: C) -> error::Result<Self>
842    where
843        C: FnOnce() -> Data,
844    {
845        let (mut backend, exists) = PathBackend::from_path_or_create(path)?;
846        let deser = DeSer::default();
847        let data = if exists {
848            Self::load_from_backend(&mut backend, &deser)?
849        } else {
850            let data = closure();
851
852            let ser = deser.serialize(&data)?;
853            backend.put_data(&ser)?;
854
855            data
856        };
857
858        let db = Self {
859            data: RwLock::new(data),
860            backend: Mutex::new(backend),
861            deser,
862        };
863        Ok(db)
864    }
865
866    /// Create [`PathDatabase`] at `path`. Initialise with `data` if the file
867    /// doesn't exist.
868    ///
869    /// Create new [`PathDatabase`] from the file at [`Path`](std::path::Path).
870    /// Contents are not loaded. If the file does not exist, it is
871    /// initialised with `data`. Frontend is always initialised with `data`.
872    pub fn create_at_path(path: PathBuf, data: Data) -> error::Result<Self> {
873        let (mut backend, exists) = PathBackend::from_path_or_create(path)?;
874        let deser = DeSer::default();
875        if !exists {
876            let ser = deser.serialize(&data)?;
877            backend.put_data(&ser)?;
878        }
879
880        let db = Self {
881            data: RwLock::new(data),
882            backend: Mutex::new(backend),
883            deser,
884        };
885        Ok(db)
886    }
887}
888
889impl<Data, DeSer> Database<Data, PathBackend, DeSer>
890where
891    Data: Serialize + DeserializeOwned + Clone + Send + Default,
892    DeSer: DeSerializer<Data> + Send + Sync + Clone,
893{
894    /// Load [`PathDatabase`] at `path` or initialise with `Data::default()`.
895    ///
896    /// Create new [`PathDatabase`] from the file at [`Path`](std::path::Path),
897    /// and load the contents. If the file does not exist, initialise with
898    /// `Data::default`.
899    pub fn load_from_path_or_default(path: PathBuf) -> error::Result<Self> {
900        Self::load_from_path_or_else(path, Data::default)
901    }
902}
903
904/// A database backed by a byte vector (`Vec<u8>`).
905pub type MemoryDatabase<D, DS> = Database<D, MemoryBackend, DS>;
906
907impl<Data, DeSer> Database<Data, MemoryBackend, DeSer>
908where
909    Data: Serialize + DeserializeOwned + Clone + Send,
910    DeSer: DeSerializer<Data> + Send + Sync + Clone,
911{
912    /// Create new in-memory database.
913    pub fn memory(data: Data) -> error::Result<Self> {
914        let backend = MemoryBackend::new();
915
916        Ok(Self {
917            data: RwLock::new(data),
918            backend: Mutex::new(backend),
919            deser: DeSer::default(),
920        })
921    }
922}
923
924/// A database backed by anonymous memory map.
925#[cfg(feature = "mmap")]
926pub type MmapDatabase<D, DS> = Database<D, MmapStorage, DS>;
927
928#[cfg(feature = "mmap")]
929impl<Data, DeSer> Database<Data, MmapStorage, DeSer>
930where
931    Data: Serialize + DeserializeOwned + Clone + Send,
932    DeSer: DeSerializer<Data> + Send + Sync + Clone,
933{
934    /// Create new [`MmapDatabase`].
935    pub fn mmap(data: Data) -> error::Result<Self> {
936        let backend = MmapStorage::new()?;
937
938        Ok(Self {
939            data: RwLock::new(data),
940            backend: Mutex::new(backend),
941            deser: DeSer::default(),
942        })
943    }
944
945    /// Create new [`MmapDatabase`] with specified initial size.
946    pub fn mmap_with_size(data: Data, size: usize) -> error::Result<Self> {
947        let backend = MmapStorage::with_size(size)?;
948
949        Ok(Self {
950            data: RwLock::new(data),
951            backend: Mutex::new(backend),
952            deser: DeSer::default(),
953        })
954    }
955}
956
957impl<Data, Back, DeSer> Database<Data, Back, DeSer> {
958    /// Exchanges the `DeSerialization` strategy with the new one.
959    pub fn with_deser<T>(self, deser: T) -> Database<Data, Back, T> {
960        Database {
961            backend: self.backend,
962            data: self.data,
963            deser,
964        }
965    }
966}
967
968impl<Data, Back, DeSer> Database<Data, Back, DeSer> {
969    /// Exchanges the `Backend` with the new one.
970    ///
971    /// The new backend does not necessarily have the latest data saved to it,
972    /// so a `.save` should be called to make sure that it is saved.
973    pub fn with_backend<T>(self, backend: T) -> Database<Data, T, DeSer> {
974        Database {
975            backend: Mutex::new(backend),
976            data: self.data,
977            deser: self.deser,
978        }
979    }
980}
981
982impl<Data, Back, DeSer> Database<Data, Back, DeSer>
983where
984    Data: Serialize + DeserializeOwned + Clone + Send,
985    Back: Backend,
986    DeSer: DeSerializer<Data> + Send + Sync + Clone,
987{
988    /// Converts from one data type to another.
989    ///
990    /// This method is useful to migrate from one datatype to another.
991    pub fn convert_data<C, OutputData>(
992        self,
993        convert: C,
994    ) -> error::Result<Database<OutputData, Back, DeSer>>
995    where
996        OutputData: Serialize + DeserializeOwned + Clone + Send,
997        C: FnOnce(Data) -> OutputData,
998        DeSer: DeSerializer<OutputData> + Send + Sync,
999    {
1000        let (data, backend, deser) = self.into_inner()?;
1001        Ok(Database {
1002            data: RwLock::new(convert(data)),
1003            backend: Mutex::new(backend),
1004            deser,
1005        })
1006    }
1007}
1008
1009#[cfg(test)]
1010mod tests {
1011    use super::*;
1012    use std::collections::HashMap;
1013    use tempfile::NamedTempFile;
1014
1015    type TestData = HashMap<usize, String>;
1016    type TestDb<B> = Database<TestData, B, crate::deser::Ron>;
1017    type TestMemDb = TestDb<MemoryBackend>;
1018
1019    fn test_data() -> TestData {
1020        let mut data = HashMap::new();
1021        data.insert(1, "Hello World".to_string());
1022        data.insert(100, "Rustbreak".to_string());
1023        data
1024    }
1025
1026    /// Used to test that `Default::default` isn't called.
1027    #[derive(Clone, Debug, Serialize, serde::Deserialize)]
1028    struct PanicDefault;
1029    impl Default for PanicDefault {
1030        fn default() -> Self {
1031            panic!("`default` was called but should not")
1032        }
1033    }
1034
1035    #[test]
1036    fn create_db_and_read() {
1037        let db = TestMemDb::memory(test_data()).expect("Could not create database");
1038        assert_eq!(
1039            "Hello World",
1040            db.read(|d| d.get(&1).cloned())
1041                .expect("Rustbreak read error")
1042                .expect("Should be `Some` but was `None`")
1043        );
1044        assert_eq!(
1045            "Rustbreak",
1046            db.read(|d| d.get(&100).cloned())
1047                .expect("Rustbreak read error")
1048                .expect("Should be `Some` but was `None`")
1049        );
1050    }
1051
1052    #[test]
1053    fn write_twice() {
1054        let db = TestMemDb::memory(test_data()).expect("Could not create database");
1055        db.write(|d| d.insert(3, "Write to db".to_string()))
1056            .expect("Rustbreak write error");
1057        db.write(|d| d.insert(3, "Second write".to_string()))
1058            .expect("Rustbreak write error");
1059        assert_eq!(
1060            "Hello World",
1061            db.read(|d| d.get(&1).cloned())
1062                .expect("Rustbreak read error")
1063                .expect("Should be `Some` but was `None`")
1064        );
1065        assert_eq!(
1066            "Rustbreak",
1067            db.read(|d| d.get(&100).cloned())
1068                .expect("Rustbreak read error")
1069                .expect("Should be `Some` but was `None`")
1070        );
1071        assert_eq!(
1072            "Second write",
1073            db.read(|d| d.get(&3).cloned())
1074                .expect("Rustbreak read error")
1075                .expect("Should be `Some` but was `None`")
1076        );
1077    }
1078
1079    #[test]
1080    fn save_load() {
1081        let db = TestMemDb::memory(test_data()).expect("Could not create database");
1082        db.save().expect("Rustbreak save error");
1083        db.write(|d| d.clear()).expect("Rustbreak write error");
1084        db.load().expect("Rustbreak load error");
1085        assert_eq!(
1086            "Hello World",
1087            db.read(|d| d.get(&1).cloned())
1088                .expect("Rustbreak read error")
1089                .expect("Should be `Some` but was `None`")
1090        );
1091        assert_eq!(
1092            "Rustbreak",
1093            db.read(|d| d.get(&100).cloned())
1094                .expect("Rustbreak read error")
1095                .expect("Should be `Some` but was `None`")
1096        );
1097    }
1098
1099    #[test]
1100    fn writesafe_twice() {
1101        let db = TestMemDb::memory(test_data()).expect("Could not create database");
1102        db.write_safe(|d| {
1103            d.insert(3, "Write to db".to_string());
1104        })
1105        .expect("Rustbreak write error");
1106        db.write_safe(|d| {
1107            d.insert(3, "Second write".to_string());
1108        })
1109        .expect("Rustbreak write error");
1110        assert_eq!(
1111            "Hello World",
1112            db.read(|d| d.get(&1).cloned())
1113                .expect("Rustbreak read error")
1114                .expect("Should be `Some` but was `None`")
1115        );
1116        assert_eq!(
1117            "Rustbreak",
1118            db.read(|d| d.get(&100).cloned())
1119                .expect("Rustbreak read error")
1120                .expect("Should be `Some` but was `None`")
1121        );
1122        assert_eq!(
1123            "Second write",
1124            db.read(|d| d.get(&3).cloned())
1125                .expect("Rustbreak read error")
1126                .expect("Should be `Some` but was `None`")
1127        );
1128    }
1129
1130    #[test]
1131    fn writesafe_panic() {
1132        let db = TestMemDb::memory(test_data()).expect("Could not create database");
1133        let err = db
1134            .write_safe(|d| {
1135                d.clear();
1136                panic!("Panic should be catched")
1137            })
1138            .expect_err("Did not error on panic in safe write!");
1139        assert!(matches!(err, RustbreakError::WritePanic));
1140
1141        assert_eq!(
1142            "Hello World",
1143            db.read(|d| d.get(&1).cloned())
1144                .expect("Rustbreak read error")
1145                .expect("Should be `Some` but was `None`")
1146        );
1147        assert_eq!(
1148            "Rustbreak",
1149            db.read(|d| d.get(&100).cloned())
1150                .expect("Rustbreak read error")
1151                .expect("Should be `Some` but was `None`")
1152        );
1153    }
1154
1155    #[test]
1156    fn borrow_data_twice() {
1157        let db = TestMemDb::memory(test_data()).expect("Could not create database");
1158        let readlock1 = db.borrow_data().expect("Rustbreak readlock error");
1159        let readlock2 = db.borrow_data().expect("Rustbreak readlock error");
1160        assert_eq!(
1161            "Hello World",
1162            readlock1.get(&1).expect("Should be `Some` but was `None`")
1163        );
1164        assert_eq!(
1165            "Hello World",
1166            readlock2.get(&1).expect("Should be `Some` but was `None`")
1167        );
1168        assert_eq!(
1169            "Rustbreak",
1170            readlock1
1171                .get(&100)
1172                .expect("Should be `Some` but was `None`")
1173        );
1174        assert_eq!(
1175            "Rustbreak",
1176            readlock2
1177                .get(&100)
1178                .expect("Should be `Some` but was `None`")
1179        );
1180        assert_eq!(*readlock1, *readlock2);
1181    }
1182
1183    #[test]
1184    fn borrow_data_mut() {
1185        let db = TestMemDb::memory(test_data()).expect("Could not create database");
1186        let mut writelock = db.borrow_data_mut().expect("Rustbreak writelock error");
1187        writelock.insert(3, "Write to db".to_string());
1188        drop(writelock);
1189        assert_eq!(
1190            "Hello World",
1191            db.read(|d| d.get(&1).cloned())
1192                .expect("Rustbreak read error")
1193                .expect("Should be `Some` but was `None`")
1194        );
1195        assert_eq!(
1196            "Rustbreak",
1197            db.read(|d| d.get(&100).cloned())
1198                .expect("Rustbreak read error")
1199                .expect("Should be `Some` but was `None`")
1200        );
1201        assert_eq!(
1202            "Write to db",
1203            db.read(|d| d.get(&3).cloned())
1204                .expect("Rustbreak read error")
1205                .expect("Should be `Some` but was `None`")
1206        );
1207    }
1208
1209    #[test]
1210    fn get_data_mem() {
1211        let db = TestMemDb::memory(test_data()).expect("Could not create database");
1212        let data = db.get_data(false).expect("could not get data");
1213        assert_eq!(test_data(), data);
1214    }
1215
1216    #[test]
1217    fn get_data_load() {
1218        let db = TestMemDb::memory(test_data()).expect("Could not create database");
1219        db.save().expect("Rustbreak save error");
1220        db.write(|d| d.clear()).expect("Rustbreak write error");
1221        let data = db.get_data(true).expect("could not get data");
1222        assert_eq!(test_data(), data);
1223    }
1224
1225    #[test]
1226    fn put_data_mem() {
1227        let db = TestMemDb::memory(TestData::default()).expect("Could not create database");
1228        db.put_data(test_data(), false).expect("could not put data");
1229        assert_eq!(
1230            "Hello World",
1231            db.read(|d| d.get(&1).cloned())
1232                .expect("Rustbreak read error")
1233                .expect("Should be `Some` but was `None`")
1234        );
1235        assert_eq!(
1236            "Rustbreak",
1237            db.read(|d| d.get(&100).cloned())
1238                .expect("Rustbreak read error")
1239                .expect("Should be `Some` but was `None`")
1240        );
1241        let data = db.get_data(false).expect("could not get data");
1242        assert_eq!(test_data(), data);
1243    }
1244
1245    #[test]
1246    fn put_data_save() {
1247        let db = TestMemDb::memory(TestData::default()).expect("Could not create database");
1248        db.put_data(test_data(), true).expect("could not put data");
1249        db.load().expect("Rustbreak load error");
1250        assert_eq!(
1251            "Hello World",
1252            db.read(|d| d.get(&1).cloned())
1253                .expect("Rustbreak read error")
1254                .expect("Should be `Some` but was `None`")
1255        );
1256        assert_eq!(
1257            "Rustbreak",
1258            db.read(|d| d.get(&100).cloned())
1259                .expect("Rustbreak read error")
1260                .expect("Should be `Some` but was `None`")
1261        );
1262        let data = db.get_data(false).expect("could not get data");
1263        assert_eq!(test_data(), data);
1264    }
1265
1266    #[test]
1267    fn save_and_into_inner() {
1268        let db = TestMemDb::memory(test_data()).expect("Could not create database");
1269        db.save().expect("Rustbreak save error");
1270        let (data, mut backend, _) = db
1271            .into_inner()
1272            .expect("error calling `Database.into_inner`");
1273        assert_eq!(test_data(), data);
1274        let parsed: TestData =
1275            ron::de::from_reader(&backend.get_data().expect("could not get data from backend")[..])
1276                .expect("backend contains invalid RON");
1277        assert_eq!(test_data(), parsed);
1278    }
1279
1280    #[test]
1281    fn clone() {
1282        let db1 = TestMemDb::memory(test_data()).expect("Could not create database");
1283        let readlock1 = db1.borrow_data().expect("Rustbreak readlock error");
1284        let db2 = db1.try_clone().expect("Rustbreak clone error");
1285        let readlock2 = db2.borrow_data().expect("Rustbreak readlock error");
1286        assert_eq!(test_data(), *readlock1);
1287        assert_eq!(*readlock1, *readlock2);
1288    }
1289
1290    #[test]
1291    fn allow_databases_with_boxed_backend() {
1292        let db =
1293            MemoryDatabase::<Vec<u64>, crate::deser::Ron>::memory(vec![]).expect("To be created");
1294        let db: Database<_, Box<dyn Backend>, _> = db.with_backend(Box::new(MemoryBackend::new()));
1295        db.put_data(vec![1, 2, 3], true)
1296            .expect("Can save data in memory");
1297        assert_eq!(
1298            &[1, 2, 3],
1299            &db.get_data(true).expect("Can get data from memory")[..]
1300        );
1301    }
1302
1303    /// Since `save` only needs read-access to the data we should be able to
1304    /// save while holding a readlock.
1305    #[test]
1306    fn save_holding_readlock() {
1307        let db = TestMemDb::memory(test_data()).expect("Could not create database");
1308        let readlock = db.borrow_data().expect("Rustbreak readlock error");
1309        db.save().expect("Rustbreak save error");
1310        assert_eq!(test_data(), *readlock);
1311    }
1312
1313    /// Test that if the file already exists, the closure won't be called.
1314    #[test]
1315    #[cfg_attr(miri, ignore)]
1316    fn pathdb_from_path_or_else_existing_nocall() {
1317        let file = NamedTempFile::new().expect("could not create temporary file");
1318        let path = file.path().to_owned();
1319        let _ = TestDb::<PathBackend>::load_from_path_or_else(path, || {
1320            panic!("closure called while file existed")
1321        });
1322    }
1323
1324    /// Test that if the file already exists, the closure won't be called.
1325    #[test]
1326    #[cfg_attr(miri, ignore)]
1327    fn filedb_from_path_or_else_existing_nocall() {
1328        let file = NamedTempFile::new().expect("could not create temporary file");
1329        let path = file.path();
1330        let _ = TestDb::<FileBackend>::load_from_path_or_else(path, || {
1331            panic!("closure called while file existed")
1332        });
1333    }
1334
1335    /// Test that if the file already exists, `default` won't be called.
1336    #[test]
1337    #[cfg_attr(miri, ignore)]
1338    fn pathdb_from_path_or_default_existing_nocall() {
1339        let file = NamedTempFile::new().expect("could not create temporary file");
1340        let path = file.path().to_owned();
1341        let _ = Database::<PanicDefault, PathBackend, crate::deser::Ron>::load_from_path_or_default(
1342            path,
1343        );
1344    }
1345
1346    /// Test that if the file already exists, the closure won't be called.
1347    #[test]
1348    #[cfg_attr(miri, ignore)]
1349    fn filedb_from_path_or_default_existing_nocall() {
1350        let file = NamedTempFile::new().expect("could not create temporary file");
1351        let path = file.path();
1352        let _ = Database::<PanicDefault, FileBackend, crate::deser::Ron>::load_from_path_or_default(
1353            path,
1354        );
1355    }
1356
1357    #[test]
1358    #[cfg_attr(miri, ignore)]
1359    fn pathdb_from_path_or_new() {
1360        let dir = tempfile::tempdir().expect("could not create temporary directory");
1361        let mut file_path = dir.path().to_owned();
1362        file_path.push("rustbreak_path_db.db");
1363        let db = TestDb::<PathBackend>::load_from_path_or(file_path, test_data())
1364            .expect("could not load from path");
1365        db.load().expect("could not load");
1366        let readlock = db.borrow_data().expect("Rustbreak readlock error");
1367        assert_eq!(test_data(), *readlock);
1368        dir.close().expect("Error while deleting temp directory!");
1369    }
1370
1371    #[test]
1372    #[cfg_attr(miri, ignore)]
1373    fn pathdb_from_path_or_else_new() {
1374        let dir = tempfile::tempdir().expect("could not create temporary directory");
1375        let mut file_path = dir.path().to_owned();
1376        file_path.push("rustbreak_path_db.db");
1377        let db = TestDb::<PathBackend>::load_from_path_or_else(file_path, test_data)
1378            .expect("could not load from path");
1379        db.load().expect("could not load");
1380        let readlock = db.borrow_data().expect("Rustbreak readlock error");
1381        assert_eq!(test_data(), *readlock);
1382        dir.close().expect("Error while deleting temp directory!");
1383    }
1384
1385    #[test]
1386    #[cfg_attr(miri, ignore)]
1387    fn filedb_from_path_or_new() {
1388        let dir = tempfile::tempdir().expect("could not create temporary directory");
1389        let mut file_path = dir.path().to_owned();
1390        file_path.push("rustbreak_path_db.db");
1391        let db = TestDb::<FileBackend>::load_from_path_or(file_path, test_data())
1392            .expect("could not load from path");
1393        db.load().expect("could not load");
1394        let readlock = db.borrow_data().expect("Rustbreak readlock error");
1395        assert_eq!(test_data(), *readlock);
1396        dir.close().expect("Error while deleting temp directory!");
1397    }
1398
1399    #[test]
1400    #[cfg_attr(miri, ignore)]
1401    fn filedb_from_path_or_else_new() {
1402        let dir = tempfile::tempdir().expect("could not create temporary directory");
1403        let mut file_path = dir.path().to_owned();
1404        file_path.push("rustbreak_path_db.db");
1405        let db = TestDb::<FileBackend>::load_from_path_or_else(file_path, test_data)
1406            .expect("could not load from path");
1407        db.load().expect("could not load");
1408        let readlock = db.borrow_data().expect("Rustbreak readlock error");
1409        assert_eq!(test_data(), *readlock);
1410        dir.close().expect("Error while deleting temp directory!");
1411    }
1412
1413    #[test]
1414    #[cfg_attr(miri, ignore)]
1415    fn pathdb_from_path_new_fail() {
1416        let dir = tempfile::tempdir().expect("could not create temporary directory");
1417        let mut file_path = dir.path().to_owned();
1418        file_path.push("rustbreak_path_db.db");
1419        let err = TestDb::<PathBackend>::load_from_path(file_path)
1420            .expect_err("should fail with file not found");
1421        if let RustbreakError::Backend(BackendError::Io(io_err)) = &err {
1422            assert_eq!(std::io::ErrorKind::NotFound, io_err.kind());
1423        } else {
1424            panic!("Wrong error: {}", err)
1425        };
1426
1427        dir.close().expect("Error while deleting temp directory!");
1428    }
1429
1430    #[test]
1431    #[cfg_attr(miri, ignore)]
1432    fn filedb_from_path_new_fail() {
1433        let dir = tempfile::tempdir().expect("could not create temporary directory");
1434        let mut file_path = dir.path().to_owned();
1435        file_path.push("rustbreak_path_db.db");
1436        let err = TestDb::<FileBackend>::load_from_path(file_path)
1437            .expect_err("should fail with file not found");
1438        if let RustbreakError::Backend(BackendError::Io(io_err)) = &err {
1439            assert_eq!(std::io::ErrorKind::NotFound, io_err.kind());
1440        } else {
1441            panic!("Wrong error: {}", err)
1442        };
1443
1444        dir.close().expect("Error while deleting temp directory!");
1445    }
1446
1447    #[test]
1448    #[cfg_attr(miri, ignore)]
1449    fn pathdb_from_path_existing() {
1450        let file = NamedTempFile::new().expect("could not create temporary file");
1451        let path = file.path().to_owned();
1452        // initialise the file
1453        let db = TestDb::<PathBackend>::create_at_path(path.clone(), test_data())
1454            .expect("could not create db");
1455        db.save().expect("could not save db");
1456        drop(db);
1457        // test that loading now works
1458        let db = TestDb::<PathBackend>::load_from_path(path).expect("could not load");
1459        let readlock = db.borrow_data().expect("Rustbreak readlock error");
1460        assert_eq!(test_data(), *readlock);
1461    }
1462
1463    #[test]
1464    #[cfg_attr(miri, ignore)]
1465    fn filedb_from_path_existing() {
1466        let file = NamedTempFile::new().expect("could not create temporary file");
1467        let path = file.path();
1468        // initialise the file
1469        let db =
1470            TestDb::<FileBackend>::create_at_path(path, test_data()).expect("could not create db");
1471        db.save().expect("could not save db");
1472        drop(db);
1473        // test that loading now works
1474        let db = TestDb::<FileBackend>::load_from_path(path).expect("could not load");
1475        let readlock = db.borrow_data().expect("Rustbreak readlock error");
1476        assert_eq!(test_data(), *readlock);
1477    }
1478}