use anyhow::{Context, Result}; use std::path::PathBuf; const LABEL: &str = "xyz.spacesh.daemon"; fn plist_path() -> Result { let home = dirs::home_dir().context("no home")?; Ok(home.join("Library").join("LaunchAgents").join(format!("{LABEL}.plist"))) } /// Render the launchd plist. `run_at_load` defaults to false in this slice. pub fn render_plist(exe: &str, run_at_load: bool) -> String { format!( r#" Label {LABEL} ProgramArguments {exe} KeepAlive RunAtLoad <{run_at_load}/> "#, run_at_load = if run_at_load { "true" } else { "false" } ) } pub fn install_agent() -> Result<()> { let exe = std::env::current_exe()?; let plist = render_plist(&exe.to_string_lossy(), false); let path = plist_path()?; if let Some(parent) = path.parent() { std::fs::create_dir_all(parent)?; } std::fs::write(&path, plist)?; // Load it (best-effort; ignore "already loaded"). let _ = std::process::Command::new("launchctl") .arg("load") .arg(&path) .status(); Ok(()) } #[cfg(test)] mod tests { use super::*; #[test] fn plist_has_label_keepalive_and_exe() { let p = render_plist("/usr/local/bin/spaceshd", false); assert!(p.contains("xyz.spacesh.daemon")); assert!(p.contains("/usr/local/bin/spaceshd")); assert!(p.contains("KeepAlive\n ")); assert!(p.contains("RunAtLoad\n ")); } #[test] fn run_at_load_toggles() { assert!(render_plist("x", true).contains("RunAtLoad\n ")); } }