diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index a31f5b1..d5b6273 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -42,4 +42,4 @@ jobs: uses: actions-rs/cargo@v1 with: command: test - args: -- --nocapture + args: --all-features -- --nocapture diff --git a/Cargo.toml b/Cargo.toml index 85a426b..8dc78b9 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -13,6 +13,7 @@ readme = "README.md" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] +serde = { version = "1.0.144", optional = true } thiserror = "1.0" [target.'cfg(target_os = "linux")'.dependencies] @@ -28,3 +29,8 @@ cc = "1.0.73" libc = "0.2.101" winapi = {version = "0.3", features = ["ws2def", "ws2ipdef", "netioapi", "iphlpapi", "iptypes", "ntdef"] } +[dev-dependencies] +serde_test = "1.0.144" + +[features] +serde = ["dep:serde"] diff --git a/src/interface.rs b/src/interface.rs index 10e27c8..2834577 100644 --- a/src/interface.rs +++ b/src/interface.rs @@ -4,6 +4,11 @@ use std::fmt::Debug; use std::net::{IpAddr, Ipv4Addr, Ipv6Addr}; +#[cfg(feature = "serde")] +use serde::{Serialize, Serializer}; +#[cfg(feature = "serde")] +use serde::ser::SerializeStruct; + /// An alias for an `Option` that wraps either a `Ipv4Addr` or a `Ipv6Addr` /// representing the IP for a Network Interface netmask pub type Netmask = Option; @@ -21,35 +26,19 @@ pub struct NetworkInterface { pub index: u32, } -/// Network interface address -#[derive(Debug, Clone, Copy)] -pub enum Addr { - /// IPV4 Interface from the AFINET network interface family - V4(V4IfAddr), - /// IPV6 Interface from the AFINET6 network interface family - V6(V6IfAddr), -} +#[cfg(feature = "serde")] +impl Serialize for NetworkInterface { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + let mut state = serializer.serialize_struct("NetworkInterface", 3)?; -/// IPV4 Interface from the AFINET network interface family -#[derive(Debug, Clone, Copy)] -pub struct V4IfAddr { - /// The IP address for this network interface - pub ip: Ipv4Addr, - /// The broadcast address for this interface - pub broadcast: Option, - /// The netmask for this interface - pub netmask: Netmask, -} - -/// IPV6 Interface from the AFINET6 network interface family -#[derive(Debug, Clone, Copy)] -pub struct V6IfAddr { - /// The IP address for this network interface - pub ip: Ipv6Addr, - /// The broadcast address for this interface - pub broadcast: Option, - /// The netmask for this interface - pub netmask: Netmask, + state.serialize_field("name", &self.name)?; + state.serialize_field("addr", &self.addr)?; + state.serialize_field("mac_addr", &self.mac_addr)?; + state.end() + } } impl NetworkInterface { @@ -100,6 +89,28 @@ impl NetworkInterface { } } +/// Network interface address +#[derive(Debug, Clone, Copy)] +pub enum Addr { + /// IPV4 Interface from the AFINET network interface family + V4(V4IfAddr), + /// IPV6 Interface from the AFINET6 network interface family + V6(V6IfAddr), +} + +#[cfg(feature = "serde")] +impl Serialize for Addr { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + match *self { + Addr::V4(v4_if_addr) => v4_if_addr.serialize(serializer), + Addr::V6(v6_if_addr) => v6_if_addr.serialize(serializer), + } + } +} + impl Addr { pub fn ip(self) -> IpAddr { match self { @@ -122,3 +133,55 @@ impl Addr { } } } + +/// IPV4 Interface from the AFINET network interface family +#[derive(Debug, Clone, Copy)] +pub struct V4IfAddr { + /// The IP address for this network interface + pub ip: Ipv4Addr, + /// The broadcast address for this interface + pub broadcast: Option, + /// The netmask for this interface + pub netmask: Netmask, +} + +#[cfg(feature = "serde")] +impl Serialize for V4IfAddr { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + let mut state = serializer.serialize_struct("V4IfAddr", 3)?; + + state.serialize_field("ip", &self.ip)?; + state.serialize_field("broadcast", &self.broadcast)?; + state.serialize_field("netmask", &self.netmask)?; + state.end() + } +} + +/// IPV6 Interface from the AFINET6 network interface family +#[derive(Debug, Clone, Copy)] +pub struct V6IfAddr { + /// The IP address for this network interface + pub ip: Ipv6Addr, + /// The broadcast address for this interface + pub broadcast: Option, + /// The netmask for this interface + pub netmask: Netmask, +} + +#[cfg(feature = "serde")] +impl Serialize for V6IfAddr { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + let mut state = serializer.serialize_struct("V6IfAddr", 3)?; + + state.serialize_field("ip", &self.ip)?; + state.serialize_field("broadcast", &self.broadcast)?; + state.serialize_field("netmask", &self.netmask)?; + state.end() + } +} diff --git a/src/test.rs b/src/test.rs index fb80b35..2304062 100644 --- a/src/test.rs +++ b/src/test.rs @@ -1,10 +1,78 @@ -#[allow(unused_imports)] -use crate::{NetworkInterface, NetworkInterfaceConfig}; +#[cfg(test)] +mod tests { + use std::net::Ipv4Addr; -#[test] -fn show_network_interfaces() { - let network_interfaces = NetworkInterface::show().unwrap(); + #[cfg(feature = "serde")] + use serde_test::{Configure, Token, assert_ser_tokens}; - println!("{network_interfaces:#?}"); - assert!(network_interfaces.len() > 1); + #[allow(unused_imports)] + use crate::{Addr, V4IfAddr, Netmask, NetworkInterface, NetworkInterfaceConfig}; + + #[test] + fn show_network_interfaces() { + let network_interfaces = NetworkInterface::show().unwrap(); + + println!("{:#?}", network_interfaces); + assert!(network_interfaces.len() > 1); + } + + #[test] + #[cfg(feature = "serde")] + fn network_interface_serialization() { + let network_interface = NetworkInterface { + name: String::from("Supercool"), + addr: Some(Addr::V4(V4IfAddr { + ip: Ipv4Addr::new(128, 0, 0, 1), + broadcast: Some(Ipv4Addr::new(127, 12, 84, 222)), + netmask: Some(Ipv4Addr::new(128, 1, 135, 24)), + })), + mac_addr: Some(String::from("84:62:7a:03:bd:01")), + }; + + assert_ser_tokens( + &network_interface.compact(), + &[ + Token::Struct { + name: "NetworkInterface", + len: 3, + }, + Token::Str("name"), + Token::Str("Supercool"), + Token::Str("addr"), + Token::Some, + Token::Struct { + name: "V4IfAddr", + len: 3, + }, + Token::Str("ip"), + Token::Tuple { len: 4 }, + Token::U8(128), + Token::U8(0), + Token::U8(0), + Token::U8(1), + Token::TupleEnd, + Token::Str("broadcast"), + Token::Some, + Token::Tuple { len: 4 }, + Token::U8(127), + Token::U8(12), + Token::U8(84), + Token::U8(222), + Token::TupleEnd, + Token::Str("netmask"), + Token::Some, + Token::Tuple { len: 4 }, + Token::U8(128), + Token::U8(1), + Token::U8(135), + Token::U8(24), + Token::TupleEnd, + Token::StructEnd, + Token::Str("mac_addr"), + Token::Some, + Token::Str("84:62:7a:03:bd:01"), + Token::StructEnd, + ], + ); + } }