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; mod stats; 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: _, ref error } => { // FEAT: It should fail if states is empty and print a informative characters on the line otherwise eprintln!("Error: {}", error); std::process::exit(2); } } } 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 // FEAT: Should be configurable via CLI sleep(tokio::time::Duration::from_millis(1000)).await; } }