use sha2::{Digest, Sha256};
use std::path::Path;
#[must_use]
pub fn compute_instance_hash(data_dir: &Path) -> String {
let machine_id = get_machine_id().unwrap_or_else(get_fallback_id);
let path_str = data_dir.canonicalize().map_or_else(
|_| data_dir.to_string_lossy().to_string(),
|p| p.to_string_lossy().to_string(),
);
let mut hasher = Sha256::new();
hasher.update(b"velesdb-instance-v1:"); hasher.update(machine_id.as_bytes());
hasher.update(b":");
hasher.update(path_str.as_bytes());
hex::encode(hasher.finalize())
}
fn get_machine_id() -> Option<String> {
#[cfg(target_os = "linux")]
{
std::fs::read_to_string("/etc/machine-id")
.or_else(|_| std::fs::read_to_string("/var/lib/dbus/machine-id"))
.ok()
.map(|s| s.trim().to_string())
}
#[cfg(target_os = "macos")]
{
std::process::Command::new("ioreg")
.args(["-rd1", "-c", "IOPlatformExpertDevice"])
.output()
.ok()
.and_then(|output| {
let stdout = String::from_utf8_lossy(&output.stdout);
stdout
.lines()
.find(|line| line.contains("IOPlatformUUID"))
.and_then(|line| line.split('"').nth(3))
.map(std::string::ToString::to_string)
})
}
#[cfg(target_os = "windows")]
{
std::process::Command::new("reg")
.args([
"query",
"HKLM\\SOFTWARE\\Microsoft\\Cryptography",
"/v",
"MachineGuid",
])
.output()
.ok()
.and_then(|output| {
let stdout = String::from_utf8_lossy(&output.stdout);
stdout
.lines()
.find(|line| line.contains("MachineGuid"))
.and_then(|line| line.split_whitespace().last())
.map(std::string::ToString::to_string)
})
}
#[cfg(not(any(target_os = "linux", target_os = "macos", target_os = "windows")))]
{
None
}
}
fn get_fallback_id() -> String {
let hostname = hostname::get().map_or_else(
|_| "unknown".to_string(),
|h| h.to_string_lossy().to_string(),
);
let username = whoami::username();
format!("fallback:{hostname}:{username}")
}
#[cfg(test)]
#[path = "instance_id_tests.rs"]
mod tests;