ostool-server 0.2.0

Server for managing development boards, serial sessions, and TFTP artifacts
use network_interface::{Addr, NetworkInterface, NetworkInterfaceConfig};

use crate::api::models::NetworkInterfaceSummary;

pub fn list_network_interfaces() -> anyhow::Result<Vec<NetworkInterfaceSummary>> {
    let mut interfaces = NetworkInterface::show()?
        .into_iter()
        .map(|interface| {
            let primary_ipv4 = interface.addr.iter().find_map(|addr| match addr {
                Addr::V4(v4) => Some(*v4),
                Addr::V6(_) => None,
            });
            let ipv4_addresses = interface
                .addr
                .iter()
                .filter_map(|addr| match addr {
                    Addr::V4(v4) => Some(v4.ip.to_string()),
                    Addr::V6(_) => None,
                })
                .collect::<Vec<_>>();
            let loopback = primary_ipv4.is_some_and(|v4| v4.ip.is_loopback());

            let label = if let Some(primary_ipv4) = primary_ipv4 {
                let netmask = primary_ipv4
                    .netmask
                    .map(|netmask| format!("/{netmask}"))
                    .unwrap_or_default();
                format!("{} ({}{})", interface.name, primary_ipv4.ip, netmask)
            } else {
                interface.name.clone()
            };

            NetworkInterfaceSummary {
                name: interface.name,
                label,
                ipv4_addresses,
                netmask: primary_ipv4
                    .and_then(|v4| v4.netmask)
                    .map(|netmask| netmask.to_string()),
                loopback,
            }
        })
        .collect::<Vec<_>>();

    interfaces.sort_by(|a, b| {
        a.loopback
            .cmp(&b.loopback)
            .then_with(|| a.name.cmp(&b.name))
    });
    Ok(interfaces)
}

pub fn default_non_loopback_interface_name() -> Option<String> {
    list_network_interfaces()
        .ok()?
        .into_iter()
        .find(|interface| !interface.loopback && !interface.ipv4_addresses.is_empty())
        .map(|interface| interface.name)
}