speedtest-tui 0.1.1

A terminal-based network speed test tool with real-time gauges and graphs
use anyhow::Result;
use serde::{Deserialize, Serialize};
use sysinfo::Networks;

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct NetworkInterface {
    pub name: String,
    pub ip: String,
    pub mac: Option<String>,
    pub is_up: bool,
    pub received_bytes: u64,
    pub transmitted_bytes: u64,
}

/// List all network interfaces
pub fn list_interfaces() -> Result<Vec<NetworkInterface>> {
    let networks = Networks::new_with_refreshed_list();
    let mut interfaces = Vec::new();

    for (name, data) in &networks {
        // Get MAC address
        let mac = data.mac_address().to_string();
        let mac = if mac.is_empty() || mac == "00:00:00:00:00:00" {
            None
        } else {
            Some(mac)
        };

        // Try to get IP address for this interface
        let ip = get_interface_ip(name).unwrap_or_else(|| "N/A".to_string());

        interfaces.push(NetworkInterface {
            name: name.clone(),
            ip,
            mac,
            is_up: data.received() > 0 || data.transmitted() > 0,
            received_bytes: data.received(),
            transmitted_bytes: data.transmitted(),
        });
    }

    // Sort by name
    interfaces.sort_by(|a, b| a.name.cmp(&b.name));

    Ok(interfaces)
}

/// Get IP address for a specific interface
fn get_interface_ip(interface_name: &str) -> Option<String> {
    // Try to get the local IP and match it
    // This is a simplified implementation
    if let Ok(local_ip) = local_ip_address::local_ip() {
        // For the primary interface, return the local IP
        if interface_name.contains("en0")
            || interface_name.contains("eth0")
            || interface_name.contains("wlan0")
            || interface_name.contains("Wi-Fi")
            || interface_name.contains("Ethernet")
        {
            return Some(local_ip.to_string());
        }
    }

    // For loopback
    if interface_name.contains("lo") || interface_name.contains("Loopback") {
        return Some("127.0.0.1".to_string());
    }

    None
}

#[allow(dead_code)]
pub fn get_bandwidth_usage() -> Result<BandwidthUsage> {
    let networks = Networks::new_with_refreshed_list();

    let mut total_rx: u64 = 0;
    let mut total_tx: u64 = 0;

    for (_, data) in &networks {
        total_rx += data.received();
        total_tx += data.transmitted();
    }

    Ok(BandwidthUsage {
        received_bytes: total_rx,
        transmitted_bytes: total_tx,
    })
}

#[derive(Debug, Clone)]
#[allow(dead_code)]
pub struct BandwidthUsage {
    pub received_bytes: u64,
    pub transmitted_bytes: u64,
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_list_interfaces() {
        let result = list_interfaces();
        assert!(result.is_ok());
        let interfaces = result.unwrap();
        // Should have at least loopback
        assert!(!interfaces.is_empty());
    }

    #[test]
    fn test_bandwidth_usage() {
        let result = get_bandwidth_usage();
        assert!(result.is_ok());
    }
}