aboutsummaryrefslogtreecommitdiff
path: root/src/main.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/main.rs')
-rw-r--r--src/main.rs108
1 files changed, 108 insertions, 0 deletions
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::<PingResult>(10);
+
+ let mut states: Vec<State> = 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;
+ }
+}