Skip to main content

netscli_core/ops/
network.rs

1use super::config::Ops;
2use crate::error::Result;
3use crate::{ArpEntry, InterfaceInfo, NetworkManager};
4
5impl Ops {
6    pub fn list_interfaces(&self) -> Vec<InterfaceInfo> {
7        NetworkManager::get_interfaces()
8    }
9
10    /// Discover services via mDNS/DNS-SD across a curated list of common
11    /// service types. Waits up to `timeout` for responses.
12    ///
13    /// Pass an empty `service_types` slice to use
14    /// [`crate::mdns::COMMON_SERVICE_TYPES`] as the default probe set.
15    #[cfg(feature = "mdns")]
16    pub async fn discover_mdns(
17        &self,
18        service_types: &[String],
19        timeout: std::time::Duration,
20    ) -> Result<Vec<crate::mdns::MdnsService>> {
21        // Clamp rather than reject: a browse is a "wait this long" request,
22        // not an addressing mistake, so trimming an over-long wait gives the
23        // caller results instead of an error. The engines are the authority
24        // on their own limits -- `Ops` used to pass this straight through,
25        // leaving the TUI as the only surface that bounded it.
26        let timeout = timeout.min(std::time::Duration::from_millis(crate::MAX_MDNS_TIMEOUT_MS));
27        if service_types.is_empty() {
28            crate::mdns::MdnsEngine::discover_common(timeout).await
29        } else {
30            let refs: Vec<&str> = service_types.iter().map(String::as_str).collect();
31            crate::mdns::MdnsEngine::discover(&refs, timeout).await
32        }
33    }
34
35    /// Read the local ARP/neighbour table.
36    ///
37    /// Async, and it moves the read to a blocking thread, because on Windows
38    /// and macOS this shells out to `arp` and waits on the child process.
39    /// Every other method on `Ops` is async, so a sync one here invited
40    /// exactly the bug it produced: three separate callers — the MCP tool
41    /// dispatcher, the Tauri command, and `DiscoverEngine` — all invoked it
42    /// straight from an async context and parked a runtime worker on
43    /// `waitpid` for the life of the subprocess.
44    ///
45    /// The MCP case was the sharp one. Its handlers are capped at 16
46    /// concurrent, and the read loop is itself a task on the same pool, so
47    /// sixteen `get_arp_table` calls could stall every worker — including
48    /// the one reading stdin. No further request would even be parsed, and
49    /// a cancellation could not get through.
50    pub async fn get_arp_table(&self) -> Result<Vec<ArpEntry>> {
51        tokio::task::spawn_blocking(NetworkManager::get_arp_table)
52            .await
53            .map_err(|e| crate::error::Error::Other(format!("ARP read task failed: {e}")))?
54    }
55}