Reusing the CLI's registry patterns
The CLI ships two small modules that map a friendly filesystem name to
a backend file path (and to one workspace inside it). If you want the
same ergonomics in your own binary, the simplest path is to copy the
shapes — they're deliberately tiny and dependency-light. Extracting a
shared fslite-registry crate is a separate decision; this page is
about getting the patterns.
Why these live in fslite-cli
fslite-core and fslite-sqlite are deliberately name-free. They
operate on concrete file paths and WorkspaceIds. The mapping from
"a string the user typed" to "a concrete database file" is a
fslite-cli-side concern. The actual code is about 200 lines total
across three files; that's small enough to copy rather than crateify.
The data shapes
The two data structures live at
registry.rs (opens in a new tab)
and
context.rs (opens in a new tab).
use std::collections::BTreeMap;
use std::path::PathBuf;
use fslite_core::WorkspaceId;
/// On-disk mapping: name → SQLite database file,
/// name → workspace in that database.
#[derive(Default, Serialize, Deserialize)]
pub struct Registry {
pub filesystems: BTreeMap<String, PathBuf>,
pub workspaces: BTreeMap<String, BTreeMap<String, WorkspaceId>>,
}
/// Persisted default for the next bare-verb invocation.
#[derive(Default, Serialize, Deserialize)]
pub struct Context {
pub filesystem: Option<String>,
pub workspace: Option<String>,
}BTreeMap instead of HashMap so the JSON output is deterministic.
The config-directory resolver
CLI persisted state lives under a per-user config directory. The
resolver at
paths.rs (opens in a new tab)
honours, in priority order:
FSLITE_CONFIG_DIR(env var, manually settable).XDG_CONFIG_HOME(set by XDG-compliant desktops).$HOME/.config/fslite(the Linux default).
To get the same behaviour in your own binary, copy the function verbatim. It's ~25 lines including the env-var mutex that serialises test mutations.
How to copy these patterns
The easiest, most explicit, lowest-friction route:
# In your own repo:
mkdir -p src/cli_state
curl -fsSL \
https://raw.githubusercontent.com/seanrobmerriam/fslite/main/crates/fslite-cli/src/registry.rs \
-o src/cli_state/registry.rs
curl -fsSL \
https://raw.githubusercontent.com/seanrobmerriam/fslite/main/crates/fslite-cli/src/context.rs \
-o src/cli_state/context.rs
curl -fsSL \
https://raw.githubusercontent.com/seanrobmerriam/fslite/main/crates/fslite-cli/src/paths.rs \
-o src/cli_state/paths.rsAdjust the mod declarations and crate-internal field types
(WorkspaceId is just re-exported from fslite-core). The
serialisation JSON shape and the resolve-priority order are part of the
contract; the rest is internal.
You'll also need to drop in serde and serde_json to your
Cargo.toml; the original paths.rs adds tempfile only for tests.
When to ask for extraction
The shapes pull into a shared crate when at least one of these is true:
- You have two or more binaries in the same repo that need to share the registry (e.g., a server and a CLI that should agree on friendly names).
- The registry shapes need their own versioned migration support (the in-tree version is "rewrite on save").
- You want third-party tools to read the same registry
(
fslite-doctor, a TUI, etc.).
If none of these apply, copying is the right call — adding a published
crate before its API is stable costs every consumer a Cargo.toml
edit.
When NOT to copy
If your app's notion of "registered filesystem" is materially different from the CLI's — for example, if your friendly names must round-trip through DNS, or if your workspaces aren't backed by SQLite — then the shapes here won't be a clean fit. Use them as a reference, but build your own.