aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/args.rs2
-rw-r--r--src/ip.rs32
-rw-r--r--src/main.rs45
-rw-r--r--src/print.rs189
-rw-r--r--src/stats.rs25
5 files changed, 203 insertions, 90 deletions
diff --git a/src/args.rs b/src/args.rs
index 09de553..fb5d6c1 100644
--- a/src/args.rs
+++ b/src/args.rs
@@ -1,4 +1,4 @@
-use clap::{Parser, ArgAction};
+use clap::{ArgAction, Parser};
#[derive(Parser, Debug)]
#[command(name = "ping")]
diff --git a/src/ip.rs b/src/ip.rs
index 068d38b..b49a2a0 100644
--- a/src/ip.rs
+++ b/src/ip.rs
@@ -29,7 +29,7 @@ pub fn parse_bsd_ipv4(s: &str) -> Result<Ipv4Addr, String> {
let parts: Vec<&str> = s.split('.').collect();
let n = parts.len();
- if n < 1 || n > 4 {
+ if !(1..=4).contains(&n) {
return Err("invalid number of parts".into());
}
@@ -41,33 +41,31 @@ pub fn parse_bsd_ipv4(s: &str) -> Result<Ipv4Addr, String> {
let addr: u32 = match n {
4 => {
- if parsed_parts[0] > 0xFF || parsed_parts[1] > 0xFF ||
- parsed_parts[2] > 0xFF || parsed_parts[3] > 0xFF {
+ if parsed_parts[0] > 0xFF
+ || parsed_parts[1] > 0xFF
+ || parsed_parts[2] > 0xFF
+ || parsed_parts[3] > 0xFF
+ {
return Err("invalid IP part out of range".into());
}
- (parsed_parts[0] << 24) |
- (parsed_parts[1] << 16) |
- (parsed_parts[2] << 8) |
- parsed_parts[3]
+ (parsed_parts[0] << 24)
+ | (parsed_parts[1] << 16)
+ | (parsed_parts[2] << 8)
+ | parsed_parts[3]
}
3 => {
if parsed_parts[0] > 0xFF || parsed_parts[1] > 0xFF || parsed_parts[2] > 0xFFFF {
return Err("invalid IP part out of range".into());
}
- (parsed_parts[0] << 24) |
- (parsed_parts[1] << 16) |
- parsed_parts[2]
+ (parsed_parts[0] << 24) | (parsed_parts[1] << 16) | parsed_parts[2]
}
2 => {
if parsed_parts[0] > 0xFF || parsed_parts[1] > 0xFFFFFF {
return Err("invalid IP part out of range".into());
}
- (parsed_parts[0] << 24) |
- parsed_parts[1]
- }
- 1 => {
- parsed_parts[0]
+ (parsed_parts[0] << 24) | parsed_parts[1]
}
+ 1 => parsed_parts[0],
_ => {
return Err("invalid number of parts".into());
}
@@ -81,11 +79,11 @@ pub fn parse_ip(s: &str) -> Result<IpAddr, String> {
if let Ok(ip) = s.parse::<Ipv6Addr>() {
return Ok(IpAddr::V6(ip));
}
-
+
// Try to parse as BSD valid IPv4
if let Ok(ipv4) = parse_bsd_ipv4(s) {
return Ok(IpAddr::V4(ipv4));
}
-
+
Err(format!("invalid IP address: {}", s))
}
diff --git a/src/main.rs b/src/main.rs
index 5f4f1e0..d5e1588 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -12,7 +12,7 @@ mod state;
mod stats;
use args::Args;
use ip::parse_ip;
-use pinger::{spawn_ping, PingResult};
+use pinger::{PingResult, spawn_ping};
use print::{print_legende_line, print_ping_line, print_stats};
use state::State;
use stats::compute_stats;
@@ -36,31 +36,36 @@ async fn main() {
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) {
+ 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
+ if current_time - *time_sent > 2000 {
+ // 2s = 2000ms
*state = State::Lost;
}
}
}
// Send the ping
- states.push(State::NotYetReceived { time_sent: current_time });
+ states.push(State::NotYetReceived {
+ time_sent: current_time,
+ });
spawn_ping(target, tx.clone(), packet_id);
packet_id += 1;
@@ -68,18 +73,16 @@ async fn main() {
loop {
match rx.try_recv() {
// Update the state of the received packet
- Ok(result) => {
- match result {
- PingResult::Ping { rtt, ttl, id } => {
- if (id as usize) < states.len() {
- states[id as usize] = State::Received { rtt: rtt, ttl };
- }
- }
- PingResult::NoPingErr { id } => {
- states[id as usize] = State::SendError;
+ Ok(result) => match result {
+ PingResult::Ping { rtt, ttl, id } => {
+ if (id as usize) < states.len() {
+ states[id as usize] = State::Received { rtt, ttl };
}
}
- }
+ PingResult::NoPingErr { id } => {
+ states[id as usize] = State::SendError;
+ }
+ },
Err(TryRecvError::Disconnected) => {
eprintln!("Critical error, pinger thread killed");
std::process::exit(2);
@@ -92,7 +95,12 @@ async fn main() {
}
// Put the cursor back up
- if !std::process::Command::new("tput").arg("rc").status().map(|s| s.success()).unwrap_or(false) {
+ if !std::process::Command::new("tput")
+ .arg("rc")
+ .status()
+ .map(|s| s.success())
+ .unwrap_or(false)
+ {
println!("Process failed too");
std::process::exit(1);
}
@@ -116,7 +124,6 @@ async fn main() {
}
}
-
// Sleep for 1 second before the next ping
// FEAT: Should be configurable via CLI
sleep(tokio::time::Duration::from_millis(1000)).await;
diff --git a/src/print.rs b/src/print.rs
index 28e1b9e..421efa3 100644
--- a/src/print.rs
+++ b/src/print.rs
@@ -9,61 +9,166 @@ struct SymbolTableElement {
character: &'static str,
}
-const GREEN_FG :&str = "\x1b[32m";
-const ORANGE_FG :&str = "\x1b[38;5;208m";
-const RED_FG :&str = "\x1b[31m";
+const GREEN_FG: &str = "\x1b[32m";
+const ORANGE_FG: &str = "\x1b[38;5;208m";
+const RED_FG: &str = "\x1b[31m";
// const BLACK_FG :&str = "\x1b[31m";
-const BLACK_BG :&str = "\x1b[40m";
-const GREEN_BG :&str = "\x1b[40m";
-const ORANGE_BG :&str = "\x1b[48;5;208m";
-const ERASE_LINE_END:&str = "\x1b[0K";
+const BLACK_BG: &str = "\x1b[40m";
+const GREEN_BG: &str = "\x1b[40m";
+const ORANGE_BG: &str = "\x1b[48;5;208m";
+const ERASE_LINE_END: &str = "\x1b[0K";
+const RESET: &str = "\x1b[0m";
-const CHARS_TABLE :[SymbolTableElement; 25] = [
- SymbolTableElement{ fg: GREEN_FG, bg: BLACK_BG, character: "▁"},
- SymbolTableElement{ fg: GREEN_FG, bg: BLACK_BG, character: "▂"},
- SymbolTableElement{ fg: GREEN_FG, bg: BLACK_BG, character: "▃"},
- SymbolTableElement{ fg: GREEN_FG, bg: BLACK_BG, character: "▄"},
- SymbolTableElement{ fg: GREEN_FG, bg: BLACK_BG, character: "▅"},
- SymbolTableElement{ fg: GREEN_FG, bg: BLACK_BG, character: "▆"},
- SymbolTableElement{ fg: GREEN_FG, bg: BLACK_BG, character: "▇"},
- SymbolTableElement{ fg: GREEN_FG, bg: BLACK_BG, character: "█"},
- SymbolTableElement{ fg: ORANGE_FG, bg: GREEN_BG, character: "▁"},
- SymbolTableElement{ fg: ORANGE_FG, bg: GREEN_BG, character: "▂"},
- SymbolTableElement{ fg: ORANGE_FG, bg: GREEN_BG, character: "▃"},
- SymbolTableElement{ fg: ORANGE_FG, bg: GREEN_BG, character: "▄"},
- SymbolTableElement{ fg: ORANGE_FG, bg: GREEN_BG, character: "▅"},
- SymbolTableElement{ fg: ORANGE_FG, bg: GREEN_BG, character: "▆"},
- SymbolTableElement{ fg: ORANGE_FG, bg: GREEN_BG, character: "▇"},
- SymbolTableElement{ fg: ORANGE_FG, bg: GREEN_BG, character: "█"},
- SymbolTableElement{ fg: RED_FG, bg: ORANGE_BG, character: "▁"},
- SymbolTableElement{ fg: RED_FG, bg: ORANGE_BG, character: "▂"},
- SymbolTableElement{ fg: RED_FG, bg: ORANGE_BG, character: "▃"},
- SymbolTableElement{ fg: RED_FG, bg: ORANGE_BG, character: "▄"},
- SymbolTableElement{ fg: RED_FG, bg: ORANGE_BG, character: "▅"},
- SymbolTableElement{ fg: RED_FG, bg: ORANGE_BG, character: "▆"},
- SymbolTableElement{ fg: RED_FG, bg: ORANGE_BG, character: "▇"},
- SymbolTableElement{ fg: RED_FG, bg: ORANGE_BG, character: "█"},
- SymbolTableElement{ fg: RED_FG, bg: BLACK_BG, character: "?"}
+const CHARS_TABLE: [SymbolTableElement; 25] = [
+ SymbolTableElement {
+ fg: GREEN_FG,
+ bg: BLACK_BG,
+ character: "▁",
+ },
+ SymbolTableElement {
+ fg: GREEN_FG,
+ bg: BLACK_BG,
+ character: "▂",
+ },
+ SymbolTableElement {
+ fg: GREEN_FG,
+ bg: BLACK_BG,
+ character: "▃",
+ },
+ SymbolTableElement {
+ fg: GREEN_FG,
+ bg: BLACK_BG,
+ character: "▄",
+ },
+ SymbolTableElement {
+ fg: GREEN_FG,
+ bg: BLACK_BG,
+ character: "▅",
+ },
+ SymbolTableElement {
+ fg: GREEN_FG,
+ bg: BLACK_BG,
+ character: "▆",
+ },
+ SymbolTableElement {
+ fg: GREEN_FG,
+ bg: BLACK_BG,
+ character: "▇",
+ },
+ SymbolTableElement {
+ fg: GREEN_FG,
+ bg: BLACK_BG,
+ character: "█",
+ },
+ SymbolTableElement {
+ fg: ORANGE_FG,
+ bg: GREEN_BG,
+ character: "▁",
+ },
+ SymbolTableElement {
+ fg: ORANGE_FG,
+ bg: GREEN_BG,
+ character: "▂",
+ },
+ SymbolTableElement {
+ fg: ORANGE_FG,
+ bg: GREEN_BG,
+ character: "▃",
+ },
+ SymbolTableElement {
+ fg: ORANGE_FG,
+ bg: GREEN_BG,
+ character: "▄",
+ },
+ SymbolTableElement {
+ fg: ORANGE_FG,
+ bg: GREEN_BG,
+ character: "▅",
+ },
+ SymbolTableElement {
+ fg: ORANGE_FG,
+ bg: GREEN_BG,
+ character: "▆",
+ },
+ SymbolTableElement {
+ fg: ORANGE_FG,
+ bg: GREEN_BG,
+ character: "▇",
+ },
+ SymbolTableElement {
+ fg: ORANGE_FG,
+ bg: GREEN_BG,
+ character: "█",
+ },
+ SymbolTableElement {
+ fg: RED_FG,
+ bg: ORANGE_BG,
+ character: "▁",
+ },
+ SymbolTableElement {
+ fg: RED_FG,
+ bg: ORANGE_BG,
+ character: "▂",
+ },
+ SymbolTableElement {
+ fg: RED_FG,
+ bg: ORANGE_BG,
+ character: "▃",
+ },
+ SymbolTableElement {
+ fg: RED_FG,
+ bg: ORANGE_BG,
+ character: "▄",
+ },
+ SymbolTableElement {
+ fg: RED_FG,
+ bg: ORANGE_BG,
+ character: "▅",
+ },
+ SymbolTableElement {
+ fg: RED_FG,
+ bg: ORANGE_BG,
+ character: "▆",
+ },
+ SymbolTableElement {
+ fg: RED_FG,
+ bg: ORANGE_BG,
+ character: "▇",
+ },
+ SymbolTableElement {
+ fg: RED_FG,
+ bg: ORANGE_BG,
+ character: "█",
+ },
+ SymbolTableElement {
+ fg: RED_FG,
+ bg: BLACK_BG,
+ character: "?",
+ },
];
pub fn print_legende_line() {
for (index, element) in CHARS_TABLE.iter().enumerate() {
let val = 10 * index as u64;
- print!("{} {}{}{}\x1b[0m ", val, element.fg, element.bg, element.character);
+ print!(
+ "{} {}{}{}\x1b[0m ",
+ val, element.fg, element.bg, element.character
+ );
}
- println!("");
+ println!();
}
+// TODO: Simplify this function
pub fn print_ping_line(states: &Vec<State>) {
for state in states {
match state {
State::Received { rtt, .. } => {
let (fg, bg, symbol) = get_symbol_and_colors_for_rrt(*rtt);
- print!("{}{}{}{}", fg, bg, symbol, "\x1b[0m");
+ print!("{}{}{}{}", fg, bg, symbol, RESET);
}
State::Lost => {
let (fg, bg, symbol) = get_symbol_and_colors_for_rrt(250);
- print!("{}{}{}{}", fg, bg, symbol, "\x1b[0m");
+ print!("{}{}{}{}", fg, bg, symbol, RESET);
}
State::NotYetReceived { .. } => {
// fg grey, bg black
@@ -75,16 +180,12 @@ pub fn print_ping_line(states: &Vec<State>) {
}
}
- println!("");
+ println!();
}
fn get_symbol_and_colors_for_rrt(rrt: u64) -> (&'static str, &'static str, &'static str) {
- let index: usize = if rrt >= 240 {
- 24
- } else {
- rrt / 10
- } as usize;
- let SymbolTableElement{fg, bg, character} = CHARS_TABLE[index];
+ let index: usize = if rrt >= 240 { 24 } else { rrt / 10 } as usize;
+ let SymbolTableElement { fg, bg, character } = CHARS_TABLE[index];
(fg, bg, character)
}
diff --git a/src/stats.rs b/src/stats.rs
index 1d2f451..db99fb4 100644
--- a/src/stats.rs
+++ b/src/stats.rs
@@ -21,7 +21,10 @@ pub fn compute_stats(states: &[crate::state::State]) -> Option<Stats> {
let mut rtts: Vec<u64> = Vec::new();
let mut ttls: Vec<u32> = Vec::new();
- let received = states.iter().filter(|state| matches!(state, crate::state::State::Received { .. })).count();
+ let received = states
+ .iter()
+ .filter(|state| matches!(state, crate::state::State::Received { .. }))
+ .count();
let lost = states.len() - received;
if received == 0 {
@@ -42,7 +45,11 @@ pub fn compute_stats(states: &[crate::state::State]) -> Option<Stats> {
let sum_rtt: u64 = rtts.iter().sum();
let avg_rtt = sum_rtt as f64 / rtts.len() as f64;
- let mad_rtt: f64 = rtts.iter().map(|&x| (x as f64 - avg_rtt).abs()).sum::<f64>() / rtts.len() as f64;
+ let mad_rtt: f64 = rtts
+ .iter()
+ .map(|&x| (x as f64 - avg_rtt).abs())
+ .sum::<f64>()
+ / rtts.len() as f64;
// TTL stats
let min_ttl = ttls.iter().min().copied().unwrap();
@@ -51,15 +58,15 @@ pub fn compute_stats(states: &[crate::state::State]) -> Option<Stats> {
let sum_ttl: u32 = ttls.iter().sum();
let avg_ttl = sum_ttl as f64 / ttls.len() as f64;
- Some(Stats {
+ Some(Stats {
received,
lost,
- min_rtt,
- max_rtt,
- avg_rtt,
+ min_rtt,
+ max_rtt,
+ avg_rtt,
mad_rtt,
- min_ttl,
- max_ttl,
- avg_ttl
+ min_ttl,
+ max_ttl,
+ avg_ttl,
})
}