Skip to main content

Storage

Trait Storage 

Source
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§

Source

fn list(&self, kind: StoreKind, reply: Reply<Vec<String>>)

The names of the stored documents of kind, in any order.

Source

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.

Source

fn save(&self, kind: StoreKind, name: &str, document: String, reply: Reply<()>)

Stores document as name of kind, replacing any document of that name. Answer Ok only once it is kept.

Source

fn delete(&self, kind: StoreKind, name: &str, reply: Reply<()>)

Removes the document name of kind. Removing a document that does not exist succeeds.

Provided Methods§

Source

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.

Source

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.

Source

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".

Implementors§