Skip to main content

Crate teksilo_settings

Crate teksilo_settings 

Source
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 as Signal<T>. The QSettings analogue. Struct values aren’t supported here because TOML serializes them as tables, indistinguishable from nested key paths; use SettingsFile<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 implementing MruEntry. Apps register their own via TeksiloAppBuilder::app_state(handle).
  • WindowStateService — per-window geometry persistence. When registered via TeksiloAppBuilder::settings(...), every WindowConfig carrying an id(...) is automatically restored on creation (sanitized against the current monitor) and recorded on every change by teksilo-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 tick

Structs§

AppPaths
Resolved OS-correct application directories (config and data).
DebouncedWriter
Atomic, debounced single-file writer.
ListFile
On-disk shape for a persisted list: a versioned wrapper around Vec<T>. Apps write migrations against this type, not the bare Vec.
Migrator
Schema migration pipeline for a Versioned type.
MruList
A persisted MRU list backed by PersistedListModel<T>.
OpenedSettings
The outcome of SettingsBundle::open: ready-to-register handles.
PerWindowState
Persisted geometry for one labeled window.
PersistedListModel
A reactive, Keyed-item list whose mutations persist to a single TOML file by merging ops, not by overwriting a whole-document snapshot.
SettingsBundle
Declarative configuration for the persistence services an app wants installed.
SettingsFile
A reactive handle to a single typed file on disk.
SettingsKey
A statically-named setting. Centralizes the dotted key, the value type, and the default factory. Construct as a const:
SettingsRegistry
Registry mapping a canonical settings path to the live Reloadable handle that owns it, so a file-watcher event naming that path can be dispatched to the right in-memory state.
SettingsStore
A dynamic dotted-key reactive settings store.
SettingsWatcher
Active directory watcher over one or more settings directories. One per TeksiloAppBuilder::run invocation (when a settings bundle with watching enabled is configured).
WindowStateService
Persistent, in-memory-backed store for per-window geometry.

Enums§

FlushError
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 starting Vec<T> — in particular, the fresh one read off disk at flush time, which may already include a peer process’s concurrent changes.
MigrationError
Errors surfaced by Migrator::run.
SettingsBundleError
Errors surfaced by SettingsBundle::open.
SettingsFileError
Errors surfaced by SettingsFile operations (and, by extension, every other persisted type in this crate — they all share this error type).
SettingsStoreError
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 PersistedListModel dedupes and diffs by.
MruEntry
An item that can live in an MruList. Requires Keyed for 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.
SettingsExt
Convenience accessors for settings services attached to the app’s app_state registry.
Versioned
A persisted struct whose schema is versioned.

Functions§

set_write_failure_sink
Register a process-wide sink invoked whenever any DebouncedWriter permanently discards a queued write (see WriteFailureSink). There is only one slot: a later call replaces an earlier one. teksilo-app uses this to forward the failure to the UI thread as a typed AppEvent.

Type Aliases§

LandedStamp
The (mtime, len) stamp disk_stamp computes for a settings file — named so every Arc<Mutex<...>> wrapping it (here and in WindowStateService) reads as one term instead of clippy’s type_complexity-tripping nested-generics spelling.
SettingsReloadSink
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 winit EventLoopProxy as AppEvent::SettingsReload, which hops back onto the UI thread where the (single-threaded, Rc-based) SettingsRegistry actually lives.
WriteFailureSink
Invoked (off the caller’s thread — on the shared worker thread) when a DebouncedWriter’s queued patches are permanently discarded: either flush_writer gave up after MAX_WRITE_ATTEMPTS, or the writer was dropped (Unregister) while its final flush was still failing. This is the write-side analogue of crate::reload::Reloadable’s read-side contract — the previous behaviour was a bare eprintln! 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 via set_write_failure_sink.
WriteLandedSink
Invoked on the shared worker thread the instant a DebouncedWriter’s queued patches land successfully, with the fresh on-disk (mtime, len) stamp (one extra fs::metadata, computed once, right after the write — negligible cost). The write-side analogue of WriteFailureSink. Send + Sync because it runs off the caller’s thread — a consumer that needs to update !Send state (an Rc<Cell<_>>) must copy the value out on its own thread the next time it looks (see WindowStateService::reload_from_disk).