Shared TOML config primitives for the curator,
warden, and lector apps —
the three sibling macOS consoles share the same window → group → tab config shape and the
same house style, so the domain-free pieces live here once instead of being copied between them.
It is intentionally not a generic config framework. The apps' models diverge at every level —
the leaf tab fields (curator: url/session; warden: dir/shell/probe; lector: dir), and
the Config/WindowConfig decorations around them — so each app keeps its own leaf type,
validation, and cascade resolution and calls these primitives. Only the parts that need zero
knowledge of the leaf are shared:
model— the leaf-free config-model primitives:Density(whole-app chrome sizing),OpenOnLaunch(window launch target), the non-fatalWarning, and the logic-freeGroup<T>container — anameplus aVec<T>of each app's own leaf tab, holding no leaf meaning itself. Plus the shared serde defaults (default_true,default_window_width/_height).fmt— a house-style TOML formatter (format_str,format_file) wrappingtaplowith a fixed style: nested-table indentation, aligned=and trailing comments, preserved key order, blank-line-separated containers with tight nested tabs.format_filerewrites atomically and only when bytes change (watcher-loop safe), preserving symlinks and file mode.fmt_cliis the sharedfmtCLI subcommand all three apps delegate to, sowarden fmt,curator fmt, andlector fmtare one implementation (thevalidatesubcommand stays per-app — it prints each app's own schema).colour—#rgb/#rrggbbhex parsing for the per-window accent colour (Colour::parse,Colour::hex).edit— leaf-agnostic structural insertion.add_tab(path, window_title, group, fields)appends a[[window.tab]]or[[window.group.tab]]table, atomic and comment-preserving viatoml_edit. The caller supplies an ordered field list; this module knows nothing about what the fields mean, so it works for curator's (url/session) leaves and warden's (dir/shell/probe) leaves alike. An unknown group errors rather than silently creating one.paths— config-path resolution (resolve_config_path,default_config_path). The named env var wins when set and non-empty; a set-but-empty var falls through to the default (~/.config/<app_dir>/config.toml) instead of the confusing "cannot read config" error an empty path would otherwise cause.seed— a starter config, on request only.write_default_config(path, template)writestemplatetopathatomically if nothing is there yet, and never clobbers an existing file. It never fires automatically (no launch hook, no first-run marker) — an app calls it only when the user asks for one. The mechanism is shared; the template is the caller's own leaf schema.
It's consumed as a git dependency (the apps build from source on a fresh clone, so there's no crates.io publish):
[dependencies]
config-core = { git = "https://github.com/Lockyc/config-core" }cargo test # unit + golden formatter tests
cargo fmt
cargo clippyMIT © Lachlan Collins