use std::path::{Path, PathBuf};
pub fn per_host_path(db_path: &Path) -> PathBuf {
let Some(host) = host_discriminator() else {
return db_path.to_path_buf();
};
tagged(db_path, &host)
}
pub(crate) fn tagged(db_path: &Path, host: &str) -> PathBuf {
let Some(name) = db_path
.file_name()
.map(|s| s.to_string_lossy().into_owned())
else {
return db_path.to_path_buf();
};
let tag = format!(".h-{host}");
if name.ends_with(&tag) || name.contains(&format!("{tag}.")) {
return db_path.to_path_buf();
}
let new_name = match name.rsplit_once('.') {
Some((stem, ext)) if !stem.is_empty() => format!("{stem}{tag}.{ext}"),
_ => format!("{name}{tag}"),
};
db_path.with_file_name(new_name)
}
pub(crate) fn host_discriminator() -> Option<String> {
let raw = hostname_raw()?;
let mut s: String = raw
.trim()
.chars()
.map(|c| {
if c.is_ascii_alphanumeric() {
c.to_ascii_lowercase()
} else {
'-'
}
})
.collect();
s.truncate(24);
let s = s.trim_matches('-');
(!s.is_empty()).then(|| s.to_string())
}
#[cfg(unix)]
fn hostname_raw() -> Option<String> {
let mut buf = [0u8; 256];
if unsafe { libc::gethostname(buf.as_mut_ptr().cast(), buf.len()) } != 0 {
return None;
}
let len = buf.iter().position(|&b| b == 0).unwrap_or(buf.len());
String::from_utf8(buf[..len].to_vec()).ok()
}
#[cfg(windows)]
fn hostname_raw() -> Option<String> {
if let Ok(host) = std::env::var("COMPUTERNAME") {
if !host.trim().is_empty() {
return Some(host);
}
}
#[link(name = "kernel32")]
unsafe extern "system" {
fn GetComputerNameW(buffer: *mut u16, size: *mut u32) -> i32;
}
let mut buffer = [0u16; 256];
let mut size = buffer.len() as u32;
if unsafe { GetComputerNameW(buffer.as_mut_ptr(), &mut size) } == 0 || size == 0 {
return None;
}
String::from_utf16(&buffer[..size as usize]).ok()
}
#[cfg(not(any(unix, windows)))]
fn hostname_raw() -> Option<String> {
None
}