Add process-wide Settings model (ticket 008)

Introduces src/settings.rs with a single, process-wide Settings struct
(colour scheme, CSS entries shaped for imported-vs-linked, and a
profiles placeholder for ticket 009), JSON-serialized via serde_json to
keep deps minimal. Default settings path is computed with the
directories crate (ProjectDirs::from("", "", "ottyl")) and overridable
via a new --settings-path CLI arg. Settings::load creates and persists
a default file on first run; Settings::save persists changes.

Wires a single Arc<Mutex<Settings>> into main() and threads it through
api::router as an additive ApiState field for tickets 010/011 to build
on.

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 12:42:53 +01:00
co-authored by Claude Sonnet 5
parent 1fb564d06b
commit 22647620fa
5 changed files with 388 additions and 3 deletions
+12 -2
View File
@@ -1,5 +1,8 @@
use std::sync::{Arc, Mutex};
use crate::engine::{EngineHandle, EngineMessage, EngineQuery, EngineResponse};
use crate::dto::{LogsParams, TimelineParams};
use crate::settings::Settings;
use axum::extract::{Query, State};
use axum::http::StatusCode;
use axum::response::{IntoResponse, Json};
@@ -8,13 +11,20 @@ use axum::Router;
use tokio::sync::oneshot;
use tower_http::services::{ServeDir, ServeFile};
/// Shared, process-wide settings handle. Cheap to clone; lock is only held
/// briefly since settings changes are rare (unlike the engine's hot path).
pub type SettingsHandle = Arc<Mutex<Settings>>;
#[derive(Clone)]
struct ApiState {
engine: EngineHandle,
// TODO(ticket 010/011): wire up settings/CSS/NavMenu endpoints using this.
#[allow(dead_code)]
settings: SettingsHandle,
}
pub fn router(engine: EngineHandle, static_dir: &str) -> Router {
let state = ApiState { engine };
pub fn router(engine: EngineHandle, static_dir: &str, settings: SettingsHandle) -> Router {
let state = ApiState { engine, settings };
let index_html = format!("{static_dir}/index.html");
let static_service = ServeDir::new(static_dir).fallback(ServeFile::new(index_html));