telemetry_kit/
user.rs

1//! User identification utilities
2
3use crate::error::{Result, TelemetryError};
4use machine_uid;
5use sha2::{Digest, Sha256};
6
7const SALT: &str = "telemetry-kit-v1";
8
9/// Generate an anonymous user ID based on machine ID
10///
11/// Format: `client_<first_64_chars_of_hash>`
12///
13/// This creates a stable, anonymous identifier that:
14/// - Persists across runs on the same machine
15/// - Cannot be reverse-engineered to get the machine ID
16/// - Does not contain any PII
17pub fn generate_user_id() -> Result<String> {
18    let machine_id = machine_uid::get()
19        .map_err(|e| TelemetryError::MachineId(format!("Failed to get machine ID: {}", e)))?;
20
21    let mut hasher = Sha256::new();
22    hasher.update(machine_id.as_bytes());
23    hasher.update(SALT.as_bytes());
24    let hash = hasher.finalize();
25
26    let hex_hash = hex::encode(hash);
27    Ok(format!("client_{}", &hex_hash[..64]))
28}
29
30/// Generate a session ID
31///
32/// Format: `sess_<uuid>`
33///
34/// Sessions are unique per process run and help group events from a single execution.
35pub fn generate_session_id() -> String {
36    let uuid = uuid::Uuid::new_v4();
37    format!("sess_{}", uuid.simple())
38}
39
40#[cfg(test)]
41mod tests {
42    use super::*;
43
44    #[test]
45    fn test_user_id_generation() {
46        let user_id = generate_user_id().unwrap();
47        assert!(user_id.starts_with("client_"));
48        assert_eq!(user_id.len(), 7 + 64); // "client_" + 64 hex chars
49    }
50
51    #[test]
52    fn test_user_id_stability() {
53        // User ID should be the same across calls
54        let id1 = generate_user_id().unwrap();
55        let id2 = generate_user_id().unwrap();
56        assert_eq!(id1, id2);
57    }
58
59    #[test]
60    fn test_session_id_generation() {
61        let session_id = generate_session_id();
62        assert!(session_id.starts_with("sess_"));
63    }
64
65    #[test]
66    fn test_session_id_uniqueness() {
67        // Session IDs should be different
68        let id1 = generate_session_id();
69        let id2 = generate_session_id();
70        assert_ne!(id1, id2);
71    }
72}