use std::ffi::OsStr;
use std::path::PathBuf;
const SOCKET_NAME: &str = "daemon.sock";
const FALLBACK_BASE: &str = "~/.cache/hallouminate";
pub fn daemon_socket_path() -> PathBuf {
if let Some(explicit) = std::env::var_os("HALLOUMINATE_SOCKET")
&& !explicit.is_empty()
{
return PathBuf::from(explicit);
}
if let Some(runtime) = std::env::var_os("XDG_RUNTIME_DIR")
&& !runtime.is_empty()
{
let mut p = PathBuf::from(runtime);
p.push("hallouminate");
p.push(SOCKET_NAME);
return p;
}
let base = shellexpand::tilde(FALLBACK_BASE).into_owned();
PathBuf::from(base).join(SOCKET_NAME)
}
pub(crate) fn sibling_socket_path() -> Option<PathBuf> {
compute_sibling_socket_path(
std::env::var_os("HALLOUMINATE_SOCKET").as_deref(),
std::env::var_os("XDG_RUNTIME_DIR").as_deref(),
rustix::process::geteuid().as_raw(),
)
}
fn compute_sibling_socket_path(
explicit_socket: Option<&OsStr>,
xdg_runtime_dir: Option<&OsStr>,
euid: u32,
) -> Option<PathBuf> {
if explicit_socket.is_some_and(|v| !v.is_empty()) {
return None;
}
let cache_candidate =
PathBuf::from(shellexpand::tilde(FALLBACK_BASE).into_owned()).join(SOCKET_NAME);
let (primary, sibling) = match xdg_runtime_dir.filter(|v| !v.is_empty()) {
Some(runtime) => {
let primary = PathBuf::from(runtime)
.join("hallouminate")
.join(SOCKET_NAME);
(primary, cache_candidate)
}
None => {
let runtime = PathBuf::from(format!("/run/user/{euid}"))
.join("hallouminate")
.join(SOCKET_NAME);
(cache_candidate, runtime)
}
};
(sibling != primary).then_some(sibling)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn socket_path_is_named_daemon_sock() {
let p = daemon_socket_path();
assert_eq!(p.file_name().and_then(|s| s.to_str()), Some(SOCKET_NAME));
}
#[test]
fn sibling_is_none_when_explicit_socket_override_set() {
let sibling = compute_sibling_socket_path(
Some(OsStr::new("/tmp/explicit.sock")),
Some(OsStr::new("/run/user/1000")),
1000,
);
assert_eq!(sibling, None);
}
#[test]
fn sibling_is_cache_candidate_when_xdg_runtime_dir_set() {
let sibling = compute_sibling_socket_path(None, Some(OsStr::new("/run/user/1000")), 1000);
let expected =
PathBuf::from(shellexpand::tilde(FALLBACK_BASE).into_owned()).join(SOCKET_NAME);
assert_eq!(sibling, Some(expected));
}
#[test]
fn sibling_is_conventional_run_user_path_when_xdg_runtime_dir_unset() {
let sibling = compute_sibling_socket_path(None, None, 1000);
assert_eq!(
sibling,
Some(PathBuf::from("/run/user/1000/hallouminate/daemon.sock"))
);
}
#[test]
fn empty_xdg_runtime_dir_treated_as_unset() {
let sibling = compute_sibling_socket_path(None, Some(OsStr::new("")), 1000);
assert_eq!(
sibling,
Some(PathBuf::from("/run/user/1000/hallouminate/daemon.sock"))
);
}
#[test]
fn sibling_coincident_with_primary_collapses_to_none() {
let cache = PathBuf::from(shellexpand::tilde(FALLBACK_BASE).into_owned());
let runtime = cache.parent().expect("FALLBACK_BASE has a parent");
let sibling = compute_sibling_socket_path(None, Some(runtime.as_os_str()), 1000);
assert_eq!(
sibling, None,
"coincident primary/sibling collapses to None"
);
}
}