Skip to main content

sysinfo/common/
network.rs

1// Take a look at the license at the top of the repository in the LICENSE file.
2
3use std::collections::HashMap;
4use std::fmt;
5use std::net::{AddrParseError, IpAddr};
6use std::num::ParseIntError;
7use std::str::FromStr;
8
9use crate::{NetworkDataInner, NetworksInner};
10
11/// Interacting with network interfaces.
12///
13/// ```no_run
14/// use sysinfo::Networks;
15///
16/// let networks = Networks::new_with_refreshed_list();
17/// for (interface_name, network) in &networks {
18///     println!("[{interface_name}]: {network:?}");
19/// }
20/// ```
21pub struct Networks {
22    pub(crate) inner: NetworksInner,
23}
24
25impl<'a> IntoIterator for &'a Networks {
26    type Item = (&'a String, &'a NetworkData);
27    type IntoIter = std::collections::hash_map::Iter<'a, String, NetworkData>;
28
29    fn into_iter(self) -> Self::IntoIter {
30        self.iter()
31    }
32}
33
34impl Default for Networks {
35    fn default() -> Self {
36        Networks::new()
37    }
38}
39
40impl Networks {
41    /// Creates a new empty [`Networks`][crate::Networks] type.
42    ///
43    /// If you want it to be filled directly, take a look at [`Networks::new_with_refreshed_list`].
44    ///
45    /// ```no_run
46    /// use sysinfo::Networks;
47    ///
48    /// let mut networks = Networks::new();
49    /// networks.refresh(true);
50    /// for (interface_name, network) in &networks {
51    ///     println!("[{interface_name}]: {network:?}");
52    /// }
53    /// ```
54    pub fn new() -> Self {
55        Self {
56            inner: NetworksInner::new(),
57        }
58    }
59
60    /// Creates a new [`Networks`][crate::Networks] type with the network interfaces
61    /// list loaded.
62    ///
63    /// ```no_run
64    /// use sysinfo::Networks;
65    ///
66    /// let networks = Networks::new_with_refreshed_list();
67    /// for network in &networks {
68    ///     println!("{network:?}");
69    /// }
70    /// ```
71    pub fn new_with_refreshed_list() -> Self {
72        let mut networks = Self::new();
73        networks.refresh(false);
74        networks
75    }
76
77    /// Returns the network interfaces map.
78    ///
79    /// ```no_run
80    /// use sysinfo::Networks;
81    ///
82    /// let networks = Networks::new_with_refreshed_list();
83    /// for network in networks.list() {
84    ///     println!("{network:?}");
85    /// }
86    /// ```
87    pub fn list(&self) -> &HashMap<String, NetworkData> {
88        self.inner.list()
89    }
90
91    /// Refreshes the network interfaces.
92    ///
93    /// ```no_run
94    /// use sysinfo::Networks;
95    ///
96    /// let mut networks = Networks::new_with_refreshed_list();
97    /// // Wait some time...? Then refresh the data of each network.
98    /// networks.refresh(true);
99    /// ```
100    pub fn refresh(&mut self, remove_not_listed_interfaces: bool) {
101        self.inner.refresh(remove_not_listed_interfaces)
102    }
103}
104
105impl std::ops::Deref for Networks {
106    type Target = HashMap<String, NetworkData>;
107
108    fn deref(&self) -> &Self::Target {
109        self.list()
110    }
111}
112
113/// Getting volume of received and transmitted data.
114///
115/// ```no_run
116/// use sysinfo::Networks;
117///
118/// let networks = Networks::new_with_refreshed_list();
119/// for (interface_name, network) in &networks {
120///     println!("[{interface_name}] {network:?}");
121/// }
122/// ```
123pub struct NetworkData {
124    pub(crate) inner: NetworkDataInner,
125}
126
127impl NetworkData {
128    /// Returns the number of received bytes since the last refresh.
129    ///
130    /// If you want the total number of bytes received, take a look at the
131    /// [`total_received`](NetworkData::total_received) method.
132    ///
133    /// ```no_run
134    /// use sysinfo::Networks;
135    /// use std::{thread, time};
136    ///
137    /// let mut networks = Networks::new_with_refreshed_list();
138    /// // Waiting a bit to get data from network...
139    /// thread::sleep(time::Duration::from_millis(10));
140    /// // Refreshing again to generate diff.
141    /// networks.refresh(true);
142    ///
143    /// for (interface_name, network) in &networks {
144    ///     println!("in: {} B", network.received());
145    /// }
146    /// ```
147    pub fn received(&self) -> u64 {
148        self.inner.received()
149    }
150
151    /// Returns the total number of received bytes.
152    ///
153    /// If you want the amount of received bytes since the last refresh, take a look at the
154    /// [`received`](NetworkData::received) method.
155    ///
156    /// ```no_run
157    /// use sysinfo::Networks;
158    ///
159    /// let networks = Networks::new_with_refreshed_list();
160    /// for (interface_name, network) in &networks {
161    ///     println!("in: {} B", network.total_received());
162    /// }
163    /// ```
164    pub fn total_received(&self) -> u64 {
165        self.inner.total_received()
166    }
167
168    /// Returns the number of transmitted bytes since the last refresh.
169    ///
170    /// If you want the total number of bytes transmitted, take a look at the
171    /// [`total_transmitted`](NetworkData::total_transmitted) method.
172    ///
173    /// ```no_run
174    /// use sysinfo::Networks;
175    /// use std::{thread, time};
176    ///
177    /// let mut networks = Networks::new_with_refreshed_list();
178    /// // Waiting a bit to get data from network...
179    /// thread::sleep(time::Duration::from_millis(10));
180    /// // Refreshing again to generate diff.
181    /// networks.refresh(true);
182    ///
183    /// for (interface_name, network) in &networks {
184    ///     println!("out: {} B", network.transmitted());
185    /// }
186    /// ```
187    pub fn transmitted(&self) -> u64 {
188        self.inner.transmitted()
189    }
190
191    /// Returns the total number of transmitted bytes.
192    ///
193    /// If you want the amount of transmitted bytes since the last refresh, take a look at the
194    /// [`transmitted`](NetworkData::transmitted) method.
195    ///
196    /// ```no_run
197    /// use sysinfo::Networks;
198    ///
199    /// let networks = Networks::new_with_refreshed_list();
200    /// for (interface_name, network) in &networks {
201    ///     println!("out: {} B", network.total_transmitted());
202    /// }
203    /// ```
204    pub fn total_transmitted(&self) -> u64 {
205        self.inner.total_transmitted()
206    }
207
208    /// Returns the number of incoming packets since the last refresh.
209    ///
210    /// If you want the total number of packets received, take a look at the
211    /// [`total_packets_received`](NetworkData::total_packets_received) method.
212    ///
213    /// ```no_run
214    /// use sysinfo::Networks;
215    /// use std::{thread, time};
216    ///
217    /// let mut networks = Networks::new_with_refreshed_list();
218    /// // Waiting a bit to get data from network...
219    /// thread::sleep(time::Duration::from_millis(10));
220    /// // Refreshing again to generate diff.
221    /// networks.refresh(true);
222    ///
223    /// for (interface_name, network) in &networks {
224    ///     println!("in: {}", network.packets_received());
225    /// }
226    /// ```
227    pub fn packets_received(&self) -> u64 {
228        self.inner.packets_received()
229    }
230
231    /// Returns the total number of incoming packets.
232    ///
233    /// If you want the amount of received packets since the last refresh, take a look at the
234    /// [`packets_received`](NetworkData::packets_received) method.
235    ///
236    /// ```no_run
237    /// use sysinfo::Networks;
238    ///
239    /// let networks = Networks::new_with_refreshed_list();
240    /// for (interface_name, network) in &networks {
241    ///     println!("in: {}", network.total_packets_received());
242    /// }
243    /// ```
244    pub fn total_packets_received(&self) -> u64 {
245        self.inner.total_packets_received()
246    }
247
248    /// Returns the number of outcoming packets since the last refresh.
249    ///
250    /// If you want the total number of packets transmitted, take a look at the
251    /// [`total_packets_transmitted`](NetworkData::total_packets_transmitted) method.
252    ///
253    /// ```no_run
254    /// use sysinfo::Networks;
255    /// use std::{thread, time};
256    ///
257    /// let mut networks = Networks::new_with_refreshed_list();
258    /// // Waiting a bit to get data from network...
259    /// thread::sleep(time::Duration::from_millis(10));
260    /// // Refreshing again to generate diff.
261    /// networks.refresh(true);
262    ///
263    /// for (interface_name, network) in &networks {
264    ///     println!("out: {}", network.packets_transmitted());
265    /// }
266    /// ```
267    pub fn packets_transmitted(&self) -> u64 {
268        self.inner.packets_transmitted()
269    }
270
271    /// Returns the total number of outcoming packets.
272    ///
273    /// If you want the amount of transmitted packets since the last refresh, take a look at the
274    /// [`packets_transmitted`](NetworkData::packets_transmitted) method.
275    ///
276    /// ```no_run
277    /// use sysinfo::Networks;
278    ///
279    /// let networks = Networks::new_with_refreshed_list();
280    /// for (interface_name, network) in &networks {
281    ///     println!("out: {}", network.total_packets_transmitted());
282    /// }
283    /// ```
284    pub fn total_packets_transmitted(&self) -> u64 {
285        self.inner.total_packets_transmitted()
286    }
287
288    /// Returns the number of incoming errors since the last refresh.
289    ///
290    /// If you want the total number of errors on received packets, take a look at the
291    /// [`total_errors_on_received`](NetworkData::total_errors_on_received) method.
292    ///
293    /// ```no_run
294    /// use sysinfo::Networks;
295    /// use std::{thread, time};
296    ///
297    /// let mut networks = Networks::new_with_refreshed_list();
298    /// // Waiting a bit to get data from network...
299    /// thread::sleep(time::Duration::from_millis(10));
300    /// // Refreshing again to generate diff.
301    /// networks.refresh(true);
302    ///
303    /// for (interface_name, network) in &networks {
304    ///     println!("in: {}", network.errors_on_received());
305    /// }
306    /// ```
307    pub fn errors_on_received(&self) -> u64 {
308        self.inner.errors_on_received()
309    }
310
311    /// Returns the total number of incoming errors.
312    ///
313    /// If you want the amount of errors on received packets since the last refresh, take a look at
314    /// the [`errors_on_received`](NetworkData::errors_on_received) method.
315    ///
316    /// ```no_run
317    /// use sysinfo::Networks;
318    ///
319    /// let networks = Networks::new_with_refreshed_list();
320    /// for (interface_name, network) in &networks {
321    ///     println!("in: {}", network.total_errors_on_received());
322    /// }
323    /// ```
324    pub fn total_errors_on_received(&self) -> u64 {
325        self.inner.total_errors_on_received()
326    }
327
328    /// Returns the number of outcoming errors since the last refresh.
329    ///
330    /// If you want the total number of errors on transmitted packets, take a look at the
331    /// [`total_errors_on_transmitted`](NetworkData::total_errors_on_transmitted) method.
332    ///
333    /// ```no_run
334    /// use sysinfo::Networks;
335    /// use std::{thread, time};
336    ///
337    /// let mut networks = Networks::new_with_refreshed_list();
338    /// // Waiting a bit to get data from network...
339    /// thread::sleep(time::Duration::from_millis(10));
340    /// // Refreshing again to generate diff.
341    /// networks.refresh(true);
342    ///
343    /// for (interface_name, network) in &networks {
344    ///     println!("out: {}", network.errors_on_transmitted());
345    /// }
346    /// ```
347    pub fn errors_on_transmitted(&self) -> u64 {
348        self.inner.errors_on_transmitted()
349    }
350
351    /// Returns the total number of outcoming errors.
352    ///
353    /// If you want the amount of errors on transmitted packets since the last refresh, take a look at
354    /// the [`errors_on_transmitted`](NetworkData::errors_on_transmitted) method.
355    ///
356    /// ```no_run
357    /// use sysinfo::Networks;
358    ///
359    /// let networks = Networks::new_with_refreshed_list();
360    /// for (interface_name, network) in &networks {
361    ///     println!("out: {}", network.total_errors_on_transmitted());
362    /// }
363    /// ```
364    pub fn total_errors_on_transmitted(&self) -> u64 {
365        self.inner.total_errors_on_transmitted()
366    }
367
368    /// Returns the MAC address associated to current interface.
369    ///
370    /// ```no_run
371    /// use sysinfo::Networks;
372    ///
373    /// let mut networks = Networks::new_with_refreshed_list();
374    /// for (interface_name, network) in &networks {
375    ///     println!("MAC address: {}", network.mac_address());
376    /// }
377    /// ```
378    pub fn mac_address(&self) -> MacAddr {
379        self.inner.mac_address()
380    }
381
382    /// Returns the Ip Networks associated to current interface.
383    ///
384    /// ```no_run
385    /// use sysinfo::Networks;
386    ///
387    /// let mut networks = Networks::new_with_refreshed_list();
388    /// for (interface_name, network) in &networks {
389    ///     println!("Ip Networks: {:?}", network.ip_networks());
390    /// }
391    /// ```
392    pub fn ip_networks(&self) -> &[IpNetwork] {
393        self.inner.ip_networks()
394    }
395
396    /// Returns the Maximum Transfer Unit (MTU) of the interface.
397    ///
398    /// ```no_run
399    /// use sysinfo::Networks;
400    ///
401    /// let mut networks = Networks::new_with_refreshed_list();
402    /// for (interface_name, network) in &networks {
403    ///     println!("mtu: {}", network.mtu());
404    /// }
405    /// ```
406    pub fn mtu(&self) -> u64 {
407        self.inner.mtu()
408    }
409
410    /// Returns the operational state of the interface.
411    ///
412    /// The operational state indicates whether the interface is able to pass packets or not.
413    /// For details on the individual states see the documentation of the [`InterfaceOperationalState`] enum and its variants.
414    ///
415    /// ```no_run
416    /// use sysinfo::Networks;
417    ///
418    /// let mut networks = Networks::new_with_refreshed_list();
419    /// for (interface_name, network) in &networks {
420    ///     println!("operational state: {}", network.operational_state());
421    /// }
422    /// ```
423    pub fn operational_state(&self) -> InterfaceOperationalState {
424        self.inner.operational_state()
425    }
426}
427
428/// MAC address for network interface.
429///
430/// It is returned by [`NetworkData::mac_address`][crate::NetworkData::mac_address].
431#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq, PartialOrd, Ord)]
432#[cfg_attr(feature = "serde", derive(serde::Deserialize))]
433pub struct MacAddr(pub [u8; 6]);
434
435impl MacAddr {
436    /// A `MacAddr` with all bytes set to `0`.
437    pub const UNSPECIFIED: Self = MacAddr([0; 6]);
438
439    /// Checks if this `MacAddr` has all bytes equal to `0`.
440    pub fn is_unspecified(&self) -> bool {
441        self == &MacAddr::UNSPECIFIED
442    }
443}
444
445impl fmt::Display for MacAddr {
446    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
447        let data = &self.0;
448        write!(
449            f,
450            "{:02x}:{:02x}:{:02x}:{:02x}:{:02x}:{:02x}",
451            data[0], data[1], data[2], data[3], data[4], data[5],
452        )
453    }
454}
455
456/// Error type returned from `MacAddr::from_str` implementation.
457#[derive(Debug, Clone, PartialEq, Eq)]
458pub enum MacAddrFromStrError {
459    /// A number is not in hexadecimal format.
460    IntError(ParseIntError),
461    /// Input is not of format `{02X}:{02X}:{02X}:{02X}:{02X}:{02X}`.
462    InvalidAddrFormat,
463}
464
465impl FromStr for MacAddr {
466    type Err = MacAddrFromStrError;
467
468    fn from_str(s: &str) -> Result<Self, Self::Err> {
469        let mut parts = s
470            .split(':')
471            .map(|s| u8::from_str_radix(s, 16).map_err(MacAddrFromStrError::IntError));
472
473        let Some(data0) = parts.next() else {
474            return Err(MacAddrFromStrError::InvalidAddrFormat);
475        };
476        let Some(data1) = parts.next() else {
477            return Err(MacAddrFromStrError::InvalidAddrFormat);
478        };
479        let Some(data2) = parts.next() else {
480            return Err(MacAddrFromStrError::InvalidAddrFormat);
481        };
482        let Some(data3) = parts.next() else {
483            return Err(MacAddrFromStrError::InvalidAddrFormat);
484        };
485        let Some(data4) = parts.next() else {
486            return Err(MacAddrFromStrError::InvalidAddrFormat);
487        };
488        let Some(data5) = parts.next() else {
489            return Err(MacAddrFromStrError::InvalidAddrFormat);
490        };
491
492        if parts.next().is_some() {
493            return Err(MacAddrFromStrError::InvalidAddrFormat);
494        }
495
496        Ok(MacAddr([data0?, data1?, data2?, data3?, data4?, data5?]))
497    }
498}
499
500impl core::fmt::Display for MacAddrFromStrError {
501    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
502        match self {
503            Self::IntError(error) => write!(f, "a number is not in hexadecimal format: {error}"),
504            Self::InvalidAddrFormat => f.write_str("invalid address format"),
505        }
506    }
507}
508
509impl core::error::Error for MacAddrFromStrError {}
510
511/// IP networks address for network interface.
512///
513/// It is returned by [`NetworkData::ip_networks`][crate::NetworkData::ip_networks].
514#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq, PartialOrd, Ord)]
515#[cfg_attr(feature = "serde", derive(serde::Deserialize))]
516pub struct IpNetwork {
517    /// The IP of the network interface.
518    pub addr: IpAddr,
519    /// The netmask, prefix of the IP address.
520    pub prefix: u8,
521}
522
523impl fmt::Display for IpNetwork {
524    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
525        write!(f, "{}/{}", self.addr, self.prefix)
526    }
527}
528
529/// Error type returned from `MacAddr::from_str` implementation.
530#[derive(Debug, Clone, PartialEq, Eq)]
531pub enum IpNetworkFromStrError {
532    /// Prefix is not an integer.
533    PrefixError(ParseIntError),
534    /// Failed to parse IP address.
535    AddrParseError(AddrParseError),
536    /// Input is not of format `[IP address]/[number]`.
537    InvalidAddrFormat,
538}
539
540impl FromStr for IpNetwork {
541    type Err = IpNetworkFromStrError;
542
543    #[allow(clippy::from_str_radix_10)]
544    fn from_str(s: &str) -> Result<Self, Self::Err> {
545        let mut parts = s.split('/');
546
547        let Some(addr) = parts.next() else {
548            return Err(IpNetworkFromStrError::InvalidAddrFormat);
549        };
550        let Some(prefix) = parts.next() else {
551            return Err(IpNetworkFromStrError::InvalidAddrFormat);
552        };
553        if parts.next().is_some() {
554            return Err(IpNetworkFromStrError::InvalidAddrFormat);
555        }
556
557        Ok(IpNetwork {
558            addr: IpAddr::from_str(addr).map_err(IpNetworkFromStrError::AddrParseError)?,
559            prefix: u8::from_str_radix(prefix, 10).map_err(IpNetworkFromStrError::PrefixError)?,
560        })
561    }
562}
563
564impl core::fmt::Display for IpNetworkFromStrError {
565    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
566        match self {
567            Self::PrefixError(error) => write!(f, "prefix is not an integer: {error}"),
568            Self::AddrParseError(error) => write!(f, "failed to parse IP address: {error}"),
569            Self::InvalidAddrFormat => {
570                f.write_str("invalid address format, should `[IP address]/[number]`")
571            }
572        }
573    }
574}
575
576impl core::error::Error for IpNetworkFromStrError {}
577
578/// The operational state of some interface based on `IfOperStatus`
579/// from [RFC2863].
580///
581/// Not all states are supported by all systems.
582///
583/// If you want to only distinguish between potentially operational and not operational you
584/// likely want to:
585/// - treat [`Up`][Self::Up], [`Dormant`][Self::Dormant] and [`Unknown`][Self::Unknown] as potentially operational
586/// - treat [`Down`][Self::Down], [`Testing`][Self::Testing], [`NotPresent`][Self::NotPresent] and [`LowerLayerDown`][Self::LowerLayerDown] as not operational
587///
588/// [RFC2863]: https://datatracker.ietf.org/doc/html/rfc2863#section-3.1.12
589///
590// windows: https://learn.microsoft.com/en-us/dotnet/api/system.net.networkinformation.operationalstatus?view=net-10.0
591// linux: https://docs.kernel.org/networking/operstates.html#tlv-ifla-operstate
592// discriminants based on windows
593#[non_exhaustive]
594#[derive(Debug, Clone, Copy, PartialEq, Eq)]
595pub enum InterfaceOperationalState {
596    #[doc(hidden)]
597    #[non_exhaustive]
598    /// InterfaceOperationalState is unsupported or had an unrecognized value
599    Other,
600    /// The interface is able to pass packets.
601    Up = 1,
602    /// The interface is unable to pass packets.
603    ///
604    /// ## Examples
605    /// - an error condition has occurred
606    /// - the interface is disabled
607    Down = 2,
608    /// The interface is unable to pass packets.
609    /// The interface is performing some test(s).
610    /// Presumably it will change into another appropriate state once testing completes.
611    ///
612    /// ## Examples
613    /// - driver self-test
614    /// - media (e.g. cable) test
615    Testing = 3,
616    /// The state of the interface is unknown.
617    /// The interface may or may not be usable.
618    ///
619    /// ## Examples
620    /// - on linux the loopback interface (localhost, 127.0.0.1/8, ::1/128) typically has this state
621    Unknown = 4,
622    /// The interface is unable to pass packets.
623    /// It is in a "pending state" waiting for some external event.
624    /// When the event occurs the interface will prusumably change into the `Up`` state.
625    ///
626    /// ## Examples
627    /// - an "on-demand" interface might be waiting for a package in the transmit queue to establish a connection.
628    /// - an interface might be waiting for the remote system to establish a connection
629    /// - waiting for 802.1X protocol to establish a connection
630    Dormant = 5,
631    /// The interface is unable to pass packets.
632    /// A (hardware) component is not present.
633    ///
634    /// This is a refinement on `Down`.
635    ///
636    /// ## Examples
637    /// - hot-swap module is currently not installed
638    /// - pre-configured interface is not yet installed
639    /// - an interface has been removed but is still configured
640    NotPresent = 6,
641    /// The interface is unable to pass packets.
642    /// This interface depends on one or more interfaces and at least one of these interfaces prevents this interface from being `Up`.
643    ///
644    /// This is a refinement on `Down`.
645    ///
646    /// ## Examples
647    /// - a VPN interface depends on any physical interface being up but the physical interface is testing the cable to determine the available speed
648    /// - a single virtual interface that aggregates multiple physical interfaces but all physical interfaces are down
649    LowerLayerDown = 7,
650}
651
652impl fmt::Display for InterfaceOperationalState {
653    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
654        // see /sys/class/net/<iface>/operstate section at https://www.kernel.org/doc/Documentation/ABI/testing/sysfs-class-net
655        f.write_str(match self {
656            InterfaceOperationalState::Other => "other",
657            InterfaceOperationalState::Up => "up",
658            InterfaceOperationalState::Down => "down",
659            InterfaceOperationalState::Testing => "testing",
660            InterfaceOperationalState::Unknown => "unknown",
661            InterfaceOperationalState::Dormant => "dormant",
662            InterfaceOperationalState::NotPresent => "notpresent",
663            InterfaceOperationalState::LowerLayerDown => "lowerlayerdown",
664        })
665    }
666}
667
668#[cfg(test)]
669mod tests {
670    use crate::*;
671    use std::net::{IpAddr, Ipv4Addr, Ipv6Addr};
672    use std::str::FromStr;
673
674    // Ensure that the `Display` and `Debug` traits are implemented on the `MacAddr` struct
675    #[test]
676    fn check_display_impl_mac_address() {
677        println!(
678            "{} {:?}",
679            MacAddr([0x1, 0x2, 0x3, 0x4, 0x5, 0x6]),
680            MacAddr([0xa, 0xb, 0xc, 0xd, 0xe, 0xf])
681        );
682    }
683
684    #[test]
685    fn check_mac_address_is_unspecified_true() {
686        assert!(MacAddr::UNSPECIFIED.is_unspecified());
687        assert!(MacAddr([0; 6]).is_unspecified());
688    }
689
690    #[test]
691    fn check_mac_address_is_unspecified_false() {
692        assert!(!MacAddr([1, 2, 3, 4, 5, 6]).is_unspecified());
693    }
694
695    #[test]
696    fn check_mac_address_conversions() {
697        let mac = MacAddr([0xa, 0xb, 0xc, 0xd, 0xe, 0xf]);
698
699        let mac_s = mac.to_string();
700        assert_eq!("0a:0b:0c:0d:0e:0f", mac_s);
701        assert_eq!(Ok(mac), MacAddr::from_str(&mac_s));
702
703        assert_eq!(
704            MacAddr::from_str("0a:0b:0c:0d:0e:0f:01"),
705            Err(MacAddrFromStrError::InvalidAddrFormat)
706        );
707        assert_eq!(
708            MacAddr::from_str("0a:0b:0c:0d:0e"),
709            Err(MacAddrFromStrError::InvalidAddrFormat)
710        );
711    }
712
713    // Ensure that the `Display` and `Debug` traits are implemented on the `IpNetwork` struct
714    #[test]
715    fn check_display_impl_ip_network_ipv4() {
716        println!(
717            "{} {:?}",
718            IpNetwork {
719                addr: IpAddr::from(Ipv4Addr::new(1, 2, 3, 4)),
720                prefix: 3
721            },
722            IpNetwork {
723                addr: IpAddr::from(Ipv4Addr::new(255, 255, 255, 0)),
724                prefix: 21
725            }
726        );
727    }
728
729    #[test]
730    fn check_display_impl_ip_network_ipv6() {
731        println!(
732            "{} {:?}",
733            IpNetwork {
734                addr: IpAddr::from(Ipv6Addr::new(0xffff, 0xaabb, 00, 0, 0, 0x000c, 11, 21)),
735                prefix: 127
736            },
737            IpNetwork {
738                addr: IpAddr::from(Ipv6Addr::new(0xffcc, 0, 0, 0xffcc, 0, 0xffff, 0, 0xccaa)),
739                prefix: 120
740            }
741        )
742    }
743
744    #[test]
745    fn check_ip_networks() {
746        if !IS_SUPPORTED_SYSTEM {
747            return;
748        }
749        let networks = Networks::new_with_refreshed_list();
750        if networks.iter().any(|(_, n)| !n.ip_networks().is_empty()) {
751            return;
752        }
753        panic!("Networks should have at least one IP network ");
754    }
755
756    #[test]
757    fn check_ip_network_conversions() {
758        let addr = IpNetwork {
759            addr: IpAddr::from(Ipv6Addr::new(0xff, 0xa, 0x8, 0x12, 0x7, 0xc, 0xa, 0xb)),
760            prefix: 12,
761        };
762
763        let addr_s = addr.to_string();
764        assert_eq!("ff:a:8:12:7:c:a:b/12", addr_s);
765        assert_eq!(Ok(addr), IpNetwork::from_str(&addr_s));
766
767        let addr = IpNetwork {
768            addr: IpAddr::from(Ipv4Addr::new(255, 255, 255, 0)),
769            prefix: 21,
770        };
771
772        let addr_s = addr.to_string();
773        assert_eq!("255.255.255.0/21", addr_s);
774        assert_eq!(Ok(addr), IpNetwork::from_str(&addr_s));
775
776        assert_eq!(
777            IpNetwork::from_str("ff:a:8:12:7:c:a:b"),
778            Err(IpNetworkFromStrError::InvalidAddrFormat)
779        );
780        assert_eq!(
781            IpNetwork::from_str("ff:a:8:12:7:c:a:b/12/12"),
782            Err(IpNetworkFromStrError::InvalidAddrFormat)
783        );
784        match IpNetwork::from_str("0a:0b:0c:0d:0e/12") {
785            Err(IpNetworkFromStrError::AddrParseError(_)) => {}
786            x => panic!("expected `IpNetworkFromStrError::AddrParseError`, found {x:?}"),
787        }
788    }
789}