Restoration
Keys, scopes, persistent values, stores, and window restoration.
Typed persistent values over Signals. Missing,
corrupt, or incompatible snapshots fall back to defaults and count as stale.
| Type | Purpose |
|---|---|
RestorationKey | One path segment; rejects empty/NUL |
RestorationScope | Namespaced value bucket with JSON get/set |
Restorable<T> | Typed persistent value over a Signal |
RestorationHandle | Reset, flush, claim scopes, diagnostics |
RestorationConfig | App id, schema version, store, limits, migration |
InMemoryRestorationStore | Ephemeral store for tests |
FileRestorationStore | Atomic file store for production |
Keys and scopes
One RestorationKey is one path segment (/ is content, not a separator).
A restorable counter — close and relaunch to see the value survive. Copy, paste, and run:
use incular::prelude::*;
fn main() {
let config = RestorationConfig::file_backed("com.example.counter", 1)
.expect("restoration config");
let app = Application::new_with_restoration(
WindowOptions::default(),
config,
|cx| {
let key = RestorationKey::new("count").expect("key");
let root: Widget = match cx.restored_signal(key, 0u32) {
Some(count) => {
let tapped = count.clone();
Container::builder()
.padding(EdgeInsets::all(24.0))
.alignment(Alignment::CENTER)
.child(
GestureDetector::new(Text::new(format!(
"Count: {}",
count.get()
)))
.on_tap(move || tapped.update(|c| *c += 1)),
)
.build()
.into()
}
None => Text::new("Restoration off").into(),
};
root
},
)
.expect("valid application");
incular::run(app).expect("run application");
}Persistent values
get() reads, set() writes equality-checked, update() mutates,
remove() drops the entry, signal() exposes the underlying
Signal (always write through set/update so
persistence fires) — all exercised in the runnable program above.
TextEditingController::restored(scope, key) and
Form::register_with_restoration(controller, scope, key) bind editors and
fields the same way.
Stores and config
InMemoryRestorationStore::new() for tests; FileRestorationStore::new(path)
or for_application(id) (OS dir + restoration.json) for production;
RestorationConfig::new(id, version, store) or file_backed(id, version)
with with_snapshot_limit (.max(1), default 8 MiB), with_debounce, and
with_migration — the file_backed form runs in the program above.
Saves debounce (250 ms), write atomically (temp + rename; failures never clobber good files), retry 3x with 500 ms linear backoff.
Handle and diagnostics
Inside a build, cx.restoration() returns an optional handle; handle.reset(),
handle.flush(), handle.remove_scope(&scope), handle.claim_scope(&scope),
handle.diagnostics(), and handle.path() manage scopes and diagnostics.
ClaimedRestorationScope is an RAII lease; dropping releases the path.
RestorationStore trait (load / save / remove, optional location;
RestorationStoreError carries diagnostics and must leave prior snapshots
intact). RestorableWindowFactory is an in-memory factory for persisted
auxiliary window kinds (never serialized). RootRestorationScope /
UnmanagedRestorationScope bracket restoration subtrees explicitly.
Windows and routes
Restorable windows use WindowRestorationId::new("main"),
Application::new_restorable, register_restorable_window_factory, and
restore_restorable_windows (unknown kinds/ids/duplicates skipped and
counted); route stacks persist via RestorableRoute and
NavigationRestoreReport.
Related: Application,
Signal, EditableText.