1pub fn current_hostname() -> String {
2 hostname::get()
3 .unwrap_or_default()
4 .to_string_lossy()
5 .to_string()
6}
7
8pub fn local_ip() -> String {
10 if let Ok(ifaces) = if_addrs::get_if_addrs() {
11 for iface in ifaces {
12 if iface.is_loopback() {
13 continue;
14 }
15 if let if_addrs::IfAddr::V4(v4) = iface.addr {
16 return v4.ip.to_string();
17 }
18 }
19 }
20 "127.0.0.1".to_string()
21}
22
23pub fn device_id() -> String {
26 let Some(proj_dirs) = directories::ProjectDirs::from("com", "Tiligre Open Space", "Toole")
27 else {
28 return current_hostname();
29 };
30 let dir = proj_dirs.data_dir();
31 if std::fs::create_dir_all(dir).is_err() {
32 return current_hostname();
33 }
34
35 let file = dir.join("device_id");
36 if let Ok(existing) = std::fs::read_to_string(&file) {
37 if !existing.trim().is_empty() {
38 return existing.trim().to_string();
39 }
40 }
41
42 let id = format!("{}-{}", current_hostname(), short_suffix());
43 let _ = std::fs::write(&file, &id);
44 id
45}
46
47pub fn short_suffix() -> String {
50 const ALPHABET: &[u8] = b"0123456789ABCDEFGHJKMNPQRSTVWXYZ";
51 let bytes = uuid::Uuid::new_v4().as_bytes().to_owned();
52 let mut out = String::new();
53 for i in 0..5 {
54 out.push(ALPHABET[bytes[i] as usize & 0x1F] as char);
55 }
56 out
57}
58
59pub fn manual_peer(ip: &str) -> Option<crate::Peer> {
64 let ip: std::net::Ipv4Addr = ip.trim().parse().ok()?;
65 if !crate::firewall::is_manual_ipv4_allowed(ip) {
66 return None;
67 }
68 Some(crate::Peer {
69 id: format!("manual-{ip}"),
70 addr: ip.to_string(),
71 })
72}