pub trait Storage: MaybeSend + 'static {
// Required methods
fn list(&self, kind: StoreKind, reply: Reply<Vec<String>>);
fn load(&self, kind: StoreKind, name: &str, reply: Reply<Option<String>>);
fn save(
&self,
kind: StoreKind,
name: &str,
document: String,
reply: Reply<()>,
);
fn delete(&self, kind: StoreKind, name: &str, reply: Reply<()>);
// Provided methods
fn load_versioned(
&self,
kind: StoreKind,
name: &str,
reply: Reply<Option<Versioned>>,
) { ... }
fn save_versioned(
&self,
kind: StoreKind,
name: &str,
document: String,
expected: Option<Revision>,
reply: Reply<SaveOutcome>,
) { ... }
fn watch(&self, kind: StoreKind, name: &str, events: Events<Revision>) { ... }
}Expand description
Where the library keeps settings and drawings between sessions: files, browser storage or a server, as the host chooses. The library reads and writes nothing on its own.
Documents are JSON text with a version field that the library writes and migrates. Store them
verbatim and hand them back unchanged. Names come from the library (a drawings document is named
by its symbol) and may contain any character, / and .. included: a host that keeps documents
as files must encode the name rather than use it as a path, and list returns the names as they
were saved.
These methods are called on the UI thread and must return at once. Do the work elsewhere and
answer through the Reply you were given, from any thread, runtime or browser task. A failed
save keeps the state in memory and is reported to the user once, so answer with an error
rather than dropping a document. The library waits for the answer to one call before it makes
another for the same document, so different calls may be worked on concurrently. Renaming is a
save under the new name and, once that succeeds, a delete of the old one.
A host shared by several clients can also keep revisions, with
load_versioned, save_versioned and
watch. The library then writes a document only over the revision it read,
so a client never overwrites a newer version unnoticed: a refused save keeps the unsaved
document in memory and is settled with a ConflictChoice. A host that answers
load_versioned with FeedError::NotSupported, as the default does, keeps the plain
behaviour above. Switching accounts means a new Storage; the library drops the old one’s
pending answers and streams, so nothing of the old account reaches the new one.
use std::collections::HashMap;
use std::sync::Mutex;
use egui_charts::data::Reply;
use egui_charts::services::{Storage, StoreKind};
/// Documents held in memory, lost when the application exits. Answers inline.
#[derive(Default)]
struct InMemory(Mutex<HashMap<(StoreKind, String), String>>);
impl Storage for InMemory {
fn list(&self, kind: StoreKind, reply: Reply<Vec<String>>) {
let documents = self.0.lock().unwrap();
let names = documents
.keys()
.filter(|(k, _)| *k == kind)
.map(|(_, name)| name.clone())
.collect();
drop(documents);
reply.send(Ok(names));
}
fn load(&self, kind: StoreKind, name: &str, reply: Reply<Option<String>>) {
let document = self.0.lock().unwrap().get(&(kind, name.to_owned())).cloned();
reply.send(Ok(document));
}
fn save(&self, kind: StoreKind, name: &str, document: String, reply: Reply<()>) {
self.0.lock().unwrap().insert((kind, name.to_owned()), document);
reply.send(Ok(()));
}
fn delete(&self, kind: StoreKind, name: &str, reply: Reply<()>) {
self.0.lock().unwrap().remove(&(kind, name.to_owned()));
reply.send(Ok(()));
}
}
let storage: std::sync::Arc<dyn Storage> = std::sync::Arc::new(InMemory::default());Required Methods§
Sourcefn list(&self, kind: StoreKind, reply: Reply<Vec<String>>)
fn list(&self, kind: StoreKind, reply: Reply<Vec<String>>)
The names of the stored documents of kind, in any order.
Sourcefn load(&self, kind: StoreKind, name: &str, reply: Reply<Option<String>>)
fn load(&self, kind: StoreKind, name: &str, reply: Reply<Option<String>>)
The document name of kind. Answer Ok(None) only when you know there is no such
document, and an error whenever it cannot be read, including when you cannot tell whether
it exists, so the library never saves over it.
Provided Methods§
Sourcefn load_versioned(
&self,
kind: StoreKind,
name: &str,
reply: Reply<Option<Versioned>>,
)
fn load_versioned( &self, kind: StoreKind, name: &str, reply: Reply<Option<Versioned>>, )
Like load, with the revision of the document. Answer
FeedError::NotSupported if the host keeps no revisions; the library then uses load
and save for that document. By default every call is refused so.
Sourcefn save_versioned(
&self,
kind: StoreKind,
name: &str,
document: String,
expected: Option<Revision>,
reply: Reply<SaveOutcome>,
)
fn save_versioned( &self, kind: StoreKind, name: &str, document: String, expected: Option<Revision>, reply: Reply<SaveOutcome>, )
Stores document only if the stored one is at revision expected, or does not exist when
expected is None. Answer SaveOutcome::Saved with the new revision once it is kept.
If the stored document differs, change nothing and answer SaveOutcome::Conflict with
the stored version. The check and the write must be one atomic step.
Sourcefn watch(&self, kind: StoreKind, name: &str, events: Events<Revision>)
fn watch(&self, kind: StoreKind, name: &str, events: Events<Revision>)
Pushes the new revision each time another client changes the document name of kind,
until events is inactive. Writes of this client may be pushed too; the library ignores
the revisions it knows. Called right after the library starts reading a document that
follows remote changes, so a change made while it is read is not missed, and by default
pushes nothing.
Dyn Compatibility§
This trait is dyn compatible.
In older versions of Rust, dyn compatibility was called "object safety".