Trim comments across Rust and webapp source to direct one-liners (ticket 030)

Pass over src/*.rs, webapp/src/**, and the in-app "?" docs prose:
condensed multi-paragraph doc comments to single lines that state the
non-obvious why, deleted comments that only restated adjacent code, and
tightened the Docs.tsx help-panel wording. No logic, structure, or code
formatting changed — verified via cargo test (143 passed) and
tsc -b && vite build.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XFUNZwCvz3kcod3352ZR2o
This commit is contained in:
Hugh Kaznowski
2026-09-05 17:02:33 +01:00
co-authored by Claude Sonnet 5
parent 3cef6dc8ae
commit 00c21c8032
32 changed files with 347 additions and 1550 deletions
+17 -171
View File
@@ -29,8 +29,7 @@ static WEBAPP_DIST: EmbedDir = fs_embed::fs_embed!("webapp/dist");
struct ApiState {
engine: EngineHandle,
settings: SettingsHandle,
/// Path to the settings JSON file on disk. Imported CSS files are stored
/// alongside it, in its parent directory (see `ApiState::css_dir`).
/// Imported CSS files are stored alongside this path's parent directory (see `ApiState::css_dir`).
settings_path: Arc<PathBuf>,
}
@@ -42,13 +41,7 @@ impl ApiState {
.unwrap_or_else(|| PathBuf::from("."))
}
/// Persists a newly-declared param into the active profile, if any (see
/// `ProfileStore::record_param_declaration`), so it survives a restart.
/// A no-op (not an error) when no profile is currently active — the
/// engine declaration above has already succeeded regardless. Failures
/// (e.g. I/O) are logged to stderr rather than surfaced to the caller: the
/// engine declaration is the source of truth for the running process, and
/// this is a best-effort persistence step layered on top.
/// Best-effort: a no-op if no profile is active, and I/O failures are only logged, since the engine declaration is already the source of truth.
fn persist_param_declaration(&self, request: &DeclareParamRequest) {
let Some((store_path, profile_name)) = self.active_profile_location() else {
return;
@@ -76,7 +69,6 @@ impl ApiState {
}
}
/// The active profile's store path and name, if a profile is currently active.
fn active_profile_location(&self) -> Option<(PathBuf, String)> {
let settings = self.settings.lock().unwrap();
let profile_name = settings.profiles.active_profile.clone()?;
@@ -126,8 +118,7 @@ pub fn router(engine: EngineHandle, settings: SettingsHandle, settings_path: Pat
.fallback(serve_webapp)
}
/// Serves the webapp embedded in the binary (see `WEBAPP_DIST`), falling
/// back to `index.html` for any unmatched path so client-side routing works.
/// Falls back to `index.html` for unmatched paths so client-side routing works.
async fn serve_webapp(uri: axum::http::Uri) -> Response {
let path = uri.path().trim_start_matches('/');
let file = WEBAPP_DIST
@@ -167,11 +158,6 @@ async fn ask(engine: &EngineHandle, query: EngineQuery) -> Result<EngineResponse
response.await.map_err(|_| StatusCode::SERVICE_UNAVAILABLE)
}
/// `GET /api/logs` — also accepts a `filter` query param (ticket 007): a
/// FilterBar query string, parsed and evaluated inside the engine. A
/// malformed filter string comes back as `EngineResponse::Error` and is
/// surfaced here as `400 Bad Request` with `{ "error": "..." }`, matching
/// ticket 004's `declare_param`/`declare_metric` error-mapping pattern.
async fn get_logs(
State(state): State<ApiState>,
Query(params): Query<LogsParams>,
@@ -186,8 +172,6 @@ async fn get_logs(
}
}
/// `GET /api/timeline` — same `filter` query param and error mapping as
/// `get_logs` above.
async fn get_timeline(
State(state): State<ApiState>,
Query(params): Query<TimelineParams>,
@@ -210,10 +194,6 @@ async fn get_stats(State(state): State<ApiState>) -> impl IntoResponse {
}
}
/// `GET /api/params` — lists all declared params (name, regex, capture
/// group, kind) as `Vec<ParamDeclarationDto>`. Mirrors `list_metrics` below
/// (ticket 015 needed this for LogPreview's column picker; ticket 004 only
/// exposed the equivalent for metrics).
async fn list_params(State(state): State<ApiState>) -> impl IntoResponse {
match ask(&state.engine, EngineQuery::ParamDeclarations).await {
Ok(EngineResponse::ParamDeclarations(declarations)) => Json(declarations).into_response(),
@@ -222,14 +202,6 @@ async fn list_params(State(state): State<ApiState>) -> impl IntoResponse {
}
}
/// `POST /api/params` — declares a param. Body mirrors `DeclareParamRequest`:
/// `{ "name": "...", "message_match_regex": "...", "capture_group": "...",
/// "kind": "string" | "number" | "timestamp" }`. On success, also persists
/// the declaration into the active profile (if any — see
/// `ApiState::persist_param_declaration`) and returns `201 Created` with the
/// declaration as `ParamDeclarationDto`. On a validation failure (bad regex,
/// unknown capture group), returns `400 Bad Request` with
/// `{ "error": "..." }`.
async fn declare_param(
State(state): State<ApiState>,
Json(body): Json<DeclareParamRequest>,
@@ -260,8 +232,6 @@ async fn declare_param(
}
}
/// `GET /api/metrics` — lists all declared metrics (name, regex, params) as
/// `Vec<MetricDeclarationDto>`.
async fn list_metrics(State(state): State<ApiState>) -> impl IntoResponse {
match ask(&state.engine, EngineQuery::MetricDeclarations).await {
Ok(EngineResponse::MetricDeclarations(declarations)) => Json(declarations).into_response(),
@@ -270,11 +240,6 @@ async fn list_metrics(State(state): State<ApiState>) -> impl IntoResponse {
}
}
/// `POST /api/metrics` — declares a metric. Body mirrors
/// `DeclareMetricRequest`: `{ "name": "...", "message_match_regex": "...",
/// "params": [{ "param_name": "...", "capture_group": "...", "kind": "..." }] }`.
/// Same success/failure shape as `declare_param` above, persisting into the
/// active profile via `ApiState::persist_metric_declaration`.
async fn declare_metric(
State(state): State<ApiState>,
Json(body): Json<DeclareMetricRequest>,
@@ -306,9 +271,7 @@ async fn declare_metric(
}
}
/// Parses the `labels` query param (`"foo=bar,baz=qux"`) into a
/// `key -> expected value` map. Malformed pairs (no `=`) are skipped rather
/// than rejected — see `BucketValuesParams` doc comment for the format.
/// Malformed pairs (no `=`) are skipped rather than rejected.
fn parse_labels_filter(raw: &str) -> BTreeMap<String, String> {
raw.split(',')
.filter_map(|pair| pair.split_once('='))
@@ -316,9 +279,6 @@ fn parse_labels_filter(raw: &str) -> BTreeMap<String, String> {
.collect()
}
/// `GET /api/metrics/:name` — queries bucket values for one metric. Query
/// params: `labels` (see `parse_labels_filter`), `start_ms`, `end_ms`,
/// `since_sequence`, `limit` (mirroring `/api/logs`/`/api/timeline`).
async fn get_metric_bucket_values(
State(state): State<ApiState>,
AxumPath(metric_name): AxumPath<String>,
@@ -340,12 +300,6 @@ async fn get_metric_bucket_values(
}
}
/// `GET /api/query` — evaluates a PromQL query (ticket 013; see `crate::promql` for
/// the supported grammar) against declared metrics. Query params: `promql` (the
/// query string), `start_ms`, `end_ms` (bound which points are included, mirroring
/// `/api/logs`/`/api/metrics/:name`). Returns `Vec<PromqlSeriesDto>` — chart-ready
/// `labels` + `(timestamp_ms, value)` series — on success (`200 OK`), or
/// `400 Bad Request` with `{ "error": "..." }` for a malformed query.
async fn run_promql_query(
State(state): State<ApiState>,
Query(params): Query<PromqlParams>,
@@ -365,10 +319,6 @@ async fn run_promql_query(
}
}
/// Maps a `CssError` from a settings mutation/lookup to a response status
/// and message: `NotFound` -> 404, `NotImported` -> 400 (the id is valid but
/// refers to a linked entry, which this operation doesn't apply to), `Io` ->
/// 500.
fn css_error_response(err: CssError) -> axum::response::Response {
match err {
CssError::NotFound => (StatusCode::NOT_FOUND, err.to_string()).into_response(),
@@ -377,18 +327,12 @@ fn css_error_response(err: CssError) -> axum::response::Response {
}
}
/// `GET /api/settings/css` — lists all configured CSS entries (imported and
/// linked) as `Vec<CssEntryDto>`.
async fn list_css(State(state): State<ApiState>) -> impl IntoResponse {
let settings = state.settings.lock().unwrap();
let entries: Vec<CssEntryDto> = settings.css_entries.iter().map(CssEntryDto::from).collect();
Json(entries).into_response()
}
/// `POST /api/settings/css` — imports raw CSS content. Body:
/// `{ "filename": "theme.css", "content": "body { ... }" }`. Stores the
/// content as a file alongside the settings file, adds a `CssEntry::Imported`
/// to settings, and returns the new entry as `CssEntryDto` (201 Created).
async fn import_css(
State(state): State<ApiState>,
Json(body): Json<ImportCssRequest>,
@@ -414,11 +358,7 @@ async fn import_css(
}
}
/// `POST /api/settings/css/link` — registers a reference to an external CSS
/// path/URL. Body: `{ "path_or_url": "https://example.com/style.css" }`.
/// Content is never copied; the entry is never deletable via
/// `DELETE /api/settings/css/:id`. Returns the new entry as `CssEntryDto`
/// (201 Created).
/// Content is never copied, and the entry can never be deleted via `DELETE /api/settings/css/:id`.
async fn link_css(
State(state): State<ApiState>,
Json(body): Json<LinkCssRequest>,
@@ -436,10 +376,6 @@ async fn link_css(
}
}
/// `DELETE /api/settings/css/:id` — removes an imported CSS entry (file +
/// settings reference). Rejects with 400 if `id` refers to a `Linked` entry
/// (never deletable this way) and 404 if `id` is unknown. On success returns
/// 204 No Content.
async fn remove_css(
State(state): State<ApiState>,
AxumPath(id): AxumPath<String>,
@@ -452,10 +388,7 @@ async fn remove_css(
}
}
/// `GET /api/settings/css/:id/content` — serves back the raw CSS content of
/// an imported entry (`content-type: text/css`). Rejects with 400 for a
/// linked entry (there's no local content to serve — the frontend should use
/// `path_or_url` directly) and 404 for an unknown id.
/// Rejects a linked entry with 400 — there's no local content; the frontend should use `path_or_url` directly.
async fn get_css_content(
State(state): State<ApiState>,
AxumPath(id): AxumPath<String>,
@@ -473,8 +406,6 @@ async fn get_css_content(
}
}
/// Maps an `AlertError` to a response status/message: `NotFound` -> 404,
/// `Io` -> 500. Mirrors `css_error_response` above.
fn alert_error_response(err: AlertError) -> axum::response::Response {
match err {
AlertError::NotFound => (StatusCode::NOT_FOUND, err.to_string()).into_response(),
@@ -482,18 +413,11 @@ fn alert_error_response(err: AlertError) -> axum::response::Response {
}
}
/// `GET /api/settings/alerts` — lists all configured alert rules as
/// `Vec<AlertRule>`.
async fn list_alert_rules(State(state): State<ApiState>) -> impl IntoResponse {
let settings = state.settings.lock().unwrap();
Json(settings.alert_rules.clone()).into_response()
}
/// `POST /api/settings/alerts` — creates an alert rule. Body:
/// `{ "name": "...", "enabled": true, "condition": { "kind": "immediate" | \
/// "threshold" | "newvalue" | "ratio", ... }, "sink": { "kind": "ntfy", \
/// "topic": "...", "server": "..." | null } }`. Returns `201 Created` with
/// the created rule (including its generated `id`).
async fn create_alert_rule(
State(state): State<ApiState>,
Json(body): Json<CreateAlertRuleRequest>,
@@ -505,9 +429,6 @@ async fn create_alert_rule(
}
}
/// `PUT /api/settings/alerts/:id` — replaces an alert rule's fields (id in
/// the URL is authoritative). Returns the updated rule, or `404 Not Found`
/// if no rule with `id` exists.
async fn update_alert_rule(
State(state): State<ApiState>,
AxumPath(id): AxumPath<String>,
@@ -520,8 +441,6 @@ async fn update_alert_rule(
}
}
/// `DELETE /api/settings/alerts/:id` — removes an alert rule. Returns
/// `204 No Content`, or `404 Not Found` if no rule with `id` exists.
async fn delete_alert_rule(State(state): State<ApiState>, AxumPath(id): AxumPath<String>) -> impl IntoResponse {
let mut settings = state.settings.lock().unwrap();
match settings.remove_alert_rule(&state.settings_path, &id) {
@@ -530,11 +449,7 @@ async fn delete_alert_rule(State(state): State<ApiState>, AxumPath(id): AxumPath
}
}
/// `POST /api/settings/alerts/test` — delivers a test notification to an
/// ntfy topic/server without requiring a saved rule, so the sink can be
/// verified before saving. Body: `{ "topic": "...", "server": "..." | null }`.
/// Returns `204 No Content` on success, `502 Bad Gateway` if ntfy rejects
/// or is unreachable.
/// Delivers a test notification without a saved rule so the sink can be verified before saving.
async fn test_alert_notification(Json(body): Json<TestAlertNotificationRequest>) -> impl IntoResponse {
let server = body.server.unwrap_or_else(|| "https://ntfy.sh".to_string());
match deliver_ntfy(&server, &body.topic, "Test notification from ottyl").await {
@@ -543,20 +458,13 @@ async fn test_alert_notification(Json(body): Json<TestAlertNotificationRequest>)
}
}
/// `GET /api/settings` — returns the subset of `Settings` the NavMenu UI
/// needs (colour scheme, active profile name) as `SettingsDto`. CSS entries
/// have their own dedicated list endpoint (`GET /api/settings/css`).
/// CSS entries have their own endpoint (`GET /api/settings/css`).
async fn get_settings(State(state): State<ApiState>) -> impl IntoResponse {
let settings = state.settings.lock().unwrap();
Json(SettingsDto::from(&*settings)).into_response()
}
/// `PATCH /api/settings` — updates process-wide settings. Body:
/// `{ "color_scheme": "light" | "dark" | "system", "max_logs": number |
/// null }` (omit a field to leave it unchanged; `max_logs: null` clears the
/// cap back to unlimited). Persists to `settings_path`, pushes a changed
/// `max_logs` to the engine so it takes effect on the next ingest without a
/// restart, and returns the updated settings as `SettingsDto`.
/// `max_logs: null` clears the cap; a changed value is pushed to the engine so it applies without a restart.
async fn update_settings(
State(state): State<ApiState>,
Json(body): Json<UpdateSettingsRequest>,
@@ -588,9 +496,6 @@ async fn update_settings(
Json(new_max_logs.0).into_response()
}
/// Maps a `ProfileError` to a response status/message: `NotFound` -> 404,
/// `DuplicateName`/`CannotDeleteDefault` -> 400, `Io` -> 500. Mirrors
/// `css_error_response` above.
fn profile_error_response(err: ProfileError) -> axum::response::Response {
match err {
ProfileError::NotFound => (StatusCode::NOT_FOUND, err.to_string()).into_response(),
@@ -600,10 +505,6 @@ fn profile_error_response(err: ProfileError) -> axum::response::Response {
}
}
/// `GET /api/settings/profiles` — lists all known profiles with their
/// param/metric declaration and dashboard counts, as `Vec<ProfileSummaryDto>`.
/// Use `GET /api/settings`'s `active_profile` field to know which one (if
/// any) is currently active.
async fn list_profiles(State(state): State<ApiState>) -> impl IntoResponse {
let settings = state.settings.lock().unwrap();
let profiles_dir = profile::resolve_profiles_dir(&settings, &state.settings_path);
@@ -612,10 +513,6 @@ async fn list_profiles(State(state): State<ApiState>) -> impl IntoResponse {
Json(summaries).into_response()
}
/// `POST /api/settings/profiles` — creates a new, empty profile. Body:
/// `{ "name": "..." }`. Returns `201 Created` with the new profile as
/// `ProfileSummaryDto`, or `400 Bad Request` if a profile with that name
/// already exists.
async fn create_profile(
State(state): State<ApiState>,
Json(body): Json<CreateProfileRequest>,
@@ -633,11 +530,7 @@ async fn create_profile(
}
}
/// `DELETE /api/settings/profiles/:name` — deletes a profile. If it was the
/// active profile, `active_profile` is cleared (but the engine's currently
/// declared params/metrics are left as-is — they simply won't be reloaded on
/// the next restart). Returns `204 No Content`, or `404 Not Found` if no
/// profile with that name exists.
/// If it was the active profile, `active_profile` is cleared, but the engine's already-declared params/metrics keep running until the next restart.
async fn delete_profile(
State(state): State<ApiState>,
AxumPath(name): AxumPath<String>,
@@ -652,12 +545,7 @@ async fn delete_profile(
}
}
/// Removes every currently-declared param/metric from the running engine
/// (via `EngineMessage::RemoveParamDeclaration`/`RemoveMetricDeclaration`),
/// then replays `profile`'s stored declarations into it (see
/// `profile::load_into_engine`). Used by `activate_profile` so switching the
/// active profile takes effect on the running engine immediately, without
/// requiring a process restart.
/// Removes every currently-declared param/metric from the engine, then replays `profile`'s declarations into it.
async fn reload_engine_declarations(engine: &EngineHandle, profile: &Profile) {
if let Ok(EngineResponse::ParamDeclarations(params)) =
ask(engine, EngineQuery::ParamDeclarations).await
@@ -690,12 +578,6 @@ async fn reload_engine_declarations(engine: &EngineHandle, profile: &Profile) {
profile::load_into_engine(engine, profile).await;
}
/// `POST /api/settings/profiles/:name/activate` — sets `name` as the active
/// profile (persisted, see `ProfileStore::set_active`) and hot-swaps the
/// running engine's declared params/metrics to match it (see
/// `reload_engine_declarations`), so the switch is visible without a
/// restart. Returns the updated settings as `SettingsDto`, or `404 Not
/// Found` if no profile with that name exists.
async fn activate_profile(
State(state): State<ApiState>,
AxumPath(name): AxumPath<String>,
@@ -723,9 +605,6 @@ async fn activate_profile(
}
}
/// `GET /api/settings/profiles/:name/dashboards` — lists all dashboards saved
/// on the profile `name`, as `Vec<DashboardDto>`. Returns `404 Not Found` if
/// no profile with that name exists.
async fn list_dashboards(
State(state): State<ApiState>,
AxumPath(name): AxumPath<String>,
@@ -742,10 +621,6 @@ async fn list_dashboards(
}
}
/// `POST /api/settings/profiles/:name/dashboards` — creates a new, empty
/// dashboard on profile `name`. Body: `{ "name": "..." }`. Returns
/// `201 Created` with the new dashboard as `DashboardDto`, or `404 Not
/// Found` if no profile with that name exists.
async fn create_dashboard(
State(state): State<ApiState>,
AxumPath(name): AxumPath<String>,
@@ -761,9 +636,6 @@ async fn create_dashboard(
}
}
/// `GET /api/settings/profiles/:name/dashboards/:dashboard_id` — fetches one
/// dashboard by id. Returns `404 Not Found` if the profile or dashboard
/// doesn't exist.
async fn get_dashboard(
State(state): State<ApiState>,
AxumPath((name, dashboard_id)): AxumPath<(String, String)>,
@@ -780,11 +652,6 @@ async fn get_dashboard(
}
}
/// `PUT /api/settings/profiles/:name/dashboards/:dashboard_id` — replaces the
/// dashboard's name/panels. Body: `{ "name": "...", "panels": [{ "id": "...",
/// "title": "...", "promql": "...", "chart_type": "bar" | "line" | "pie" }] }`.
/// Returns the updated dashboard as `DashboardDto`, or `404 Not Found` if the
/// profile or dashboard doesn't exist.
async fn save_dashboard(
State(state): State<ApiState>,
AxumPath((name, dashboard_id)): AxumPath<(String, String)>,
@@ -805,9 +672,6 @@ async fn save_dashboard(
}
}
/// `DELETE /api/settings/profiles/:name/dashboards/:dashboard_id` — deletes a
/// dashboard. Returns `204 No Content`, or `404 Not Found` if the profile or
/// dashboard doesn't exist.
async fn delete_dashboard(
State(state): State<ApiState>,
AxumPath((name, dashboard_id)): AxumPath<(String, String)>,
@@ -836,10 +700,7 @@ mod tests {
use tokio::sync::mpsc;
use tower::ServiceExt;
/// Spins up a fresh engine + router pair, backed by a temp settings file,
/// for one test. Returns the router, the engine handle (so a test can
/// ingest lines directly without an HTTP route for it), and the
/// `TempDir` guard (kept alive for the test's duration).
/// Spins up a fresh engine + router pair backed by a temp settings file.
fn test_app() -> (Router, EngineHandle, SettingsHandle, tempfile::TempDir) {
let (engine_tx, engine_rx) = mpsc::channel(128);
@@ -937,9 +798,7 @@ mod tests {
let status = delete_request(&app, "/api/settings/alerts/does-not-exist").await;
assert_eq!(status, StatusCode::NOT_FOUND);
// `update_alert_rule`'s `AlertError::NotFound` maps to a plain-text
// 404 body (mirrors `css_error_response`), not JSON, so this checks
// the status directly rather than via `send_json`'s JSON parsing.
// AlertError::NotFound maps to a plain-text (not JSON) 404 body, so check status directly instead of via send_json.
let request = Request::builder()
.method("PUT")
.uri("/api/settings/alerts/does-not-exist")
@@ -1085,16 +944,11 @@ mod tests {
let values = body.as_array().unwrap();
assert_eq!(values.len(), 2);
for value in values {
// `MetricPrimitive` is a Rust enum with derived `Serialize`, so its
// JSON form is externally tagged: `{"String": "/a"}`.
// MetricPrimitive derives Serialize as an externally-tagged enum, so JSON is {"String": "/a"}.
assert_eq!(value["labels"]["path"]["String"], "/a");
}
// Bucket values are grouped by label combination (ordered by the
// labels' `BTreeMap` key, i.e. "/a" before "/b"), not globally by
// sequence — so filtering to sequence > 0 leaves sequence 2 (from the
// "/a" bucket) ahead of sequence 1 (from the "/b" bucket), and a
// limit of 1 picks the former.
// Bucket values group by label (BTreeMap-ordered "/a" before "/b"), not by sequence, so limit=1 picks sequence 2 from "/a" ahead of sequence 1 from "/b".
let (status, body) = get_json(&app, "/api/metrics/requests?since_sequence=0&limit=1").await;
assert_eq!(status, StatusCode::OK);
let values = body.as_array().unwrap();
@@ -1159,10 +1013,7 @@ mod tests {
async fn declaring_param_persists_into_active_profile() {
let (app, _engine, settings_handle, dir) = test_app();
// Set up an active profile against the *same* shared `settings`
// handle the router was built with — mutating a separate `Settings`
// loaded fresh from disk wouldn't be visible to the running app's
// in-memory settings.
// Use the same shared settings handle the router was built with — a separately-loaded Settings wouldn't be visible to the running app.
let settings_path = dir.path().join("settings.json");
let profiles_dir = {
let settings = settings_handle.lock().unwrap();
@@ -1229,12 +1080,10 @@ mod tests {
assert!(body["panels"].as_array().unwrap().is_empty());
let dashboard_id = body["id"].as_str().unwrap().to_string();
// Listing shows the new (empty) dashboard.
let (status, body) = get_json(&app, "/api/settings/profiles/web/dashboards").await;
assert_eq!(status, StatusCode::OK);
assert_eq!(body.as_array().unwrap().len(), 1);
// Save adds a panel.
let (status, body) = send_json(
&app,
"PUT",
@@ -1253,13 +1102,11 @@ mod tests {
assert_eq!(panels.len(), 1);
assert_eq!(panels[0]["chart_type"], "line");
// Get by id reflects the saved panel.
let (status, body) =
get_json(&app, &format!("/api/settings/profiles/web/dashboards/{dashboard_id}")).await;
assert_eq!(status, StatusCode::OK);
assert_eq!(body["panels"].as_array().unwrap().len(), 1);
// Delete removes it.
let status = delete_request(&app, &format!("/api/settings/profiles/web/dashboards/{dashboard_id}")).await;
assert_eq!(status, StatusCode::NO_CONTENT);
let (status, body) = get_json(&app, "/api/settings/profiles/web/dashboards").await;
@@ -1308,8 +1155,7 @@ mod tests {
)
.await;
// Simulate a restart: reload the profile store from disk directly,
// independent of the running app/engine.
// Simulate a restart by reloading the profile store fresh from disk, independent of the running app/engine.
let settings_path = dir.path().join("settings.json");
let profiles_dir = {
let settings = settings_handle.lock().unwrap();