pub struct Database<Data, Back, DeSer> { /* private fields */ }
Expand description
The Central Database to Rustbreak.
It has 3 Type Generics:
Data
: Is the Data, you must specify thisBack
: The storage backend.DeSer
: The Serializer/Deserializer or shortDeSer
. Check thedeser
module for other strategies.
Panics
If the backend or the de/serialization panics, the database is poisoned.
This means that any subsequent writes/reads will fail with an
error::RustbreakError::Poison
. You can only recover from this by
re-creating the Database Object.
Implementations
sourceimpl<Data, Back, DeSer> Database<Data, Back, DeSer> where
Data: Serialize + DeserializeOwned + Clone + Send,
Back: Backend,
DeSer: DeSerializer<Data> + Send + Sync + Clone,
impl<Data, Back, DeSer> Database<Data, Back, DeSer> where
Data: Serialize + DeserializeOwned + Clone + Send,
Back: Backend,
DeSer: DeSerializer<Data> + Send + Sync + Clone,
sourcepub fn write<T, R>(&self, task: T) -> Result<R> where
T: FnOnce(&mut Data) -> R,
pub fn write<T, R>(&self, task: T) -> Result<R> where
T: FnOnce(&mut Data) -> R,
Write lock the database and get write access to the Data
container.
This gives you an exclusive lock on the memory object. Trying to open the database in writing will block if it is currently being written to.
Panics
If you panic in the closure, the database is poisoned. This means that
any subsequent writes/reads will fail with an
error::RustbreakError::Poison
. You can only recover from
this by re-creating the Database Object.
If you do not have full control over the code being written, and cannot
incur the cost of having a single operation panicking then use
Database::write_safe
.
Examples
use rustbreak::{deser::Ron, FileDatabase};
#[derive(Debug, Serialize, Deserialize, Clone)]
struct Data {
level: u32,
}
let db = FileDatabase::<Data, Ron>::from_file(file, Data { level: 0 })?;
db.write(|db| {
db.level = 42;
})?;
// You can also return from a `.read()`. But don't forget that you cannot return references
// into the structure
let value = db.read(|db| db.level)?;
assert_eq!(42, value);
sourcepub fn write_safe<T>(&self, task: T) -> Result<()> where
T: FnOnce(&mut Data) + UnwindSafe,
pub fn write_safe<T>(&self, task: T) -> Result<()> where
T: FnOnce(&mut Data) + UnwindSafe,
Write lock the database and get write access to the Data
container in
a safe way.
This gives you an exclusive lock on the memory object. Trying to open the database in writing will block if it is currently being written to.
This differs to Database::write
in that a clone of the internal data
is made, which is then passed to the closure. Only if the closure
doesn’t panic is the internal model updated.
Depending on the size of the database this can be very costly. This is a tradeoff to make for panic safety.
You should read the documentation about this:
UnwindSafe
Panics
When the closure panics, it is caught and a
error::RustbreakError::WritePanic
will be returned.
Examples
use rustbreak::{
deser::Ron,
error::RustbreakError,
FileDatabase,
};
#[derive(Debug, Serialize, Deserialize, Clone)]
struct Data {
level: u32,
}
let db = FileDatabase::<Data, Ron>::from_file(file, Data { level: 0 })?;
let result = db
.write_safe(|db| {
db.level = 42;
panic!("We panic inside the write code.");
})
.expect_err("This should have been caught");
match result {
RustbreakError::WritePanic => {
// We can now handle this, in this example we will just ignore it
}
e => {
println!("{:#?}", e);
// You should always have generic error catching here.
// This future-proofs your code, and makes your code more robust.
// In this example this is unreachable though, and to assert that we have this
// macro here
unreachable!();
}
}
// We read it back out again, it has not changed
let value = db.read(|db| db.level)?;
assert_eq!(0, value);
sourcepub fn read<T, R>(&self, task: T) -> Result<R> where
T: FnOnce(&Data) -> R,
pub fn read<T, R>(&self, task: T) -> Result<R> where
T: FnOnce(&Data) -> R,
Read lock the database and get read access to the Data
container.
This gives you a read-only lock on the database. You can have as many readers in parallel as you wish.
Errors
May return:
Panics
If you panic in the closure, the database is poisoned. This means that
any subsequent writes/reads will fail with an
error::RustbreakError::Poison
. You can only recover from
this by re-creating the Database Object.
sourcepub fn borrow_data<'a>(&'a self) -> Result<RwLockReadGuard<'a, Data>>
pub fn borrow_data<'a>(&'a self) -> Result<RwLockReadGuard<'a, Data>>
Read lock the database and get access to the underlying struct.
This gives you access to the underlying struct, allowing for simple read only operations on it.
Examples
use rustbreak::{deser::Ron, FileDatabase};
#[derive(Debug, Serialize, Deserialize, Clone)]
struct Data {
level: u32,
}
let db = FileDatabase::<Data, Ron>::from_file(file, Data { level: 0 })?;
db.write(|db| {
db.level = 42;
})?;
let data = db.borrow_data()?;
assert_eq!(42, data.level);
sourcepub fn borrow_data_mut<'a>(&'a self) -> Result<RwLockWriteGuard<'a, Data>>
pub fn borrow_data_mut<'a>(&'a self) -> Result<RwLockWriteGuard<'a, Data>>
Write lock the database and get access to the underlying struct.
This gives you access to the underlying struct, allowing you to modify it.
Panics
If you panic while holding this reference, the database is poisoned.
This means that any subsequent writes/reads will fail with an
error::RustbreakError::Poison
. You can only recover from
this by re-creating the Database Object.
If you do not have full control over the code being written, and cannot
incur the cost of having a single operation panicking then use
Database::write_safe
.
Examples
use rustbreak::{deser::Ron, FileDatabase};
#[derive(Debug, Serialize, Deserialize, Clone)]
struct Data {
level: u32,
}
let db = FileDatabase::<Data, Ron>::from_file(file, Data { level: 0 })?;
{
let mut data = db.borrow_data_mut()?;
data.level = 42;
}
let data = db.borrow_data()?;
assert_eq!(42, data.level);
sourcepub fn get_data(&self, load: bool) -> Result<Data>
pub fn get_data(&self, load: bool) -> Result<Data>
Get a clone of the data as it is in memory right now.
To make sure you have the latest data, call this method with load
true.
sourcepub fn put_data(&self, new_data: Data, save: bool) -> Result<()>
pub fn put_data(&self, new_data: Data, save: bool) -> Result<()>
Puts the data as is into memory.
To save the data afterwards, call with save
true.
sourcepub fn from_parts(data: Data, backend: Back, deser: DeSer) -> Self
pub fn from_parts(data: Data, backend: Back, deser: DeSer) -> Self
Create a database from its constituents.
sourcepub fn into_inner(self) -> Result<(Data, Back, DeSer)>
pub fn into_inner(self) -> Result<(Data, Back, DeSer)>
Break a database into its individual parts.
sourcepub fn try_clone(&self) -> Result<MemoryDatabase<Data, DeSer>>
pub fn try_clone(&self) -> Result<MemoryDatabase<Data, DeSer>>
Tries to clone the Data in the Database.
This method returns a MemoryDatabase
which has an empty vector as a
backend initially. This means that the user is responsible for assigning
a new backend if an alternative is wanted.
Examples
use rustbreak::{deser::Ron, FileDatabase};
#[derive(Debug, Serialize, Deserialize, Clone)]
struct Data {
level: u32,
}
let db = FileDatabase::<Data, Ron>::from_file(file, Data { level: 0 })?;
db.write(|db| {
db.level = 42;
})?;
db.save()?;
let other_db = db.try_clone()?;
// You can also return from a `.read()`. But don't forget that you cannot return references
// into the structure
let value = other_db.read(|db| db.level)?;
assert_eq!(42, value);
sourceimpl<Data, DeSer> Database<Data, FileBackend, DeSer> where
Data: Serialize + DeserializeOwned + Clone + Send,
DeSer: DeSerializer<Data> + Send + Sync + Clone,
impl<Data, DeSer> Database<Data, FileBackend, DeSer> where
Data: Serialize + DeserializeOwned + Clone + Send,
DeSer: DeSerializer<Data> + Send + Sync + Clone,
sourcepub fn load_from_path<S>(path: S) -> Result<Self> where
S: AsRef<Path>,
pub fn load_from_path<S>(path: S) -> Result<Self> where
S: AsRef<Path>,
Create new FileDatabase
from the file at Path
,
and load the contents.
sourcepub fn load_from_path_or<S>(path: S, data: Data) -> Result<Self> where
S: AsRef<Path>,
pub fn load_from_path_or<S>(path: S, data: Data) -> Result<Self> where
S: AsRef<Path>,
Load FileDatabase
at path
or initialise with data
.
Create new FileDatabase
from the file at Path
,
and load the contents. If the file does not exist, initialise with
data
.
sourcepub fn load_from_path_or_else<S, C>(path: S, closure: C) -> Result<Self> where
S: AsRef<Path>,
C: FnOnce() -> Data,
pub fn load_from_path_or_else<S, C>(path: S, closure: C) -> Result<Self> where
S: AsRef<Path>,
C: FnOnce() -> Data,
Load FileDatabase
at path
or initialise with closure
.
Create new FileDatabase
from the file at Path
,
and load the contents. If the file does not exist, closure
is
called and the database is initialised with it’s return value.
sourcepub fn create_at_path<S>(path: S, data: Data) -> Result<Self> where
S: AsRef<Path>,
pub fn create_at_path<S>(path: S, data: Data) -> Result<Self> where
S: AsRef<Path>,
Create FileDatabase
at path
. Initialise with data
if the file
doesn’t exist.
Create new FileDatabase
from the file at Path
.
Contents are not loaded. If the file does not exist, it is
initialised with data
. Frontend is always initialised with data
.
sourcepub fn from_file(file: File, data: Data) -> Result<Self>
pub fn from_file(file: File, data: Data) -> Result<Self>
Create new FileDatabase
from a file.
sourceimpl<Data, DeSer> Database<Data, FileBackend, DeSer> where
Data: Serialize + DeserializeOwned + Clone + Send + Default,
DeSer: DeSerializer<Data> + Send + Sync + Clone,
impl<Data, DeSer> Database<Data, FileBackend, DeSer> where
Data: Serialize + DeserializeOwned + Clone + Send + Default,
DeSer: DeSerializer<Data> + Send + Sync + Clone,
sourcepub fn load_from_path_or_default<S>(path: S) -> Result<Self> where
S: AsRef<Path>,
pub fn load_from_path_or_default<S>(path: S) -> Result<Self> where
S: AsRef<Path>,
Load FileDatabase
at path
or initialise with Data::default()
.
Create new FileDatabase
from the file at Path
,
and load the contents. If the file does not exist, initialise with
Data::default
.
sourceimpl<Data, DeSer> Database<Data, PathBackend, DeSer> where
Data: Serialize + DeserializeOwned + Clone + Send,
DeSer: DeSerializer<Data> + Send + Sync + Clone,
impl<Data, DeSer> Database<Data, PathBackend, DeSer> where
Data: Serialize + DeserializeOwned + Clone + Send,
DeSer: DeSerializer<Data> + Send + Sync + Clone,
sourcepub fn load_from_path(path: PathBuf) -> Result<Self>
pub fn load_from_path(path: PathBuf) -> Result<Self>
Create new PathDatabase
from the file at Path
,
and load the contents.
sourcepub fn load_from_path_or(path: PathBuf, data: Data) -> Result<Self>
pub fn load_from_path_or(path: PathBuf, data: Data) -> Result<Self>
Load PathDatabase
at path
or initialise with data
.
Create new PathDatabase
from the file at Path
,
and load the contents. If the file does not exist, initialise with
data
.
sourcepub fn load_from_path_or_else<C>(path: PathBuf, closure: C) -> Result<Self> where
C: FnOnce() -> Data,
pub fn load_from_path_or_else<C>(path: PathBuf, closure: C) -> Result<Self> where
C: FnOnce() -> Data,
Load PathDatabase
at path
or initialise with closure
.
Create new PathDatabase
from the file at Path
,
and load the contents. If the file does not exist, closure
is
called and the database is initialised with it’s return value.
sourcepub fn create_at_path(path: PathBuf, data: Data) -> Result<Self>
pub fn create_at_path(path: PathBuf, data: Data) -> Result<Self>
Create PathDatabase
at path
. Initialise with data
if the file
doesn’t exist.
Create new PathDatabase
from the file at Path
.
Contents are not loaded. If the file does not exist, it is
initialised with data
. Frontend is always initialised with data
.
sourceimpl<Data, DeSer> Database<Data, PathBackend, DeSer> where
Data: Serialize + DeserializeOwned + Clone + Send + Default,
DeSer: DeSerializer<Data> + Send + Sync + Clone,
impl<Data, DeSer> Database<Data, PathBackend, DeSer> where
Data: Serialize + DeserializeOwned + Clone + Send + Default,
DeSer: DeSerializer<Data> + Send + Sync + Clone,
sourcepub fn load_from_path_or_default(path: PathBuf) -> Result<Self>
pub fn load_from_path_or_default(path: PathBuf) -> Result<Self>
Load PathDatabase
at path
or initialise with Data::default()
.
Create new PathDatabase
from the file at Path
,
and load the contents. If the file does not exist, initialise with
Data::default
.
sourceimpl<Data, DeSer> Database<Data, MemoryBackend, DeSer> where
Data: Serialize + DeserializeOwned + Clone + Send,
DeSer: DeSerializer<Data> + Send + Sync + Clone,
impl<Data, DeSer> Database<Data, MemoryBackend, DeSer> where
Data: Serialize + DeserializeOwned + Clone + Send,
DeSer: DeSerializer<Data> + Send + Sync + Clone,
sourceimpl<Data, Back, DeSer> Database<Data, Back, DeSer>
impl<Data, Back, DeSer> Database<Data, Back, DeSer>
sourcepub fn with_deser<T>(self, deser: T) -> Database<Data, Back, T>
pub fn with_deser<T>(self, deser: T) -> Database<Data, Back, T>
Exchanges the DeSerialization
strategy with the new one.
sourceimpl<Data, Back, DeSer> Database<Data, Back, DeSer>
impl<Data, Back, DeSer> Database<Data, Back, DeSer>
sourcepub fn with_backend<T>(self, backend: T) -> Database<Data, T, DeSer>
pub fn with_backend<T>(self, backend: T) -> Database<Data, T, DeSer>
Exchanges the Backend
with the new one.
The new backend does not necessarily have the latest data saved to it,
so a .save
should be called to make sure that it is saved.
sourceimpl<Data, Back, DeSer> Database<Data, Back, DeSer> where
Data: Serialize + DeserializeOwned + Clone + Send,
Back: Backend,
DeSer: DeSerializer<Data> + Send + Sync + Clone,
impl<Data, Back, DeSer> Database<Data, Back, DeSer> where
Data: Serialize + DeserializeOwned + Clone + Send,
Back: Backend,
DeSer: DeSerializer<Data> + Send + Sync + Clone,
sourcepub fn convert_data<C, OutputData>(
self,
convert: C
) -> Result<Database<OutputData, Back, DeSer>> where
OutputData: Serialize + DeserializeOwned + Clone + Send,
C: FnOnce(Data) -> OutputData,
DeSer: DeSerializer<OutputData> + Send + Sync,
pub fn convert_data<C, OutputData>(
self,
convert: C
) -> Result<Database<OutputData, Back, DeSer>> where
OutputData: Serialize + DeserializeOwned + Clone + Send,
C: FnOnce(Data) -> OutputData,
DeSer: DeSerializer<OutputData> + Send + Sync,
Converts from one data type to another.
This method is useful to migrate from one datatype to another.
Trait Implementations
Auto Trait Implementations
impl<Data, Back, DeSer> RefUnwindSafe for Database<Data, Back, DeSer> where
DeSer: RefUnwindSafe,
impl<Data, Back, DeSer> Send for Database<Data, Back, DeSer> where
Back: Send,
Data: Send,
DeSer: Send,
impl<Data, Back, DeSer> Sync for Database<Data, Back, DeSer> where
Back: Send,
Data: Send + Sync,
DeSer: Sync,
impl<Data, Back, DeSer> Unpin for Database<Data, Back, DeSer> where
Back: Unpin,
Data: Unpin,
DeSer: Unpin,
impl<Data, Back, DeSer> UnwindSafe for Database<Data, Back, DeSer> where
DeSer: UnwindSafe,
Blanket Implementations
sourceimpl<T> BorrowMut<T> for T where
T: ?Sized,
impl<T> BorrowMut<T> for T where
T: ?Sized,
const: unstable · sourcefn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
Mutably borrows from an owned value. Read more