//! OS-conventional paths for client-side storage. use std::path::PathBuf; use directories::ProjectDirs; use thiserror::Error; #[derive(Debug, Error)] pub enum PathError { #[error("cannot determine OS data directory (is $HOME set?)")] 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 { ProjectDirs::from("", "", "doctate") .map(|dirs| dirs.data_local_dir().join("pending")) .ok_or(PathError::NoDataDir) } /// 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 { ProjectDirs::from("", "", "doctate") .map(|dirs| dirs.data_local_dir().join("cases")) .ok_or(PathError::NoDataDir) } /// 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 { ProjectDirs::from("", "", "doctate") .map(|dirs| dirs.data_local_dir().join("oneliners_cache.json")) .ok_or(PathError::NoDataDir) } /// 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 { ProjectDirs::from("", "", "doctate") .map(|dirs| dirs.data_local_dir().join("client.lock")) .ok_or(PathError::NoDataDir) }