1use std::{net::SocketAddr, num::NonZeroU32};
2
3use socket2::{SockAddr, Type};
4
5use crate::ICMP;
6
7#[derive(Debug)]
10pub struct Config {
11 pub sock_type_hint: Type,
12 pub kind: ICMP,
13 pub bind: Option<SockAddr>,
14 pub interface: Option<String>,
15 pub interface_index: Option<NonZeroU32>,
16 pub ttl: Option<u32>,
17 pub fib: Option<u32>,
18}
19
20impl Default for Config {
21 fn default() -> Self {
22 Self {
23 sock_type_hint: Type::DGRAM,
24 kind: ICMP::default(),
25 bind: None,
26 interface: None,
27 interface_index: None,
28 ttl: None,
29 fib: None,
30 }
31 }
32}
33
34impl Config {
35 pub fn new() -> Self {
37 Self::default()
38 }
39
40 pub fn builder() -> ConfigBuilder {
41 ConfigBuilder::default()
42 }
43}
44
45#[derive(Debug)]
46pub struct ConfigBuilder {
47 sock_type_hint: Type,
48 kind: ICMP,
49 bind: Option<SockAddr>,
50 interface: Option<String>,
51 interface_index: Option<NonZeroU32>,
52 ttl: Option<u32>,
53 fib: Option<u32>,
54}
55
56impl Default for ConfigBuilder {
57 fn default() -> Self {
58 Self {
59 sock_type_hint: Type::DGRAM,
60 kind: ICMP::default(),
61 bind: None,
62 interface: None,
63 interface_index: None,
64 ttl: None,
65 fib: None,
66 }
67 }
68}
69
70impl ConfigBuilder {
71 pub fn bind(mut self, bind: SocketAddr) -> Self {
76 self.bind = Some(SockAddr::from(bind));
77 self
78 }
79
80 pub fn interface(mut self, interface: &str) -> Self {
86 self.interface = Some(interface.to_string());
87 self
88 }
89
90 pub fn interface_index(mut self, interface_index: NonZeroU32) -> Self {
92 self.interface_index = Some(interface_index);
93 self
94 }
95
96 pub fn ttl(mut self, ttl: u32) -> Self {
101 self.ttl = Some(ttl);
102 self
103 }
104
105 pub fn fib(mut self, fib: u32) -> Self {
106 self.fib = Some(fib);
107 self
108 }
109
110 pub fn kind(mut self, kind: ICMP) -> Self {
112 self.kind = kind;
113 self
114 }
115
116 pub fn sock_type_hint(mut self, typ: Type) -> Self {
118 self.sock_type_hint = typ;
119 self
120 }
121
122 pub fn build(self) -> Config {
123 Config {
124 sock_type_hint: self.sock_type_hint,
125 kind: self.kind,
126 bind: self.bind,
127 interface: self.interface,
128 interface_index: self.interface_index,
129 ttl: self.ttl,
130 fib: self.fib,
131 }
132 }
133}
134
135#[cfg(test)]
136mod tests {
137 use super::*;
138 use std::net::{IpAddr, Ipv4Addr, SocketAddr};
139
140 #[test]
141 fn test_config_default() {
142 let config = Config::default();
143 assert_eq!(config.kind, ICMP::V4);
144 assert_eq!(config.sock_type_hint, Type::DGRAM);
145 assert!(config.bind.is_none());
146 assert!(config.interface.is_none());
147 assert!(config.interface_index.is_none());
148 assert!(config.ttl.is_none());
149 assert!(config.fib.is_none());
150 }
151
152 #[test]
153 fn test_config_builder_kind() {
154 let config = ConfigBuilder::default().kind(ICMP::V6).build();
155 assert_eq!(config.kind, ICMP::V6);
156 }
157
158 #[test]
159 fn test_config_builder_ttl() {
160 let config = ConfigBuilder::default().ttl(64).build();
161 assert_eq!(config.ttl, Some(64));
162 }
163
164 #[test]
165 fn test_config_builder_bind() {
166 let addr = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 0);
167 let config = ConfigBuilder::default().bind(addr).build();
168 assert!(config.bind.is_some());
169 }
170
171 #[test]
172 fn test_config_builder_interface() {
173 let config = ConfigBuilder::default().interface("eth0").build();
174 assert_eq!(config.interface, Some("eth0".to_string()));
175 }
176
177 #[test]
178 fn test_config_builder_sock_type_hint() {
179 let config = ConfigBuilder::default().sock_type_hint(Type::RAW).build();
180 assert_eq!(config.sock_type_hint, Type::RAW);
181 }
182
183 #[test]
184 fn test_config_builder_fib() {
185 let config = ConfigBuilder::default().fib(100).build();
186 assert_eq!(config.fib, Some(100));
187 }
188
189 #[test]
190 fn test_config_builder_interface_index() {
191 let index = NonZeroU32::new(1).unwrap();
192 let config = ConfigBuilder::default().interface_index(index).build();
193 assert_eq!(config.interface_index, Some(index));
194 }
195
196 #[test]
197 fn test_config_builder_chained() {
198 let addr = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 0);
199 let index = NonZeroU32::new(1).unwrap();
200
201 let config = ConfigBuilder::default()
202 .kind(ICMP::V6)
203 .ttl(128)
204 .bind(addr)
205 .interface("eth0")
206 .interface_index(index)
207 .sock_type_hint(Type::RAW)
208 .fib(200)
209 .build();
210
211 assert_eq!(config.kind, ICMP::V6);
212 assert_eq!(config.ttl, Some(128));
213 assert!(config.bind.is_some());
214 assert_eq!(config.interface, Some("eth0".to_string()));
215 assert_eq!(config.interface_index, Some(index));
216 assert_eq!(config.sock_type_hint, Type::RAW);
217 assert_eq!(config.fib, Some(200));
218 }
219
220 #[test]
221 fn test_config_build_preserves_all_fields() {
222 let addr = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(192, 168, 1, 1)), 8080);
223 let index = NonZeroU32::new(5).unwrap();
224
225 let config = ConfigBuilder::default()
226 .kind(ICMP::V4)
227 .ttl(64)
228 .bind(addr)
229 .interface("wlan0")
230 .interface_index(index)
231 .sock_type_hint(Type::DGRAM)
232 .fib(50)
233 .build();
234
235 assert_eq!(config.kind, ICMP::V4);
236 assert_eq!(config.ttl, Some(64));
237 assert!(config.bind.is_some());
238 assert_eq!(config.interface, Some("wlan0".to_string()));
239 assert_eq!(config.interface_index, Some(index));
240 assert_eq!(config.sock_type_hint, Type::DGRAM);
241 assert_eq!(config.fib, Some(50));
242 }
243
244 #[test]
245 fn test_icmp_default() {
246 assert_eq!(ICMP::default(), ICMP::V4);
247 }
248
249 #[test]
250 fn test_config_preserves_none_values() {
251 let config = ConfigBuilder::default().build();
252 assert!(config.bind.is_none());
253 assert!(config.interface.is_none());
254 assert!(config.interface_index.is_none());
255 assert!(config.ttl.is_none());
256 assert!(config.fib.is_none());
257 }
258
259 #[test]
260 fn test_config_builder_multiple_calls() {
261 let config = ConfigBuilder::default()
263 .ttl(64)
264 .ttl(128)
265 .build();
266 assert_eq!(config.ttl, Some(128)); }
268}