network/fundamentals/ip/
ipv6.rs

1use core::fmt::Display;
2
3#[derive(Default, Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
4#[repr(transparent)]
5pub struct Ipv6([u8; 16]); // Avoid 2 byte alignment issues
6
7impl Ipv6 {
8    pub const fn new(value: [u16; 8]) -> Self {
9        let mut bytes = [0; 16];
10        let mut i = 0;
11        while i < 8 {
12            let segment = value[i].to_be_bytes();
13            bytes[i * 2] = segment[0];
14            bytes[i * 2 + 1] = segment[1];
15            i += 1;
16        }
17        Self(bytes)
18    }
19
20    pub const fn into_inner(self) -> [u8; 16] {
21        self.0
22    }
23
24    pub const fn from_inner(value: [u8; 16]) -> Self {
25        Self(value)
26    }
27
28    pub const fn into_smoltcp(self) -> core::net::Ipv6Addr {
29        core::net::Ipv6Addr::from_octets(self.0)
30    }
31
32    pub const fn from_smoltcp(value: &core::net::Ipv6Addr) -> Self {
33        Self(value.octets())
34    }
35}
36
37impl TryFrom<&str> for Ipv6 {
38    type Error = ();
39
40    fn try_from(value: &str) -> Result<Self, Self::Error> {
41        let mut result = [0; 8];
42        let mut index = 0;
43
44        for part in value.split(':') {
45            if index >= 8 {
46                return Err(());
47            }
48            let part = u16::from_str_radix(part, 16).map_err(|_| ())?;
49            result[index] = part;
50            index += 1;
51        }
52
53        if index != 8 {
54            return Err(());
55        }
56
57        Ok(Self::new(result))
58    }
59}
60
61impl Display for Ipv6 {
62    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
63        write!(
64            f,
65            "{:x}{:x}:{:x}{:x}:{:x}{:x}:{:x}{:x}:{:x}{:x}:{:x}{:x}:{:x}{:x}:{:x}{:x}",
66            self.0[0],
67            self.0[1],
68            self.0[2],
69            self.0[3],
70            self.0[4],
71            self.0[5],
72            self.0[6],
73            self.0[7],
74            self.0[8],
75            self.0[9],
76            self.0[10],
77            self.0[11],
78            self.0[12],
79            self.0[13],
80            self.0[14],
81            self.0[15]
82        )
83    }
84}
85
86#[cfg(test)]
87mod tests {
88    use alloc::string::ToString;
89
90    use super::*;
91
92    #[test]
93    fn test_ipv6_display() {
94        let ip = Ipv6::new([0; 8]);
95
96        assert_eq!(ip.to_string(), "00:00:00:00:00:00:00:00");
97    }
98
99    #[test]
100    fn test_ipv6_try_from() {
101        let ip = Ipv6::try_from("0:0:0:0:0:0:0:0").unwrap();
102
103        assert_eq!(ip.0, [0; 16]);
104
105        Ipv6::try_from("0:0:0:0:0:0:0:0:0").unwrap_err();
106
107        Ipv6::try_from("0:0:0:0:0:0:0").unwrap_err();
108
109        let ip = Ipv6::try_from("1234:5678:9abc:def0:1234:5678:9abc:def0").unwrap();
110
111        assert_eq!(
112            ip.0,
113            [
114                0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0, 0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc,
115                0xde, 0xf0
116            ]
117        );
118    }
119}