//! Settings I/O — the ONLY place in `service/settings/` that may perform side effects. //! //! Side effects here include: reading/writing `.huskies/project.toml` and //! spawning the editor process via `std::process::Command`. //! All business logic, branching, and type definitions belong in `mod.rs`, //! `project.rs`, or `validate.rs`. use super::Error; use std::path::Path; /// Read the raw TOML content from `config_path`. /// /// Returns an empty string if the file does not exist yet, so callers can /// treat a missing config the same as an empty one. /// /// # Errors /// - [`Error::Io`] if the file exists but cannot be read. pub(super) fn read_config_toml(config_path: &Path) -> Result { if config_path.exists() { std::fs::read_to_string(config_path).map_err(|e| Error::Io(format!("Read config: {e}"))) } else { Ok(String::new()) } } /// Write `content` to `config_path`, creating parent directories as needed. /// /// # Errors /// - [`Error::Io`] if the directory cannot be created or the file write fails. pub(super) fn write_config_toml(config_path: &Path, content: &str) -> Result<(), Error> { if let Some(parent) = config_path.parent() { std::fs::create_dir_all(parent) .map_err(|e| Error::Io(format!("Create .huskies dir: {e}")))?; } std::fs::write(config_path, content).map_err(|e| Error::Io(format!("Write config: {e}"))) } /// Spawn the editor process with `file_ref` as the sole argument. /// /// Does not wait for the editor to exit — fire-and-forget so the UI remains /// responsive. /// /// # Errors /// - [`Error::SpawnError`] if the operating system cannot start the process /// (e.g. the editor binary is not on `$PATH`). pub(super) fn spawn_editor(editor_command: &str, file_ref: &str) -> Result<(), Error> { std::process::Command::new(editor_command) .arg(file_ref) .spawn() .map(|_| ()) .map_err(|e| Error::Spawn(format!("Failed to open editor: {e}"))) }