use std::path::PathBuf;
#[must_use]
pub fn socket_dir() -> PathBuf {
if let Some(dir) = std::env::var_os("KANSHOU_SOCKET_DIR") {
return PathBuf::from(dir);
}
if cfg!(target_os = "macos") {
let home = std::env::var_os("HOME").unwrap_or_default();
let mut p = PathBuf::from(home);
p.push("Library/Application Support/kanshou");
p
} else {
if let Some(xdg) = std::env::var_os("XDG_RUNTIME_DIR") {
let mut p = PathBuf::from(xdg);
p.push("kanshou");
return p;
}
let uid =
unsafe { libc_geteuid() }.unwrap_or(0);
PathBuf::from(format!("/tmp/kanshou-{uid}"))
}
}
#[must_use]
pub fn socket_path(app_name: &str, pid: u32) -> PathBuf {
let mut p = socket_dir();
p.push(format!("{app_name}-{pid}.sock"));
p
}
#[must_use]
pub fn parse_socket_name(name: &str) -> Option<(String, u32)> {
let stem = name.strip_suffix(".sock")?;
let dash = stem.rfind('-')?;
let (app, pid_str) = stem.split_at(dash);
let pid: u32 = pid_str.trim_start_matches('-').parse().ok()?;
Some((app.to_string(), pid))
}
unsafe fn libc_geteuid() -> Option<u32> {
#[cfg(unix)]
{
unsafe extern "C" {
fn getuid() -> u32;
}
Some(unsafe { getuid() })
}
#[cfg(not(unix))]
{
None
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn socket_dir_env_override_wins() {
unsafe { std::env::set_var("KANSHOU_SOCKET_DIR", "/tmp/kanshou-test-override") };
assert_eq!(
socket_dir(),
std::path::PathBuf::from("/tmp/kanshou-test-override"),
"KANSHOU_SOCKET_DIR must take precedence for hermetic tests"
);
unsafe { std::env::remove_var("KANSHOU_SOCKET_DIR") };
}
#[test]
fn socket_path_format() {
let p = socket_path("mado", 12345);
assert!(p.to_string_lossy().ends_with("mado-12345.sock"));
}
#[test]
fn parse_basic() {
assert_eq!(
parse_socket_name("mado-12345.sock"),
Some(("mado".into(), 12345))
);
}
#[test]
fn parse_dashed_app() {
assert_eq!(
parse_socket_name("blackmatter-cli-99.sock"),
Some(("blackmatter-cli".into(), 99))
);
}
#[test]
fn parse_rejects_non_sock() {
assert_eq!(parse_socket_name("mado-12345.log"), None);
}
#[test]
fn parse_rejects_no_pid() {
assert_eq!(parse_socket_name("mado.sock"), None);
}
#[test]
fn parse_rejects_bad_pid() {
assert_eq!(parse_socket_name("mado-abc.sock"), None);
}
}