quant_eval/
fingerprint.rs1use blake3::Hasher;
4use serde::{Deserialize, Serialize};
5use std::env;
6use std::fs;
7
8#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
13pub struct MachineFingerprint {
14 fingerprint: String,
16}
17
18impl MachineFingerprint {
19 pub fn new() -> Self {
21 let mut hasher = Hasher::new();
22
23 if let Ok(hostname) = env::var("HOSTNAME").or_else(|_| env::var("HOST")) {
25 hasher.update(b"hostname:");
26 hasher.update(hostname.as_bytes());
27 }
28
29 if let Ok(user) = env::var("USER").or_else(|_| env::var("USERNAME")) {
31 hasher.update(b"user:");
32 hasher.update(user.as_bytes());
33 }
34
35 hasher.update(b"arch:");
37 hasher.update(std::env::consts::ARCH.as_bytes());
38
39 hasher.update(b"os:");
41 hasher.update(std::env::consts::OS.as_bytes());
42
43 hasher.update(b"cpus:");
45 let cpus = std::thread::available_parallelism()
46 .map(|n| n.get())
47 .unwrap_or(1);
48 hasher.update(&cpus.to_le_bytes());
49
50 if let Some(machine_id) = Self::machine_id() {
52 hasher.update(b"machine_id:");
53 hasher.update(machine_id.as_bytes());
54 }
55
56 let hash = hasher.finalize();
57 let fingerprint = hex::encode(hash.as_bytes());
58
59 Self { fingerprint }
60 }
61
62 pub fn from_hex(hex: &str) -> Self {
64 Self {
65 fingerprint: hex.to_string(),
66 }
67 }
68
69 pub fn as_str(&self) -> &str {
71 &self.fingerprint
72 }
73
74 fn machine_id() -> Option<String> {
76 let candidates = [
77 "/etc/machine-id",
78 "/var/lib/dbus/machine-id",
79 "/etc/arch-release",
80 ];
81
82 for path in candidates {
83 if let Ok(contents) = fs::read_to_string(path) {
84 let id = contents.trim().to_string();
85 if !id.is_empty() {
86 return Some(id);
87 }
88 }
89 }
90 None
91 }
92}
93
94mod hex {
96 const HEX_CHARS: &[u8; 16] = b"0123456789abcdef";
97
98 pub fn encode(data: impl AsRef<[u8]>) -> String {
99 let bytes = data.as_ref();
100 let mut s = String::with_capacity(bytes.len() * 2);
101 for &b in bytes {
102 s.push(HEX_CHARS[(b >> 4) as usize] as char);
103 s.push(HEX_CHARS[(b & 0xf) as usize] as char);
104 }
105 s
106 }
107}
108
109impl Default for MachineFingerprint {
110 fn default() -> Self {
111 Self::new()
112 }
113}
114
115impl std::fmt::Display for MachineFingerprint {
116 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
117 write!(f, "{}", self.fingerprint)
118 }
119}
120
121#[cfg(test)]
122mod tests {
123 use super::*;
124
125 #[test]
126 fn test_fingerprint_creation() {
127 let fp = MachineFingerprint::new();
128 assert!(!fp.fingerprint.is_empty());
129 assert_eq!(fp.fingerprint.len(), 64); }
131
132 #[test]
133 fn test_fingerprint_from_hex() {
134 let fp = MachineFingerprint::from_hex("00".repeat(64).as_str());
135 assert_eq!(fp.as_str(), "00".repeat(64).as_str());
136 }
137
138 #[test]
139 fn test_fingerprint_display() {
140 let fp = MachineFingerprint::from_hex("ab".repeat(32).as_str());
141 let display = format!("{}", fp);
142 assert_eq!(display, "ab".repeat(32).as_str());
143 }
144}