gio/
inet_address.rs

1// Take a look at the license at the top of the repository in the LICENSE file.
2
3use std::net::IpAddr;
4
5use glib::{prelude::*, translate::*};
6
7use crate::{ffi, prelude::*, InetAddress, SocketFamily};
8
9#[derive(Debug)]
10pub enum InetAddressBytes<'a> {
11    V4(&'a [u8; 4]),
12    V6(&'a [u8; 16]),
13}
14
15impl InetAddressBytes<'_> {
16    #[inline]
17    fn deref(&self) -> &[u8] {
18        use self::InetAddressBytes::*;
19
20        match *self {
21            V4(bytes) => bytes,
22            V6(bytes) => bytes,
23        }
24    }
25}
26
27impl InetAddress {
28    #[doc(alias = "g_inet_address_new_from_bytes")]
29    pub fn from_bytes(inet_address_bytes: InetAddressBytes) -> Self {
30        let bytes = inet_address_bytes.deref();
31
32        let family = match inet_address_bytes {
33            InetAddressBytes::V4(_) => SocketFamily::Ipv4,
34            InetAddressBytes::V6(_) => SocketFamily::Ipv6,
35        };
36        unsafe {
37            from_glib_full(ffi::g_inet_address_new_from_bytes(
38                bytes.to_glib_none().0,
39                family.into_glib(),
40            ))
41        }
42    }
43}
44
45mod sealed {
46    pub trait Sealed {}
47    impl<T: super::IsA<super::InetAddress>> Sealed for T {}
48}
49
50pub trait InetAddressExtManual: sealed::Sealed + IsA<InetAddress> + 'static {
51    // rustdoc-stripper-ignore-next
52    /// Returns `None` in case the address has a native size different than 4 and 16.
53    #[doc(alias = "g_inet_address_to_bytes")]
54    #[inline]
55    fn to_bytes(&self) -> Option<InetAddressBytes<'_>> {
56        let size = self.native_size();
57        unsafe {
58            let bytes = ffi::g_inet_address_to_bytes(self.as_ref().to_glib_none().0);
59            if size == 4 {
60                Some(InetAddressBytes::V4(&*(bytes as *const [u8; 4])))
61            } else if size == 16 {
62                Some(InetAddressBytes::V6(&*(bytes as *const [u8; 16])))
63            } else {
64                None
65            }
66        }
67    }
68}
69
70impl<O: IsA<InetAddress>> InetAddressExtManual for O {}
71
72impl From<IpAddr> for InetAddress {
73    fn from(addr: IpAddr) -> Self {
74        match addr {
75            IpAddr::V4(v4) => Self::from_bytes(InetAddressBytes::V4(&v4.octets())),
76            IpAddr::V6(v6) => Self::from_bytes(InetAddressBytes::V6(&v6.octets())),
77        }
78    }
79}
80
81impl From<InetAddress> for IpAddr {
82    fn from(addr: InetAddress) -> Self {
83        match addr.to_bytes() {
84            Some(InetAddressBytes::V4(bytes)) => IpAddr::from(*bytes),
85            Some(InetAddressBytes::V6(bytes)) => IpAddr::from(*bytes),
86            None => panic!("Unknown IP kind"),
87        }
88    }
89}
90
91#[cfg(test)]
92mod tests {
93    use std::net::IpAddr;
94
95    use crate::InetAddress;
96
97    #[test]
98    fn test_ipv6_to_rust() {
99        let rust_addr = "2606:50c0:8000::153".parse::<IpAddr>().unwrap();
100        assert!(rust_addr.is_ipv6());
101        let gio_addr = InetAddress::from(rust_addr);
102        assert_eq!(rust_addr, IpAddr::from(gio_addr));
103    }
104
105    #[test]
106    fn test_ipv4_to_rust() {
107        let rust_addr = "185.199.108.153".parse::<IpAddr>().unwrap();
108        assert!(rust_addr.is_ipv4());
109        let gio_addr = InetAddress::from(rust_addr);
110        assert_eq!(rust_addr, IpAddr::from(gio_addr));
111    }
112}