use crate::error::{NeofetchError, Result};
use std::fmt::Display;
#[derive(Debug, Clone)]
pub struct NetworkInfo {
pub interface_name: String,
pub ipv4_address: Option<String>,
pub ipv6_address: Option<String>,
pub mac_address: Option<String>,
pub is_up: bool,
}
impl Display for NetworkInfo {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let status = if self.is_up { "UP" } else { "DOWN" };
write!(f, "{} ({})", self.interface_name, status)?;
if let Some(ipv4) = &self.ipv4_address {
write!(f, " IPv4: {}", ipv4)?;
}
if let Some(mac) = &self.mac_address {
write!(f, " MAC: {}", mac)?;
}
Ok(())
}
}
#[cfg(windows)]
pub async fn get_network_info() -> Result<Vec<NetworkInfo>> {
use serde::Deserialize;
use crate::platform::wmi_query;
#[derive(Deserialize, Debug)]
#[serde(rename = "Win32_NetworkAdapterConfiguration")]
struct NetworkAdapter {
#[serde(rename = "Description")]
description: Option<String>,
#[serde(rename = "IPAddress")]
ip_address: Option<Vec<String>>,
#[serde(rename = "MACAddress")]
mac_address: Option<String>,
#[serde(rename = "IPEnabled")]
ip_enabled: Option<bool>,
}
let results: Vec<NetworkAdapter> = wmi_query()
.await
.map_err(|e| NeofetchError::wmi_error(format!("WMI query failed: {}", e)))?;
let mut interfaces = Vec::new();
for adapter in results {
if let Some(desc) = adapter.description {
let is_up = adapter.ip_enabled.unwrap_or(false);
let mut ipv4 = None;
let mut ipv6 = None;
if let Some(addresses) = adapter.ip_address {
for addr in addresses {
if addr.contains(':') {
ipv6 = Some(addr);
} else if addr.contains('.') {
ipv4 = Some(addr);
}
}
}
interfaces.push(NetworkInfo {
interface_name: desc,
ipv4_address: ipv4,
ipv6_address: ipv6,
mac_address: adapter.mac_address,
is_up,
});
}
}
Ok(interfaces)
}
#[cfg(unix)]
pub async fn get_network_info() -> Result<Vec<NetworkInfo>> {
use crate::utils::execute_command;
let mut interfaces = Vec::new();
#[cfg(target_os = "linux")]
{
if let Ok(output) = execute_command("ip", &["-brief", "addr", "show"]).await {
for line in output.lines() {
let parts: Vec<&str> = line.split_whitespace().collect();
if parts.len() >= 3 {
let name = parts[0].to_string();
let status = parts[1];
let is_up = status.contains("UP");
let mut ipv4 = None;
let mut ipv6 = None;
for addr in &parts[2..] {
let addr_clean = addr.trim_end_matches(',');
if let Some(ip) = addr_clean.split('/').next() {
if ip.contains(':') {
ipv6 = Some(ip.to_string());
} else if ip.contains('.') {
ipv4 = Some(ip.to_string());
}
}
}
interfaces.push(NetworkInfo {
interface_name: name,
ipv4_address: ipv4,
ipv6_address: ipv6,
mac_address: None,
is_up,
});
}
}
return Ok(interfaces);
}
}
if let Ok(output) = execute_command("ifconfig", &[] as &[&str]).await {
let mut current_interface: Option<NetworkInfo> = None;
for line in output.lines() {
if !line.starts_with(' ') && !line.starts_with('\t') {
if let Some(interface) = current_interface.take() {
interfaces.push(interface);
}
let parts: Vec<&str> = line.split(':').collect();
if let Some(name) = parts.first() {
let is_up = line.contains("UP");
current_interface = Some(NetworkInfo {
interface_name: name.trim().to_string(),
ipv4_address: None,
ipv6_address: None,
mac_address: None,
is_up,
});
}
} else if let Some(ref mut interface) = current_interface {
if line.contains("inet ") && !line.contains("inet6") {
if let Some(addr_start) = line.find("inet ") {
let addr_part = &line[addr_start + 5..];
if let Some(addr) = addr_part.split_whitespace().next() {
interface.ipv4_address = Some(addr.to_string());
}
}
} else if line.contains("inet6 ") {
if let Some(addr_start) = line.find("inet6 ") {
let addr_part = &line[addr_start + 6..];
if let Some(addr) = addr_part.split_whitespace().next() {
interface.ipv6_address = Some(addr.to_string());
}
}
} else if line.contains("ether ")
&& let Some(mac_start) = line.find("ether ")
{
let mac_part = &line[mac_start + 6..];
if let Some(mac) = mac_part.split_whitespace().next() {
interface.mac_address = Some(mac.to_string());
}
}
}
}
if let Some(interface) = current_interface {
interfaces.push(interface);
}
}
if interfaces.is_empty() {
return Err(NeofetchError::data_unavailable(
"No network interfaces found",
));
}
Ok(interfaces)
}
pub async fn get_active_interfaces() -> Result<Vec<NetworkInfo>> {
let all_interfaces = get_network_info().await?;
Ok(all_interfaces
.into_iter()
.filter(|iface| iface.is_up && iface.ipv4_address.is_some())
.collect())
}
#[cfg(test)]
mod tests {
use super::*;
#[tokio::test]
async fn test_get_network_info() {
let result = get_network_info().await;
assert!(result.is_ok() || result.is_err());
}
#[tokio::test]
async fn test_get_active_interfaces() {
let result = get_active_interfaces().await;
assert!(result.is_ok() || result.is_err());
}
}