8 unstable releases (3 breaking)
0.3.1 | Aug 27, 2024 |
---|---|
0.3.0 | Aug 17, 2024 |
0.2.2 | Aug 9, 2024 |
0.1.1 | Jun 21, 2024 |
0.0.1 | Jun 14, 2024 |
#1040 in Database interfaces
178 downloads per month
Used in 2 crates
(via slumber_tui)
30KB
276 lines
persisted
persisted
is a Rust library that makes it easy and quick to save arbitrary program state. Its simple and flexible design means you bring your own data store. You tell persisted
how to save data and what you want to save, and it figures out the rest.
For examples, see the examples/
directory or the documentation.
lib.rs
:
persisted
is a library for persisting arbitrary values in your program so
they can easily be restored later. The main goals of the library are:
- Explicitness: You define exactly what is persisted, and its types
- Ease of use: Thin wrappers make persisting values easy
- Flexible:
persisted
is data store-agnostic; use any persistence scheme you want, including a database, key-value store, etc.
persisted
was designed originally for use in
Slumber, a TUI HTTP client. As such, its
main use case is for persisting values between sessions in a user interface.
It is very flexible though, and could be used for persisting any type of
value in any type of context. no_std
support means it can even be used in
embedded contexts.
Concepts
persisted
serves as a middleman between your values and your data store.
You define your data structures and how your data should be saved,
and persisted
makes sure the data is loaded and stored appropriately. The
key concepts are:
- Data wrappers: [Persisted] and [PersistedLazy]
- These wrap your data to automatically restore and save values from/to the store
- Data store: any implementor of [PersistedStore]
- Key: A unique identifier for a value in the store. Each persisted value must have its own key. Key types must implement [PersistedKey].
How Does It Work?
persisted
works by wrapping each persisted value in either [Persisted] or
[PersistedLazy]. The wrapper is created with a key and optionally a default
value. A request is made to the store to load the most recent value for the
key, and if present that value is used. Whenever the value is modified, the
store is notified of the new value so it can be saved (see either
[Persisted] or [PersistedLazy] for a stricter definition of "is modified").
Because the store is accessed from constructors and destructors, it cannot
be passed around and must be reachable statically. The easiest way to do
this is with either a static
or thread_local
definition of your store.
Example
Here's an example of a very simple persistence scheme. The store keeps just a single value.
use core::cell::Cell;
use persisted::{Persisted, PersistedKey, PersistedStore};
/// Store index of the selected person
#[derive(Default)]
struct Store(Cell<Option<usize>>);
impl Store {
thread_local! {
static INSTANCE: Store = Default::default();
}
}
impl PersistedStore<SelectedIndexKey> for Store {
fn load_persisted(_key: &SelectedIndexKey) -> Option<usize> {
Self::INSTANCE.with(|store| store.0.get())
}
fn store_persisted(_key: &SelectedIndexKey, value: &usize) {
Self::INSTANCE.with(|store| store.0.set(Some(*value)))
}
}
/// Persist the selected value in the list by storing its index. This is simple
/// but relies on the list keeping the same items, in the same order, between
/// sessions.
#[derive(PersistedKey)]
#[persisted(usize)]
struct SelectedIndexKey;
#[derive(Clone, Debug)]
#[allow(unused)]
struct Person {
name: String,
age: u32,
}
/// A list of items, with one item selected
struct SelectList<T> {
values: Vec<T>,
selected_index: Persisted<Store, SelectedIndexKey>,
}
impl<T> SelectList<T> {
fn new(values: Vec<T>) -> Self {
Self {
values,
selected_index: Persisted::new(SelectedIndexKey, 0),
}
}
fn selected(&self) -> &T {
&self.values[*self.selected_index]
}
}
let list = vec![
Person {
name: "Fred".into(),
age: 17,
},
Person {
name: "Susan".into(),
age: 29,
},
Person {
name: "Ulysses".into(),
age: 40,
},
];
let mut people = SelectList::new(list.clone());
*people.selected_index.get_mut() = 1;
println!("Selected: {}", people.selected().name);
// Selected: Susan
let people = SelectList::new(list);
// The previous value was restored
assert_eq!(*people.selected_index, 1);
println!("Selected: {}", people.selected().name);
// Selected: Susan
Feature Flags
persisted
supports the following Cargo features:
derive
(default): Enable derive macrosserde
: EnableSerialize/Deserialize
implementations
Dependencies
~0.4–1MB
~21K SLoC