Skip to main content

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}