pub trait ModelStore {
Show 54 methods
// Required methods
fn read(&self, name: &str) -> Option<String>;
fn write(&self, name: &str, contents: &str) -> Result<(), String>;
fn mutation_generation(&self) -> u64;
// Provided methods
fn files(&self, _request: &Request) -> Result<Reply, String> { ... }
fn files_settled(&self, _request: &Request) -> Option<Result<(), String>> { ... }
fn backend_label(&self) -> String { ... }
fn list(&self) -> Vec<String> { ... }
fn remove(&self, _name: &str) -> Result<(), String> { ... }
fn residency(&self, name: &str) -> Residency { ... }
fn identity(&self, identity: &str) -> DocumentIdentity { ... }
fn display_name(&self, identity: &str) -> String { ... }
fn sibling_of(&self, identity: &str, file_name: &str) -> Option<String> { ... }
fn canonical_identity(&self, name: &str) -> String { ... }
fn canonicalizer(&self) -> Canonicalizer { ... }
fn take_persistence_errors(&self) -> Vec<String> { ... }
fn take_failed_writes(&self) -> Vec<String> { ... }
fn write_failed(&self, name: &str) -> bool { ... }
fn failed_write_base(&self, name: &str) -> Option<String> { ... }
fn write_if(
&self,
name: &str,
contents: &str,
expected: &str,
) -> Result<(), String> { ... }
fn plm_session(&self) -> Option<Session> { ... }
fn plm_url_setting(&self) -> Option<UrlSetting> { ... }
fn save_plm_url(&self, url: &str) -> Result<UrlSaved, String> { ... }
fn plm_client(&self) -> Option<Rc<PlmClient>> { ... }
fn plm_revision(&self, key: &str) -> Option<IndexEntry> { ... }
fn note_checked_out(&self, key: &str) { ... }
fn plm_written_by_us(&self, key: &str) -> bool { ... }
fn refresh_plm_index(&self, keys: &[String], stale: bool) { ... }
fn remember_saved_document(&self, name: &str, contents: &str) { ... }
fn local_files(&self) -> Option<&dyn ModelStore> { ... }
fn begin_pick_file(&self) -> Result<(), String> { ... }
fn take_picked_file(&self) -> Option<ImportedFile> { ... }
fn pending_writes(&self) -> usize { ... }
fn pending_saves(&self) -> usize { ... }
fn pending_writes_except(&self, names: &[&str]) -> usize { ... }
fn browser_location(&self) -> String { ... }
fn browser_entries(&self, _extensions: &[&str]) -> Vec<BrowserEntry> { ... }
fn browser_enter(&self, _identity: &str) -> Result<(), String> { ... }
fn browser_up(&self) -> Result<(), String> { ... }
fn browser_home(&self) -> Result<(), String> { ... }
fn browser_root(&self) -> Result<(), String> { ... }
fn browser_navigate(&self, _location: &str) -> Result<(), String> { ... }
fn browser_places(&self) -> Vec<BrowserPlace> { ... }
fn browser_create_dir(&self, _name: &str) -> Result<(), String> { ... }
fn browser_write(
&self,
name: &str,
contents: &str,
) -> Result<String, String> { ... }
fn browser_write_bytes(
&self,
_file_name: &str,
_contents: &[u8],
) -> Result<String, String> { ... }
fn list_external_files(&self, _extensions: &[&str]) -> Vec<String> { ... }
fn read_external_file(&self, _name: &str) -> Option<Vec<u8>> { ... }
fn supports_file_interchange(&self) -> bool { ... }
fn export_file(
&self,
name: &str,
contents: &str,
) -> Result<Option<String>, String> { ... }
fn begin_import(&self) -> Result<(), String> { ... }
fn take_import(&self) -> Option<ImportedFile> { ... }
fn begin_import_filtered(
&self,
_filter: (&str, &[&str]),
) -> Result<(), String> { ... }
fn export_file_named(
&self,
_file_name: &str,
_contents: &str,
) -> Result<(), String> { ... }
fn export_file_named_bytes(
&self,
_file_name: &str,
_contents: &[u8],
) -> Result<(), String> { ... }
}Expand description
The application’s single persistent-storage abstraction. Reserved application
keys and named model documents are read and written through the same methods.
All methods take &self; mutable backend state lives behind interior mutability.
Required Methods§
Sourcefn read(&self, name: &str) -> Option<String>
fn read(&self, name: &str) -> Option<String>
Read a stored document by name, or None if absent/unreadable.
Sourcefn write(&self, name: &str, contents: &str) -> Result<(), String>
fn write(&self, name: &str, contents: &str) -> Result<(), String>
Create or overwrite the document name with contents. Err carries a
message the panel surfaces in its status line.
Sourcefn mutation_generation(&self) -> u64
fn mutation_generation(&self) -> u64
A monotonic count of this store’s MUTATIONS — every write, remove
and browser_write that landed, WHOEVER made it. A read never moves
it; two reads that agree mean nothing was written in between.
This is a cache-invalidation key, which is why it is REQUIRED rather
than defaulted: a default would let a new backend (the GitHub one this
trait is shaped for) compile while silently never invalidating
anything, which is the failure it exists to stop. The outdated-parts
badge (panels::update_components) used to key on the file dialog’s
own save counter, which counts Save / Save As / native Save-As and
NOTHING else — so a KiCad re-import (panels::kicad_bulk::write_part)
or the explorer’s delete row could change a part’s source under an open
assembly, move no other generation, and leave the badge dark.
Provided Methods§
Sourcefn files(&self, _request: &Request) -> Result<Reply, String>
fn files(&self, _request: &Request) -> Result<Reply, String>
Bounded file management over this host’s persistent file store.
Sourcefn files_settled(&self, _request: &Request) -> Option<Result<(), String>>
fn files_settled(&self, _request: &Request) -> Option<Result<(), String>>
None while persistence is in flight; otherwise the operation’s result.
Sourcefn backend_label(&self) -> String
fn backend_label(&self) -> String
A short human label of where documents persist, for the panel header
(e.g. "filesystem: ~/.config/brep-app/models" or "browser storage").
Sourcefn list(&self) -> Vec<String>
fn list(&self) -> Vec<String>
The names of the documents currently available to Open (bare names, no extension). May be empty on a backend that cannot enumerate — then the panel falls back to the name field / import.
Sourcefn remove(&self, _name: &str) -> Result<(), String>
fn remove(&self, _name: &str) -> Result<(), String>
Delete the document name (best-effort; Ok if it is already gone).
Sourcefn residency(&self, name: &str) -> Residency
fn residency(&self, name: &str) -> Residency
Whether name is absent, listed but not yet loaded, or readable now
(see Residency). The default answers from read, which is exact for
every store that holds what it lists; only an index-hydrated store
overrides it. No caller branches on OnDisk yet — it is here so one
CAN tell “not loaded yet” from “gone” without a second store API.
Sourcefn identity(&self, identity: &str) -> DocumentIdentity
fn identity(&self, identity: &str) -> DocumentIdentity
What kind of identity identity is on THIS store (see
DocumentIdentity and the enumeration above it). Every file backend
keys by path, so the default never parses: a browser folder named
part/7/rev holds a document, not a revision.
Sourcefn display_name(&self, identity: &str) -> String
fn display_name(&self, identity: &str) -> String
The name a person sees for identity: the tab title, the name field.
The default is the file rule, model_display_name; a PLM store knows
the part number and revision label from its index, which the key alone
does not carry.
Sourcefn sibling_of(&self, identity: &str, file_name: &str) -> Option<String>
fn sibling_of(&self, identity: &str, file_name: &str) -> Option<String>
The identity of file_name beside the document identity — or None
on a store with no folders to be beside (the PLM, where a family member
or a spun-out part is a new part instead, S7 / S9). The default is the
file rule, sibling_identity.
Sourcefn canonical_identity(&self, name: &str) -> String
fn canonical_identity(&self, name: &str) -> String
The ONE spelling of the document name resolves to, as the explorer
spells it (BrowserEntry::identity), so two names for one document
compare equal: a bare bolt and /home/u/.config/brep-app/models/bolt.nbrep
on the desktop, sub/bolt and /models/sub/bolt.nbrep in the browser.
The adoption importer (S13) needs it. An assembly’s sourceKeys are
whatever name the part was inserted by, and the importer must match
each one to the file it walked. The default is name unchanged, which
is right for a store with one spelling per document (a PLM store’s
revision keys).
Sourcefn canonicalizer(&self) -> Canonicalizer
fn canonicalizer(&self) -> Canonicalizer
Self::canonical_identity as a function that outlives the borrow:
what the open documents compare identities with (a tab is named by the
explorer’s spelling, a sourceKey by whatever the part was inserted
by — crate::document::live_link).
Sourcefn take_persistence_errors(&self) -> Vec<String>
fn take_persistence_errors(&self) -> Vec<String>
Persistence failures that surfaced AFTER a write / remove already
returned Ok — the price of a WRITE-BEHIND backend (see
mirror_store). The app shell drains this once per frame into the toast
overlay, so a save that never reached storage is never silent: the
in-memory copy means nothing is lost mid-session, but the user has to know
it will not survive a reload. Empty on a backend that writes synchronously
(native files), which reports through write’s Err instead.
Sourcefn take_failed_writes(&self) -> Vec<String>
fn take_failed_writes(&self) -> Vec<String>
The documents (as named to write) whose write failed AFTER write
returned Ok, each once. The frame loop marks their tabs unsaved again:
a Save that never reached the server must not leave a clean tab, or
closing it loses the edit without a question. Empty on a backend that
writes synchronously, whose write returns the Err itself.
Sourcefn write_failed(&self, name: &str) -> bool
fn write_failed(&self, name: &str) -> bool
The last write of name failed after it returned: what Self::read
gives is this session’s, not the backend’s. A Save that would skip the
write because the store “already holds” the text must write it.
Sourcefn failed_write_base(&self, name: &str) -> Option<String>
fn failed_write_base(&self, name: &str) -> Option<String>
For a Self::write_failed key: the content hash of the document the
failed write was based on (empty: unknown).
Sourcefn write_if(
&self,
name: &str,
contents: &str,
expected: &str,
) -> Result<(), String>
fn write_if( &self, name: &str, contents: &str, expected: &str, ) -> Result<(), String>
Self::write, but only over the document whose content hash is
expected (a PLM revision’s If-Match): a backend holding a newer one
refuses it, and the failure says so. A store with no such check writes.
Sourcefn plm_session(&self) -> Option<Session>
fn plm_session(&self) -> Option<Session>
The PLM this session is on, or is configured for and not on (why, when
known): what the Settings PLM tab shows (plan S1 part 3). None is the
file-based app, and the tab is absent.
Sourcefn plm_url_setting(&self) -> Option<UrlSetting>
fn plm_url_setting(&self) -> Option<UrlSetting>
Where this session’s PLM server URL is kept: the Settings PLM tab’s
Server URL field reads it, in every native session (file or PLM).
None where no plm.json exists to write — the browser, whose PLM is
the server that serves its page, and the in-memory stores.
Sourcefn save_plm_url(&self, url: &str) -> Result<UrlSaved, String>
fn save_plm_url(&self, url: &str) -> Result<UrlSaved, String>
Save the Server URL to plm.json as --plm-configure does
(crate::plm::config::repoint: a malformed URL is refused, and a
different server drops this machine’s token). A file session that was
on plm.json reports the new server from here on (signed out), so the
tab’s sign-in reaches it at once; a PLM session stays on its server
until the next start.
Sourcefn plm_client(&self) -> Option<Rc<PlmClient>>
fn plm_client(&self) -> Option<Rc<PlmClient>>
The signed-in PLM client of a session whose store IS the PLM, for the
calls that are not store reads and writes (Generate, spin-out, the
lifecycle verbs). None on every file store, and on a PLM configured
but not connected.
Sourcefn plm_revision(&self, key: &str) -> Option<IndexEntry>
fn plm_revision(&self, key: &str) -> Option<IndexEntry>
What the PLM’s store index says of revision key (part/…/rev/…):
its part number, label, lifecycle and lock, as the session last read
them. None on every file store and for a key the index does not list.
No request: this is the index the session already holds.
Sourcefn note_checked_out(&self, key: &str)
fn note_checked_out(&self, key: &str)
This session’s checkout of the PLM revision key just landed: its
index row reads it as checked out to us now, before the change feed
says so (a Save planned meanwhile must not ask to check it out again).
A no-op on every file store.
Sourcefn plm_written_by_us(&self, key: &str) -> bool
fn plm_written_by_us(&self, key: &str) -> bool
Whether the index row of the PLM revision key lists our own last
write of it, with nothing newer listed since: what the change feed
names for it is our own save, which a reader need not read back.
false on every file store.
Sourcefn refresh_plm_index(&self, keys: &[String], stale: bool)
fn refresh_plm_index(&self, keys: &[String], stale: bool)
Bring the held index up to date after the change feed moved: keys
are the revisions the feed named, stale means it could not say. A
no-op on every file store.
Sourcefn remember_saved_document(&self, name: &str, contents: &str)
fn remember_saved_document(&self, name: &str, contents: &str)
Remember a document that has already been durably written through a PLM request. Update the session mirror without issuing a second write.
Sourcefn local_files(&self) -> Option<&dyn ModelStore>
fn local_files(&self) -> Option<&dyn ModelStore>
This machine’s own files, browsable, for the file chooser (any file,
any folder): the native file store itself, or the file store a PLM
session keeps beside the server. None in the browser, which picks
through Self::begin_pick_file instead.
Sourcefn begin_pick_file(&self) -> Result<(), String>
fn begin_pick_file(&self) -> Result<(), String>
The browser’s file chooser for ANY file (not the model lane’s
.nbrep input): the choice arrives later through
Self::take_picked_file, under the file’s own name.
Sourcefn take_picked_file(&self) -> Option<ImportedFile>
fn take_picked_file(&self) -> Option<ImportedFile>
The file Self::begin_pick_file’s chooser delivered, once.
Sourcefn pending_writes(&self) -> usize
fn pending_writes(&self) -> usize
Writes handed to a WRITE-BEHIND backend that have not settled yet, the desktop’s own state files included: the close path and exit wait for all of them. Always 0 on a backend that writes synchronously.
Sourcefn pending_saves(&self) -> usize
fn pending_saves(&self) -> usize
The part of Self::pending_writes the status bar’s working
indicator names as saving (“Saving to browser storage”): documents and
state on their way to a backend. The desktop’s own settings, layout,
recents and recovery files written behind are not a save the user
would recognise, and are left out. Also what a flow waiting for its
save to reach the server (Generate, a PLM follow) counts.
Sourcefn pending_writes_except(&self, names: &[&str]) -> usize
fn pending_writes_except(&self, names: &[&str]) -> usize
Self::pending_writes, leaving out the writes of the documents
stored under names: what settle (MCP store_writes) on one
document waits for while other tabs save. A store that cannot tell its
writes apart counts all. The desktop’s write-behind state files are
never counted here: everything in the process reads them through the
lane, so no settled state depends on their fsync.
Sourcefn browser_location(&self) -> String
fn browser_location(&self) -> String
Current directory shown by the embedded explorer.
Sourcefn browser_entries(&self, _extensions: &[&str]) -> Vec<BrowserEntry>
fn browser_entries(&self, _extensions: &[&str]) -> Vec<BrowserEntry>
Directories and matching files at the current explorer location. Directories are never filtered. The default implementation presents the backend’s named model collection as a flat virtual directory.
fn browser_enter(&self, _identity: &str) -> Result<(), String>
fn browser_up(&self) -> Result<(), String>
fn browser_home(&self) -> Result<(), String>
fn browser_root(&self) -> Result<(), String>
Navigate the explorer directly to location — a value previously returned
by Self::browser_location, a BrowserPlace::location, a breadcrumb
ancestor, or a user-typed path. Err if it is not a directory this backend
can browse. Powers the breadcrumb, the sidebar, back/forward, and the
path-edit field. Default: unsupported.
Sourcefn browser_places(&self) -> Vec<BrowserPlace>
fn browser_places(&self) -> Vec<BrowserPlace>
Quick-access places for the explorer’s left sidebar (home, documents, the models root, disks…). Default: none, and the sidebar hides itself.
Sourcefn browser_create_dir(&self, _name: &str) -> Result<(), String>
fn browser_create_dir(&self, _name: &str) -> Result<(), String>
Create a child directory at the current browser location.
Sourcefn browser_write(&self, name: &str, contents: &str) -> Result<String, String>
fn browser_write(&self, name: &str, contents: &str) -> Result<String, String>
Write a filename at the current browser location and return its stable identity for subsequent plain Save operations.
Sourcefn browser_write_bytes(
&self,
_file_name: &str,
_contents: &[u8],
) -> Result<String, String>
fn browser_write_bytes( &self, _file_name: &str, _contents: &[u8], ) -> Result<String, String>
Write contents under EXACTLY file_name (extension kept: no model
extension munging, unlike Self::browser_write) at the current
browser location, and return the written file’s full identity. The
Save-file chooser’s door: on desktop every export goes through it once
the user has chosen the folder in the explorer. Default: unsupported —
the browser has no folders to choose and downloads instead
(Self::export_file_named_bytes).
Sourcefn list_external_files(&self, _extensions: &[&str]) -> Vec<String>
fn list_external_files(&self, _extensions: &[&str]) -> Vec<String>
Files available to the in-app explorer for a foreign-format import. Names retain their extension. Browser storage returns none and offers an Upload button instead; desktop enumerates its application files directory.
Sourcefn read_external_file(&self, _name: &str) -> Option<Vec<u8>>
fn read_external_file(&self, _name: &str) -> Option<Vec<u8>>
Read one entry returned by Self::list_external_files.
Sourcefn supports_file_interchange(&self) -> bool
fn supports_file_interchange(&self) -> bool
Whether this backend can exchange files with the user’s real filesystem
(browser download+upload). The panel shows the Upload affordance only when
this is true.
Sourcefn export_file(
&self,
name: &str,
contents: &str,
) -> Result<Option<String>, String>
fn export_file( &self, name: &str, contents: &str, ) -> Result<Option<String>, String>
Hand contents to the user as a file named after name (browser: a
download; native w/ dialog: a Save-As). Returns the saved document’s
identity — the full path the user chose on native (so the caller can
re-save straight to it), the bare download name on the web — or None
when the user cancelled. No-op (None) by default.
Sourcefn begin_import(&self) -> Result<(), String>
fn begin_import(&self) -> Result<(), String>
Begin importing a real file — opens the platform picker. The result is
retrieved later via Self::take_import (upload/read is async in the
browser). No-op by default.
Sourcefn take_import(&self) -> Option<ImportedFile>
fn take_import(&self) -> Option<ImportedFile>
Poll for a completed import, consuming it.
None until a begin_import finishes. Default: never any.
Sourcefn begin_import_filtered(&self, _filter: (&str, &[&str])) -> Result<(), String>
fn begin_import_filtered(&self, _filter: (&str, &[&str])) -> Result<(), String>
Begin importing a real file behind a specific picker filter (a human
label + dot-less extensions, e.g. ("STEP", &["step","stp"])). The chosen
file’s contents + its FULL name (extension preserved, so the panel can
route it) arrive via Self::take_import. Default: reuse Self::begin_import.
Sourcefn export_file_named(
&self,
_file_name: &str,
_contents: &str,
) -> Result<(), String>
fn export_file_named( &self, _file_name: &str, _contents: &str, ) -> Result<(), String>
Hand contents to the user under EXACTLY file_name (extension included,
no model-extension munging) — the Save-As / download for a foreign export
format. The file’s media type comes from its extension
(media_type). Default no-op (interchange unsupported).
Sourcefn export_file_named_bytes(
&self,
_file_name: &str,
_contents: &[u8],
) -> Result<(), String>
fn export_file_named_bytes( &self, _file_name: &str, _contents: &[u8], ) -> Result<(), String>
Self::export_file_named for a BINARY format (GLB, a sheet PDF). Every
text export could go through this one — text is bytes — but the text
method stays because the browser’s Blob takes a string directly and
routing UTF-8 through a Uint8Array for every STEP file would be a copy
for nothing. Both lanes type the file the same way, from its extension,
so which lane an export takes is a question about copies and never about
what the user receives. Default no-op, like its text sibling.
Dyn Compatibility§
This trait is dyn compatible.
In older versions of Rust, dyn compatibility was called "object safety".