network/fundamentals/ip/
ipv4.rs

1use core::fmt::Display;
2
3use crate::Ipv6;
4
5#[derive(Default, Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
6#[repr(transparent)]
7pub struct Ipv4([u8; 4]);
8
9impl Ipv4 {
10    pub const LOCALHOST: Self = Self([127, 0, 0, 1]);
11    pub const BROADCAST: Self = Self([255, 255, 255, 255]);
12
13    pub const fn new(value: [u8; 4]) -> Self {
14        Self(value)
15    }
16
17    pub const fn into_inner(self) -> [u8; 4] {
18        self.0
19    }
20
21    pub const fn from_inner(value: [u8; 4]) -> Self {
22        Self(value)
23    }
24
25    pub const fn is_multicast(&self) -> bool {
26        self.0[0] >= 224 && self.0[0] <= 239
27    }
28
29    pub const fn is_broadcast(&self) -> bool {
30        u32::from_be_bytes(self.0) == u32::from_be_bytes(Self::BROADCAST.0)
31    }
32
33    pub const fn to_ipv6_mapped(&self) -> Ipv6 {
34        Ipv6::new([
35            0,
36            0,
37            0,
38            0,
39            0,
40            0xFFFF,
41            u16::from_be_bytes([self.0[0], self.0[1]]),
42            u16::from_be_bytes([self.0[2], self.0[3]]),
43        ])
44    }
45
46    pub const fn into_smoltcp(self) -> core::net::Ipv4Addr {
47        core::net::Ipv4Addr::new(self.0[0], self.0[1], self.0[2], self.0[3])
48    }
49
50    pub const fn from_smoltcp(value: &core::net::Ipv4Addr) -> Self {
51        Self(value.octets())
52    }
53}
54
55impl TryFrom<&str> for Ipv4 {
56    type Error = ();
57
58    fn try_from(value: &str) -> Result<Self, Self::Error> {
59        let mut result = [0; 4];
60        let mut index = 0;
61
62        for part in value.split('.') {
63            if index >= 4 {
64                return Err(());
65            }
66            let part = part.parse::<u8>().map_err(|_| ())?;
67            result[index] = part;
68            index += 1;
69        }
70        if index != 4 {
71            return Err(());
72        }
73
74        Ok(Self::new(result))
75    }
76}
77
78impl Display for Ipv4 {
79    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
80        write!(f, "{}.{}.{}.{}", self.0[0], self.0[1], self.0[2], self.0[3])
81    }
82}
83
84#[cfg(test)]
85mod tests {
86    use alloc::string::ToString;
87
88    use super::*;
89
90    #[test]
91    fn test_ipv4_display() {
92        let ip = Ipv4::new([192, 168, 1, 1]);
93
94        assert_eq!(ip.to_string(), "192.168.1.1");
95    }
96
97    #[test]
98    fn test_ipv4_try_from() {
99        let ip = Ipv4::try_from("0.0.0.0").unwrap();
100
101        assert_eq!(ip.0, [0, 0, 0, 0]);
102
103        Ipv4::try_from("1.2b.3.4").unwrap_err();
104
105        Ipv4::try_from("1.2.3.4.5").unwrap_err();
106
107        Ipv4::try_from("1.2.3").unwrap_err();
108
109        let ip = Ipv4::try_from("4.3.2.1").unwrap();
110
111        assert_eq!(ip.0, [4, 3, 2, 1]);
112    }
113}