atomcode_telemetry/
identity.rs1use anyhow::{Context, Result};
4use std::env;
5use std::fs;
6use std::path::{Path, PathBuf};
7use uuid::Uuid;
8
9pub fn load_or_create(atomcode_dir: &Path) -> Result<Uuid> {
10 let path = atomcode_dir.join("device_id");
11 match fs::read_to_string(&path) {
12 Ok(s) => Uuid::parse_str(s.trim())
13 .with_context(|| format!("device_id file corrupt at {}", path.display())),
14 Err(e) if e.kind() == std::io::ErrorKind::NotFound => {
15 fs::create_dir_all(atomcode_dir)
16 .with_context(|| format!("creating {}", atomcode_dir.display()))?;
17 let id = Uuid::new_v4();
18 fs::write(&path, id.to_string())
19 .with_context(|| format!("writing {}", path.display()))?;
20 Ok(id)
21 }
22 Err(e) => Err(e).with_context(|| format!("reading {}", path.display())),
23 }
24}
25
26pub fn real_home_dir() -> Option<PathBuf> {
35 if let Ok(sudo_user) = env::var("SUDO_USER") {
37 if cfg!(target_os = "macos") {
41 return Some(PathBuf::from("/Users").join(sudo_user));
42 } else if cfg!(target_os = "linux") {
43 if sudo_user == "root" {
44 return Some(PathBuf::from("/root"));
45 }
46 return Some(PathBuf::from("/home").join(sudo_user));
47 }
48 }
49
50 dirs::home_dir()
52}
53
54pub fn default_atomcode_dir() -> PathBuf {
63 if let Some(p) = env::var("ATOMCODE_HOME").ok().filter(|s| !s.is_empty()) {
64 PathBuf::from(p)
65 } else {
66 real_home_dir().unwrap_or_else(|| PathBuf::from(".")).join(".atomcode")
67 }
68}
69
70#[cfg(test)]
71mod tests {
72 use super::*;
73 use tempfile::TempDir;
74
75 #[test]
76 fn creates_on_first_call_then_reads_same_id() {
77 let dir = TempDir::new().unwrap();
78 let id1 = load_or_create(dir.path()).unwrap();
79 let id2 = load_or_create(dir.path()).unwrap();
80 assert_eq!(id1, id2);
81 assert!(dir.path().join("device_id").exists());
82 }
83
84 #[test]
85 fn rejects_corrupt_file() {
86 let dir = TempDir::new().unwrap();
87 std::fs::write(dir.path().join("device_id"), "not-a-uuid").unwrap();
88 assert!(load_or_create(dir.path()).is_err());
89 }
90
91 #[test]
92 fn trims_whitespace_in_file() {
93 let dir = TempDir::new().unwrap();
94 let id = Uuid::new_v4();
95 std::fs::write(dir.path().join("device_id"), format!("{}\n\n", id)).unwrap();
96 let got = load_or_create(dir.path()).unwrap();
97 assert_eq!(id, got);
98 }
99}