//! OS-conventional paths for client-side storage. use std::path::PathBuf; use directories::ProjectDirs; use thiserror::Error; /// Directory name for pending (not-yet-uploaded) recordings inside the /// data-local root. const PENDING_DIR_NAME: &str = "pending"; /// Directory name for per-case marker files inside the data-local root. const CASES_DIR_NAME: &str = "cases"; /// File name for the oneliners snapshot cache inside the data-local root. const SNAPSHOT_CACHE_FILENAME: &str = "oneliners_cache.json"; /// File name for the single-instance advisory lock inside the data-local /// root. const LOCK_FILENAME: &str = "client.lock"; #[derive(Debug, Error)] pub enum PathError { #[error("cannot determine OS data directory (is $HOME set?)")] NoDataDir, } /// Resolve `{data_local_dir}/{rel}` under the doctate project scope, or /// error if the OS won't give us a data directory at all. Every public /// path helper here delegates to this so the `ProjectDirs::from(…)` /// triple stays in one place. fn project_path(rel: &str) -> Result { ProjectDirs::from("", "", "doctate") .map(|dirs| dirs.data_local_dir().join(rel)) .ok_or(PathError::NoDataDir) } /// Directory where recordings wait for upload. Persists across app /// restarts and crashes. /// Linux: `~/.local/share/doctate/pending/` /// Windows: `%LOCALAPPDATA%\doctate\pending\` /// macOS: `~/Library/Application Support/doctate/pending/` pub fn pending_dir() -> Result { project_path(PENDING_DIR_NAME) } /// Directory holding one JSON marker per known case (locally created /// and/or server-synced). Persists across app restarts. /// Linux: `~/.local/share/doctate/cases/` pub fn cases_dir() -> Result { project_path(CASES_DIR_NAME) } /// File path for the last-successful `/api/oneliners` response. Feeds /// the UI at cold start before the first live poll completes. pub fn snapshot_cache_path() -> Result { project_path(SNAPSHOT_CACHE_FILENAME) } /// File path for the single-instance advisory lock. Held by the running /// process for its entire lifetime; a second launch attempting to lock /// the same file gets `WouldBlock` and exits cleanly. pub fn lock_file_path() -> Result { project_path(LOCK_FILENAME) }