network/
ip.rs

1use core::fmt::Display;
2
3#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
4#[repr(transparent)]
5pub struct IPv4([u8; 4]);
6
7impl IPv4 {
8    pub const LOCALHOST: Self = Self([127, 0, 0, 1]);
9
10    pub const fn new(value: [u8; 4]) -> Self {
11        Self(value)
12    }
13
14    pub const fn into_inner(self) -> [u8; 4] {
15        self.0
16    }
17
18    pub const fn from_inner(value: [u8; 4]) -> Self {
19        Self(value)
20    }
21}
22
23impl TryFrom<&str> for IPv4 {
24    type Error = ();
25
26    fn try_from(value: &str) -> Result<Self, Self::Error> {
27        let mut result = [0; 4];
28        let mut index = 0;
29
30        for part in value.split('.') {
31            if index >= 4 {
32                return Err(());
33            }
34            let part = part.parse::<u8>().map_err(|_| ())?;
35            result[index] = part;
36            index += 1;
37        }
38        if index != 4 {
39            return Err(());
40        }
41
42        Ok(Self::new(result))
43    }
44}
45
46impl Display for IPv4 {
47    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
48        write!(f, "{}.{}.{}.{}", self.0[0], self.0[1], self.0[2], self.0[3])
49    }
50}
51
52#[derive(Debug, Clone, PartialEq, Eq, Hash)]
53#[repr(transparent)]
54pub struct IPv6([u16; 8]);
55
56impl IPv6 {
57    pub const fn new(value: [u16; 8]) -> Self {
58        Self(value)
59    }
60
61    pub const fn into_inner(self) -> [u16; 8] {
62        self.0
63    }
64
65    pub const fn from_inner(value: [u16; 8]) -> Self {
66        Self(value)
67    }
68}
69
70impl TryFrom<&str> for IPv6 {
71    type Error = ();
72
73    fn try_from(value: &str) -> Result<Self, Self::Error> {
74        let mut result = [0; 8];
75        let mut index = 0;
76
77        for part in value.split(':') {
78            if index >= result.len() {
79                return Err(());
80            }
81
82            let part = u16::from_str_radix(part, 16).map_err(|_| ())?;
83            result[index] = part;
84            index += 1;
85        }
86        if index != result.len() {
87            return Err(());
88        }
89
90        Ok(Self::new(result))
91    }
92}
93
94impl Display for IPv6 {
95    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
96        write!(
97            f,
98            "{:x}:{:x}:{:x}:{:x}:{:x}:{:x}:{:x}:{:x}",
99            self.0[0], self.0[1], self.0[2], self.0[3], self.0[4], self.0[5], self.0[6], self.0[7]
100        )
101    }
102}
103
104#[derive(Debug, Clone, PartialEq, Eq, Hash)]
105pub enum IP {
106    IPv4(IPv4),
107    IPv6(IPv6),
108}
109
110impl Display for IP {
111    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
112        match self {
113            IP::IPv4(value) => write!(f, "{value}"),
114            IP::IPv6(value) => write!(f, "{value}"),
115        }
116    }
117}
118
119impl From<IPv4> for IP {
120    fn from(value: IPv4) -> Self {
121        Self::IPv4(value)
122    }
123}
124
125impl From<IPv6> for IP {
126    fn from(value: IPv6) -> Self {
127        Self::IPv6(value)
128    }
129}
130
131#[cfg(test)]
132mod tests {
133    use super::*;
134
135    #[test]
136    fn test_ipv4_try_from() {
137        let ip = IPv4::try_from("0.0.0.0").unwrap();
138
139        assert_eq!(ip.0, [0, 0, 0, 0]);
140
141        IPv4::try_from("1.2b.3.4").unwrap_err();
142
143        IPv4::try_from("1.2.3.4.5").unwrap_err();
144
145        IPv4::try_from("1.2.3").unwrap_err();
146
147        let ip = IPv4::try_from("4.3.2.1").unwrap();
148
149        assert_eq!(ip.0, [4, 3, 2, 1]);
150    }
151
152    #[test]
153    fn test_ipv6_try_from() {
154        let ip = IPv6::try_from("0:0:0:0:0:0:0:0").unwrap();
155
156        assert_eq!(ip.0, [0; 8]);
157
158        IPv6::try_from("0:0:0:0:0:0:0:0:0").unwrap_err();
159
160        IPv6::try_from("0:0:0:0:0:0:0").unwrap_err();
161
162        let ip = IPv6::try_from("1234:5678:9abc:def0:1234:5678:9abc:def0").unwrap();
163
164        assert_eq!(
165            ip.0,
166            [
167                0x1234, 0x5678, 0x9abc, 0xdef0, 0x1234, 0x5678, 0x9abc, 0xdef0
168            ]
169        );
170    }
171}