From aecb7ac234ddb38772f3a968c3fa43ec3b507315 Mon Sep 17 00:00:00 2001 From: ache Date: Tue, 4 Aug 2026 09:43:58 +0200 Subject: Init commit --- src/main.rs | 108 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 108 insertions(+) create mode 100644 src/main.rs (limited to 'src/main.rs') diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 0000000..cf24d9b --- /dev/null +++ b/src/main.rs @@ -0,0 +1,108 @@ +use clap::Parser; +use std::net::IpAddr; +use tokio::sync::mpsc; +use tokio::sync::mpsc::error::TryRecvError; +use tokio::time::sleep; + +mod args; +mod ip; +mod pinger; +mod print; +mod state; +use args::Args; +use ip::parse_ip; +use pinger::{spawn_ping, PingResult}; +use print::{print_ping_line, print_legende_line}; +use state::State; + +#[tokio::main] +async fn main() { + let args = Args::parse(); + + let target: IpAddr = if let Some(target_str) = args.target.clone() { + parse_ip(&target_str).unwrap_or_else(|e| { + eprintln!("Error: {}", e); + std::process::exit(1); + }) + } else { + eprintln!("Error: target address is required"); + std::process::exit(1); + }; + + let (tx, mut rx) = mpsc::channel::(10); + + let mut states: Vec = Vec::new(); + let mut packet_id: u32 = 0; + + + print_legende_line(); + + // Save the current cursor position + if !std::process::Command::new("tput").arg("sc").status().map(|s| s.success()).unwrap_or(false) { + println!("Process failed"); + std::process::exit(1); + } + + + // Should be quit with Ctrl + C + loop { + + let current_time = State::current_time_ms(); + for state in states.iter_mut() { + if let State::NotYetReceived { time_sent } = state { + // TODO: 2000ms Should be a configurable variable + if current_time - *time_sent > 2000 { // 2s = 2000ms + *state = State::Lost; + } + } + } + + // Send the ping + states.push(State::NotYetReceived { time_sent: current_time }); + spawn_ping(target, tx.clone(), packet_id); + packet_id += 1; + + // The update state loop + loop { + match rx.try_recv() { + // Update the state of the received packet + Ok(result) => { + match result { + PingResult::Ping { rrt, ttl, id } => { + if (id as usize) < states.len() { + states[id as usize] = State::Received { rtt: rrt, ttl }; + } + } + PingResult::NoPingErr { id, error } => { + // NOTE: Maybe we should quit, I don't know. + if (id as usize) < states.len() { + states[id as usize] = State::SendError; + } + } + } + } + Err(TryRecvError::Disconnected) => { + // NOTE: Maybe we should quit, I don't know. + break; + } + Err(TryRecvError::Empty) => { + // No more ping to read + break; + } + } + } + + // Put the cursor back up + if !std::process::Command::new("tput").arg("rc").status().map(|s| s.success()).unwrap_or(false) { + println!("Process failed too"); + std::process::exit(1); + } + + // Prints lines based on the state of each ping + print_ping_line(&states); + + + // Sleep for 1 second before the next ping + sleep(tokio::time::Duration::from_millis(1000)).await; + } +} -- cgit v1.3-2-g11bf