nntp_proxy/types/network.rs
1//! Network-related domain types with validation and type safety.
2
3use derive_more::{AsRef, Deref, Display, From, Into};
4use std::net::SocketAddr;
5
6/// A validated client socket address.
7///
8/// This newtype wrapper provides type safety and prevents mixing up
9/// client addresses with other socket addresses in the codebase.
10///
11/// # Examples
12///
13/// ```
14/// use std::net::SocketAddr;
15/// use nntp_proxy::types::ClientAddress;
16///
17/// let addr: SocketAddr = "127.0.0.1:8119".parse().unwrap();
18/// let client_addr = ClientAddress::from(addr);
19///
20/// println!("Client connected from: {}", client_addr);
21/// ```
22#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Display, From, Into, AsRef, Deref)]
23pub struct ClientAddress(SocketAddr);
24
25impl ClientAddress {
26 /// Create a new client address from a socket address.
27 ///
28 /// # Examples
29 ///
30 /// ```
31 /// use std::net::SocketAddr;
32 /// use nntp_proxy::types::ClientAddress;
33 ///
34 /// let addr: SocketAddr = "192.168.1.100:12345".parse().unwrap();
35 /// let client_addr = ClientAddress::new(addr);
36 /// ```
37 #[inline]
38 #[must_use]
39 pub const fn new(addr: SocketAddr) -> Self {
40 Self(addr)
41 }
42
43 /// Get the underlying socket address.
44 ///
45 /// # Examples
46 ///
47 /// ```
48 /// use std::net::SocketAddr;
49 /// use nntp_proxy::types::ClientAddress;
50 ///
51 /// let addr: SocketAddr = "10.0.0.5:9999".parse().unwrap();
52 /// let client_addr = ClientAddress::from(addr);
53 ///
54 /// assert_eq!(client_addr.as_socket_addr(), &addr);
55 /// ```
56 #[inline]
57 #[must_use]
58 pub const fn as_socket_addr(&self) -> &SocketAddr {
59 &self.0
60 }
61
62 /// Convert into the underlying socket address.
63 ///
64 /// # Examples
65 ///
66 /// ```
67 /// use std::net::SocketAddr;
68 /// use nntp_proxy::types::ClientAddress;
69 ///
70 /// let addr: SocketAddr = "172.16.0.1:5555".parse().unwrap();
71 /// let client_addr = ClientAddress::from(addr);
72 /// let socket_addr = client_addr.into_inner();
73 ///
74 /// assert_eq!(socket_addr, addr);
75 /// ```
76 #[inline]
77 #[must_use]
78 pub const fn into_inner(self) -> SocketAddr {
79 self.0
80 }
81
82 /// Check if this is an IPv4 address.
83 ///
84 /// # Examples
85 ///
86 /// ```
87 /// use nntp_proxy::types::ClientAddress;
88 /// use std::net::SocketAddr;
89 ///
90 /// let addr = ClientAddress::from("127.0.0.1:8119".parse::<SocketAddr>().unwrap());
91 /// assert!(addr.is_ipv4());
92 ///
93 /// let addr6 = ClientAddress::from("[::1]:8119".parse::<SocketAddr>().unwrap());
94 /// assert!(!addr6.is_ipv4());
95 /// ```
96 #[inline]
97 #[must_use]
98 pub const fn is_ipv4(&self) -> bool {
99 self.0.is_ipv4()
100 }
101
102 /// Check if this is an IPv6 address.
103 ///
104 /// # Examples
105 ///
106 /// ```
107 /// use nntp_proxy::types::ClientAddress;
108 /// use std::net::SocketAddr;
109 ///
110 /// let addr = ClientAddress::from("[::1]:8119".parse::<SocketAddr>().unwrap());
111 /// assert!(addr.is_ipv6());
112 ///
113 /// let addr4 = ClientAddress::from("127.0.0.1:8119".parse::<SocketAddr>().unwrap());
114 /// assert!(!addr4.is_ipv6());
115 /// ```
116 #[inline]
117 #[must_use]
118 pub const fn is_ipv6(&self) -> bool {
119 self.0.is_ipv6()
120 }
121}
122
123#[cfg(test)]
124mod tests {
125 use super::*;
126 use std::net::{IpAddr, Ipv4Addr, Ipv6Addr};
127
128 #[test]
129 fn test_display() {
130 let addr = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(192, 168, 1, 100)), 12345);
131 let client_addr = ClientAddress::from(addr);
132 assert_eq!(format!("{client_addr}"), "192.168.1.100:12345");
133 }
134
135 #[test]
136 fn test_debug() {
137 let addr = SocketAddr::new(IpAddr::V4(Ipv4Addr::LOCALHOST), 8119);
138 let client_addr = ClientAddress::from(addr);
139 assert_eq!(format!("{client_addr:?}"), "ClientAddress(127.0.0.1:8119)");
140 }
141
142 #[test]
143 fn test_is_ipv4() {
144 let addr = SocketAddr::new(IpAddr::V4(Ipv4Addr::LOCALHOST), 8119);
145 let client_addr = ClientAddress::from(addr);
146 assert!(client_addr.is_ipv4());
147 assert!(!client_addr.is_ipv6());
148 }
149
150 #[test]
151 fn test_is_ipv6() {
152 let addr = SocketAddr::new(IpAddr::V6(Ipv6Addr::LOCALHOST), 8119);
153 let client_addr = ClientAddress::from(addr);
154 assert!(client_addr.is_ipv6());
155 assert!(!client_addr.is_ipv4());
156 }
157
158 #[test]
159 fn test_equality() {
160 let addr1 = SocketAddr::new(IpAddr::V4(Ipv4Addr::LOCALHOST), 8119);
161 let addr2 = SocketAddr::new(IpAddr::V4(Ipv4Addr::LOCALHOST), 8119);
162 let addr3 = SocketAddr::new(IpAddr::V4(Ipv4Addr::LOCALHOST), 8120);
163
164 let client1 = ClientAddress::from(addr1);
165 let client2 = ClientAddress::from(addr2);
166 let client3 = ClientAddress::from(addr3);
167
168 assert_eq!(client1, client2);
169 assert_ne!(client1, client3);
170 }
171
172 #[test]
173 fn test_hash() {
174 use std::collections::HashSet;
175
176 let addr1 = SocketAddr::new(IpAddr::V4(Ipv4Addr::LOCALHOST), 8119);
177 let addr2 = SocketAddr::new(IpAddr::V4(Ipv4Addr::LOCALHOST), 8119);
178
179 let client1 = ClientAddress::from(addr1);
180 let client2 = ClientAddress::from(addr2);
181
182 let mut set = HashSet::new();
183 set.insert(client1);
184 assert!(set.contains(&client2));
185 }
186
187 #[test]
188 fn test_deref() {
189 let addr = SocketAddr::new(IpAddr::V4(Ipv4Addr::LOCALHOST), 8119);
190 let client_addr = ClientAddress::from(addr);
191
192 // Can call SocketAddr methods directly via Deref
193 assert!(client_addr.is_ipv4());
194 assert_eq!(client_addr.port(), 8119);
195 }
196
197 #[test]
198 fn test_as_ref() {
199 let addr = SocketAddr::new(IpAddr::V4(Ipv4Addr::LOCALHOST), 8119);
200 let client_addr = ClientAddress::from(addr);
201
202 let socket_ref: &SocketAddr = client_addr.as_ref();
203 assert_eq!(socket_ref, &addr);
204 }
205}