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}