| 1 | use home::home_dir; |
| 2 | use once_cell::sync::OnceCell; |
| 3 | use std::{fs, path::PathBuf}; |
| 4 | |
| 5 | /// Global runtime configuration for the Proses daemon and CLI. |
| 6 | /// |
| 7 | /// All paths are rooted under `~/.proses/`. |
| 8 | pub struct Config { |
| 9 | /// Persistent process store: `~/.proses/store.json` |
| 10 | pub store_path: PathBuf, |
| 11 | /// Unix domain socket used for IPC: `~/.proses/proses.sock` |
| 12 | pub sock_path: PathBuf, |
| 13 | /// Daemon PID file: `~/.proses/daemon.pid` |
| 14 | pub pid_path: PathBuf, |
| 15 | /// Directory for per-process stdout/stderr logs: `~/.proses/logs/` |
| 16 | pub log_dir: PathBuf, |
| 17 | } |
| 18 | |
| 19 | static CONFIG: OnceCell<Config> = OnceCell::new(); |
| 20 | |
| 21 | /// Initialise the global [`Config`] singleton and create the necessary |
| 22 | /// directories on disk. |
| 23 | /// |
| 24 | /// Safe to call multiple times — subsequent calls are no-ops. |
| 25 | pub fn init() { |
| 26 | // If already initialised, nothing to do. |
| 27 | if CONFIG.get().is_some() { |
| 28 | return; |
| 29 | } |
| 30 | |
| 31 | let base_dir = home_dir() |
| 32 | .expect("Cannot determine the home directory") |
| 33 | .join(".proses"); |
| 34 | |
| 35 | let log_dir = base_dir.join("logs"); |
| 36 | |
| 37 | fs::create_dir_all(&base_dir).expect("Failed to create ~/.proses directory"); |
| 38 | fs::create_dir_all(&log_dir).expect("Failed to create ~/.proses/logs directory"); |
| 39 | |
| 40 | let config = Config { |
| 41 | store_path: base_dir.join("store.json"), |
| 42 | sock_path: base_dir.join("proses.sock"), |
| 43 | pid_path: base_dir.join("daemon.pid"), |
| 44 | log_dir, |
| 45 | }; |
| 46 | |
| 47 | // A concurrent caller may have beaten us to the set; that is fine — |
| 48 | // the value that wins is valid for our lifetime. |
| 49 | CONFIG.set(config).ok(); |
| 50 | } |
| 51 | |
| 52 | /// Return a reference to the global [`Config`]. |
| 53 | /// |
| 54 | /// # Panics |
| 55 | /// Panics if [`init`] has not been called yet. |
| 56 | #[inline] |
| 57 | pub fn get() -> &'static Config { |
| 58 | CONFIG |
| 59 | .get() |
| 60 | .expect("Config not initialised — call config::init() first") |
| 61 | } |