1use crate::details;
2use crate::PingReceive;
3use std::net::Ipv4Addr;
4use std::sync::Arc;
5use std::time::Duration;
6
7#[allow(clippy::module_name_repetitions)]
9pub struct PingFoxConfig {
10 pub socket_type: SocketType,
12 pub timeout: Duration,
14 pub channel_size: usize,
16}
17
18#[derive(Clone, Copy)]
23pub enum SocketType {
24 DGRAM,
28 RAW,
32}
33
34#[non_exhaustive]
37pub struct PingSentToken {}
38
39pub struct PingSender(details::PingSender<details::icmp::v4::Socket>);
41impl PingSender {
42 pub fn send_to(&mut self, ip: Ipv4Addr) -> details::PingResult<PingSentToken> {
48 self.0.send_to(ip)
49 }
50}
51
52pub struct PingReceiver(details::PingReceiver<details::icmp::v4::Socket>);
54impl PingReceiver {
55 pub fn receive(&mut self, token: PingSentToken) -> details::PingResult<PingReceive> {
63 self.0.receive(token)
64 }
65}
66
67pub fn create(config: &PingFoxConfig) -> details::PingResult<(PingSender, PingReceiver)> {
69 let socket = details::icmp::v4::Socket::new(config.socket_type, config.timeout)?;
70 let (sender, receiver) = create_with_socket::<details::icmp::v4::Socket>(socket, config.channel_size);
71 Ok((PingSender(sender), PingReceiver(receiver)))
72}
73
74fn create_with_socket<S>(socket: S, channel_size: usize) -> (details::PingSender<S>, details::PingReceiver<S>)
75where
76 S: details::icmp::v4::TSocket + 'static,
77{
78 let icmpv4 = Arc::new(details::icmp::v4::IcmpV4::new(socket));
79 let (send_record_tx, send_record_rx) = details::records::ping_send_record_channel(channel_size);
80 let ping_data_buffer = details::PingDataBuffer::new(send_record_rx);
81 (
82 details::PingSender::new(icmpv4.clone(), send_record_tx),
83 details::PingReceiver::new(icmpv4, ping_data_buffer),
84 )
85}
86
87#[cfg(test)]
88mod tests {
89 use super::*;
90 use details::icmp::v4::tests::SocketMock;
91
92 #[test]
93 fn ping_localhost_succeeds() {
94 let ip = Ipv4Addr::new(127, 0, 0, 1);
95 let channel_size = 4;
96 let socket = SocketMock::new_default();
97
98 let (mut ping_sender, mut ping_receiver) = super::create_with_socket(socket, channel_size);
99 let token = ping_sender.send_to(ip).unwrap();
100 let ping_response = ping_receiver.receive(token);
101
102 assert!(ping_response.is_ok());
103
104 let token = ping_sender.send_to(ip).unwrap();
105 let ping_response = ping_receiver.receive(token);
106
107 assert!(ping_response.is_ok());
108 }
109}