Skip to main content

toole_core/
utils.rs

1pub fn current_hostname() -> String {
2    hostname::get()
3        .unwrap_or_default()
4        .to_string_lossy()
5        .to_string()
6}
7
8// renvoie la première IP v4 non-loopback (la plus fiable pour le filtrage self)
9pub 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
23// ID device : {hostname}-{suffixe base32}. Généré une seule fois puis persisté
24// pour rester unique même si deux OS partagent le même hostname.
25pub 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
47// 5 caractères en base32 Crockford (32 symboles, pas de chiffres ambigus)
48// je l'expose en pub pour pouvoir la tester unitairement dans le crate tests/
49pub 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
59// crée un pair manuel à partir d'une IP saisie par l'utilisateur : seul
60// l'IPv4 est accepté (le transfert QUIC est aujourd'hui mono-pile v4), et
61// je refuse les adresses de loopback. L'id est dérivé de l'IP pour rester
62// stable et distinct des ids de la découverte (hostname-suffixe)
63pub 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}