Expand description
teksilo-settings — persistent, reactive user preferences for Teksilo.
Two persistence shapes share one storage backbone:
SettingsStore— dynamic, dotted-key K/V for scalar values (numbers, strings, bools, arrays of those), surfaced asSignal<T>. The QSettings analogue. Struct values aren’t supported here because TOML serializes them as tables, indistinguishable from nested key paths; useSettingsFile<T>for those instead.SettingsFile<T>— typed single-struct persistence with migrations.PersistedListModel<T>— a reactive,Keyed-item ordered collection persisted to a single file, merging by op (upsert / remove / clear) rather than by whole-document snapshot — so a peer process’s concurrent addition survives.MruList<T>— generic dedupe + pin + cap list over an app-defined item type implementingMruEntry. Apps register their own viaTeksiloAppBuilder::app_state(handle).WindowStateService— per-window geometry persistence. When registered viaTeksiloAppBuilder::settings(...), everyWindowConfigcarrying anid(...)is automatically restored on creation (sanitized against the current monitor) and recorded on every change byteksilo-app’s window manager. No widget-side wiring required.
All disk writes are atomic (write-temp + rename). Every write merges
against the document read fresh off disk under an exclusive lock —
never a blind whole-document overwrite — so two processes sharing a
file (Skribisto’s one-process-per-project model shares general.toml,
recents.toml, window_state.toml across every open project) cannot
silently clobber each other. Reloadable is the matching read-side
contract: a hook a file-system watcher calls to push a peer’s write into
this process’s live signals/models — and SettingsWatcher +
SettingsRegistry are the actual watcher: a notify-based directory
watch (parent-directory, not file, so an atomic rename-over doesn’t
invalidate it) plus the path → Reloadable lookup a changed-file
event dispatches through. TeksiloAppBuilder::settings(...) wires one
up automatically (opt out via .settings_watch(false)); every service
SettingsBundle::open opens is pre-registered into
OpenedSettings::registry, and application code can register its
own ad hoc SettingsFile / PersistedListModel / MruList handles
into that same registry.
use teksilo_settings::{AppPaths, SettingsStore, SettingsKey};
const FONT_SIZE: SettingsKey<f32> =
SettingsKey::new("editor.font_size", || 14.0);
// Tests / docs use `for_testing(...)` against a tempdir so they
// never touch the user's real config tree. Production apps use
// `AppPaths::new(qualifier, organization, application)`.
let tmp = tempfile::tempdir().unwrap();
let paths = AppPaths::for_testing(tmp.path());
let store = SettingsStore::open(paths.config_file("general")).unwrap();
let font_size = store.signal_for(&FONT_SIZE);
font_size.set(18.0); // persisted on the next debounce tickStructs§
- AppPaths
- Resolved OS-correct application directories (config and data).
- Debounced
Writer - Atomic, debounced single-file writer.
- List
File - On-disk shape for a persisted list: a versioned wrapper around
Vec<T>. Apps write migrations against this type, not the bareVec. - Migrator
- Schema migration pipeline for a
Versionedtype. - MruList
- A persisted MRU list backed by
PersistedListModel<T>. - Opened
Settings - The outcome of
SettingsBundle::open: ready-to-register handles. - PerWindow
State - Persisted geometry for one labeled window.
- Persisted
List Model - A reactive,
Keyed-item list whose mutations persist to a single TOML file by merging ops, not by overwriting a whole-document snapshot. - Settings
Bundle - Declarative configuration for the persistence services an app wants installed.
- Settings
File - A reactive handle to a single typed file on disk.
- Settings
Key - A statically-named setting. Centralizes the dotted key, the value
type, and the default factory. Construct as a
const: - Settings
Registry - Registry mapping a canonical settings path to the live
Reloadablehandle that owns it, so a file-watcher event naming that path can be dispatched to the right in-memory state. - Settings
Store - A dynamic dotted-key reactive settings store.
- Settings
Watcher - Active directory watcher over one or more settings directories. One
per
TeksiloAppBuilder::runinvocation (when a settings bundle with watching enabled is configured). - Window
State Service - Persistent, in-memory-backed store for per-window geometry.
Enums§
- Flush
Error - Errors surfaced by
DebouncedWriter::flush_now. - ListOp
- A replayable mutation of a
PersistedListModel’s backing list, expressed by key so it can be applied to any startingVec<T>— in particular, the fresh one read off disk at flush time, which may already include a peer process’s concurrent changes. - Migration
Error - Errors surfaced by
Migrator::run. - Settings
Bundle Error - Errors surfaced by
SettingsBundle::open. - Settings
File Error - Errors surfaced by
SettingsFileoperations (and, by extension, every other persisted type in this crate — they all share this error type). - Settings
Store Error - Errors surfaced by
SettingsStore::open.
Constants§
- DEFAULT_
DEBOUNCE - Default debounce window for store flushes.
- TEXT_
SCALE_ KEY - Persisted user-controlled global text-scale factor (
1.0= 100 %).
Traits§
- Keyed
- An item with a stable, owned identity — the merge key
PersistedListModeldedupes and diffs by. - MruEntry
- An item that can live in an
MruList. RequiresKeyedfor its stable merge identity; adds the pin / touch vocabulary an MRU list specifically needs on top. - Reloadable
- A persisted type that can be told “the file may have changed on disk — go look,” and will push any genuinely new content into its live signals/models.
- Settings
Ext - Convenience accessors for settings services attached to the app’s
app_stateregistry. - Versioned
- A persisted struct whose schema is versioned.
Functions§
- set_
write_ failure_ sink - Register a process-wide sink invoked whenever any
DebouncedWriterpermanently discards a queued write (seeWriteFailureSink). There is only one slot: a later call replaces an earlier one.teksilo-appuses this to forward the failure to the UI thread as a typedAppEvent.
Type Aliases§
- Landed
Stamp - The
(mtime, len)stampdisk_stampcomputes for a settings file — named so everyArc<Mutex<...>>wrapping it (here and inWindowStateService) reads as one term instead of clippy’stype_complexity-tripping nested-generics spelling. - Settings
Reload Sink - Sink type invoked on the notify worker thread whenever a watched
settings directory reports a create/modify event. Implementations
must be thread-safe;
teksilo-app’s implementation posts the path through the winitEventLoopProxyasAppEvent::SettingsReload, which hops back onto the UI thread where the (single-threaded,Rc-based)SettingsRegistryactually lives. - Write
Failure Sink - Invoked (off the caller’s thread — on the shared worker thread) when a
DebouncedWriter’s queued patches are permanently discarded: eitherflush_writergave up afterMAX_WRITE_ATTEMPTS, or the writer was dropped (Unregister) while its final flush was still failing. This is the write-side analogue ofcrate::reload::Reloadable’s read-side contract — the previous behaviour was a bareeprintln!that never left the worker thread, so a permanently unwritable settings file (read-only mount, revoked permissions, disk full) silently ate every change for the rest of the session with zero signal to the application. Registered process-wide viaset_write_failure_sink. - Write
Landed Sink - Invoked on the shared worker thread the instant a
DebouncedWriter’s queued patches land successfully, with the fresh on-disk(mtime, len)stamp (one extrafs::metadata, computed once, right after the write — negligible cost). The write-side analogue ofWriteFailureSink.Send + Syncbecause it runs off the caller’s thread — a consumer that needs to update!Sendstate (anRc<Cell<_>>) must copy the value out on its own thread the next time it looks (seeWindowStateService::reload_from_disk).