use std::{net::SocketAddr, num::NonZeroU32};
use socket2::{SockAddr, Type};
use crate::ICMP;
#[derive(Debug)]
pub struct Config {
pub sock_type_hint: Type,
pub kind: ICMP,
pub bind: Option<SockAddr>,
pub interface: Option<String>,
pub interface_index: Option<NonZeroU32>,
pub ttl: Option<u32>,
pub fib: Option<u32>,
}
impl Default for Config {
fn default() -> Self {
Self {
sock_type_hint: Type::DGRAM,
kind: ICMP::default(),
bind: None,
interface: None,
interface_index: None,
ttl: None,
fib: None,
}
}
}
impl Config {
pub fn new() -> Self {
Self::default()
}
pub fn builder() -> ConfigBuilder {
ConfigBuilder::default()
}
}
#[derive(Debug)]
pub struct ConfigBuilder {
sock_type_hint: Type,
kind: ICMP,
bind: Option<SockAddr>,
interface: Option<String>,
interface_index: Option<NonZeroU32>,
ttl: Option<u32>,
fib: Option<u32>,
}
impl Default for ConfigBuilder {
fn default() -> Self {
Self {
sock_type_hint: Type::DGRAM,
kind: ICMP::default(),
bind: None,
interface: None,
interface_index: None,
ttl: None,
fib: None,
}
}
}
impl ConfigBuilder {
pub fn bind(mut self, bind: SocketAddr) -> Self {
self.bind = Some(SockAddr::from(bind));
self
}
pub fn interface(mut self, interface: &str) -> Self {
self.interface = Some(interface.to_string());
self
}
pub fn interface_index(mut self, interface_index: NonZeroU32) -> Self {
self.interface_index = Some(interface_index);
self
}
pub fn ttl(mut self, ttl: u32) -> Self {
self.ttl = Some(ttl);
self
}
pub fn fib(mut self, fib: u32) -> Self {
self.fib = Some(fib);
self
}
pub fn kind(mut self, kind: ICMP) -> Self {
self.kind = kind;
self
}
pub fn sock_type_hint(mut self, typ: Type) -> Self {
self.sock_type_hint = typ;
self
}
pub fn build(self) -> Config {
Config {
sock_type_hint: self.sock_type_hint,
kind: self.kind,
bind: self.bind,
interface: self.interface,
interface_index: self.interface_index,
ttl: self.ttl,
fib: self.fib,
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use std::net::{IpAddr, Ipv4Addr, SocketAddr};
#[test]
fn test_config_default() {
let config = Config::default();
assert_eq!(config.kind, ICMP::V4);
assert_eq!(config.sock_type_hint, Type::DGRAM);
assert!(config.bind.is_none());
assert!(config.interface.is_none());
assert!(config.interface_index.is_none());
assert!(config.ttl.is_none());
assert!(config.fib.is_none());
}
#[test]
fn test_config_builder_kind() {
let config = ConfigBuilder::default().kind(ICMP::V6).build();
assert_eq!(config.kind, ICMP::V6);
}
#[test]
fn test_config_builder_ttl() {
let config = ConfigBuilder::default().ttl(64).build();
assert_eq!(config.ttl, Some(64));
}
#[test]
fn test_config_builder_bind() {
let addr = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 0);
let config = ConfigBuilder::default().bind(addr).build();
assert!(config.bind.is_some());
}
#[test]
fn test_config_builder_interface() {
let config = ConfigBuilder::default().interface("eth0").build();
assert_eq!(config.interface, Some("eth0".to_string()));
}
#[test]
fn test_config_builder_sock_type_hint() {
let config = ConfigBuilder::default().sock_type_hint(Type::RAW).build();
assert_eq!(config.sock_type_hint, Type::RAW);
}
#[test]
fn test_config_builder_fib() {
let config = ConfigBuilder::default().fib(100).build();
assert_eq!(config.fib, Some(100));
}
#[test]
fn test_config_builder_interface_index() {
let index = NonZeroU32::new(1).unwrap();
let config = ConfigBuilder::default().interface_index(index).build();
assert_eq!(config.interface_index, Some(index));
}
#[test]
fn test_config_builder_chained() {
let addr = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 0);
let index = NonZeroU32::new(1).unwrap();
let config = ConfigBuilder::default()
.kind(ICMP::V6)
.ttl(128)
.bind(addr)
.interface("eth0")
.interface_index(index)
.sock_type_hint(Type::RAW)
.fib(200)
.build();
assert_eq!(config.kind, ICMP::V6);
assert_eq!(config.ttl, Some(128));
assert!(config.bind.is_some());
assert_eq!(config.interface, Some("eth0".to_string()));
assert_eq!(config.interface_index, Some(index));
assert_eq!(config.sock_type_hint, Type::RAW);
assert_eq!(config.fib, Some(200));
}
#[test]
fn test_config_build_preserves_all_fields() {
let addr = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(192, 168, 1, 1)), 8080);
let index = NonZeroU32::new(5).unwrap();
let config = ConfigBuilder::default()
.kind(ICMP::V4)
.ttl(64)
.bind(addr)
.interface("wlan0")
.interface_index(index)
.sock_type_hint(Type::DGRAM)
.fib(50)
.build();
assert_eq!(config.kind, ICMP::V4);
assert_eq!(config.ttl, Some(64));
assert!(config.bind.is_some());
assert_eq!(config.interface, Some("wlan0".to_string()));
assert_eq!(config.interface_index, Some(index));
assert_eq!(config.sock_type_hint, Type::DGRAM);
assert_eq!(config.fib, Some(50));
}
#[test]
fn test_icmp_default() {
assert_eq!(ICMP::default(), ICMP::V4);
}
#[test]
fn test_config_preserves_none_values() {
let config = ConfigBuilder::default().build();
assert!(config.bind.is_none());
assert!(config.interface.is_none());
assert!(config.interface_index.is_none());
assert!(config.ttl.is_none());
assert!(config.fib.is_none());
}
#[test]
fn test_config_builder_multiple_calls() {
let config = ConfigBuilder::default()
.ttl(64)
.ttl(128)
.build();
assert_eq!(config.ttl, Some(128)); }
}