From 37ace1f555b4690db9f8d7820eac24bb9cb7c1de Mon Sep 17 00:00:00 2001 From: Ryan Jacobs Date: Fri, 12 Dec 2025 21:26:07 -0800 Subject: [PATCH] Add options `--ymin` and `--ymax` for setting y-axis scale Option `-0` is an alias for `--ymin=0` --- gping/src/main.rs | 60 ++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 51 insertions(+), 9 deletions(-) diff --git a/gping/src/main.rs b/gping/src/main.rs index b9b053bd..47ebbdb8 100644 --- a/gping/src/main.rs +++ b/gping/src/main.rs @@ -96,6 +96,18 @@ struct Args { #[arg(long, default_value = "0")] horizontal_margin: u16, + /// set y-axis minimum + #[arg(long, help = "Set vertical axis min (ms)")] + ymin: Option, + + /// set y-axis maximum + #[arg(long, help = "Set vertical axis max (ms)")] + ymax: Option, + + /// set y-axis minimum to zero + #[arg(short = '0', help = "Equivalent to --ymin=0", conflicts_with = "ymin")] + ymin_zero: bool, + #[arg( name = "color", short = 'c', @@ -128,15 +140,17 @@ following color names: 'black', 'red', 'green', 'yellow', 'blue', 'magenta', struct App { data: Vec, display_interval: chrono::Duration, - started: chrono::DateTime, + started:chrono::DateTime, + yrange: (Option, Option) } impl App { - fn new(data: Vec, buffer: u64) -> Self { + fn new(data: Vec, buffer: u64, yrange: (Option, Option)) -> Self { App { data, display_interval: chrono::Duration::from_std(Duration::from_secs(buffer)).unwrap(), started: Local::now(), + yrange: yrange } } @@ -149,7 +163,8 @@ impl App { // Find the Y axis bounds for our chart. // This is trickier than the x-axis. We iterate through all our PlotData structs // and find the min/max of all the values. Then we add a 10% buffer to them. - let (min, max) = match self + let (ymin, ymax) = self.yrange; + let (mut min, mut max) = match self .data .iter() .flat_map(|b| b.data.as_slice()) @@ -158,14 +173,27 @@ impl App { .minmax() { MinMaxResult::NoElements => (f64::INFINITY, 0_f64), - MinMaxResult::OneElement(elm) => (elm, elm), - MinMaxResult::MinMax(min, max) => (min, max), + MinMaxResult::OneElement(elm) => ( + ymin.unwrap_or(elm), + elm + ), + MinMaxResult::MinMax(min, max) => ( + ymin.unwrap_or(min), + ymax.unwrap_or(max), + ) }; + // Reject negative bounds + // Show at least 1 ms of y-axis + if ymin != None { + min = min.clamp(0.0, f64::INFINITY); + max = max.clamp(min + 1000.0, f64::INFINITY); + } + // Add a 10% buffer to the top and bottom - let max_10_percent = (max * 10_f64) / 100_f64; - let min_10_percent = (min * 10_f64) / 100_f64; - [min - min_10_percent, max + max_10_percent] + let pos_margin = (max * 10_f64) / 100_f64; + let neg_margin = (min * 10_f64) / 100_f64; + [min - neg_margin, max + pos_margin] } fn x_axis_bounds(&self) -> [f64; 2] { @@ -353,6 +381,15 @@ fn get_host_ipaddr(host: &str, force_ipv4: bool, force_ipv6: bool) -> Result +// to microseconds Option. +fn ms_to_us_option(ms: Option) -> Option { + match ms { + None => None, + Some(x) => Some(1000.0 * (x as f64)) + } +} + fn generate_man_page(path: &Path) -> anyhow::Result<()> { let man = clap_mangen::Man::new(Args::command().version(None).long_version(None)); let mut buffer: Vec = Default::default(); @@ -372,6 +409,11 @@ fn main() -> Result<()> { return Err(anyhow!("At least one host or command must be given (i.e gping google.com). Use --help for a full list of arguments.")); } + let yrange = ( + ms_to_us_option(if args.ymin_zero {Some(0)} else {args.ymin}), + ms_to_us_option(args.ymax) + ); + let mut data = vec![]; let colors = Colors::from(args.color_codes_or_names.iter()); @@ -457,7 +499,7 @@ fn main() -> Result<()> { key_tx.clone(), )); - let mut app = App::new(data, args.buffer); + let mut app = App::new(data, args.buffer, yrange); enable_raw_mode()?; let stdout = io::stdout(); let mut backend = CrosstermBackend::new(BufWriter::with_capacity(1024 * 1024 * 4, stdout));