From 19e89b0cffd5945b78de5c885f05b2e6e0fc8332 Mon Sep 17 00:00:00 2001 From: JuanLeon Lahoz Date: Sat, 24 Sep 2022 16:07:40 +0200 Subject: [PATCH] Reuse code for printing scale info --- src/format/mod.rs | 77 +++++++++++++++++++++++++++++++++++++++ src/plot/histogram.rs | 22 +++++------ src/plot/matchbar.rs | 17 ++++----- src/plot/splittimehist.rs | 14 +++---- src/plot/terms.rs | 17 ++++----- src/plot/timehist.rs | 16 +++----- 6 files changed, 111 insertions(+), 52 deletions(-) diff --git a/src/format/mod.rs b/src/format/mod.rs index 11d1021..2062455 100644 --- a/src/format/mod.rs +++ b/src/format/mod.rs @@ -1,7 +1,12 @@ +use std::fmt; use std::ops::Range; +use yansi::Color::{Blue, Green, Red}; +use yansi::Paint; + // Units-based suffixes for human formatting. const UNITS: &[&str] = &["", " K", " M", " G", " T", " P", " E", " Z", " Y"]; +pub static BAR_CHAR: &str = "∎"; #[derive(Debug)] pub struct F64Formatter { @@ -63,9 +68,46 @@ impl F64Formatter { } } +pub struct HorizontalScale { + /// How many units are represented by a char + scale: usize, +} + +impl HorizontalScale { + pub fn new(scale: usize) -> HorizontalScale { + HorizontalScale { + scale: 1.max(scale), + } + } + + pub fn get_bar(&self, units: usize) -> Paint { + Red.paint(format!("{:∎ Paint { + Green.paint(format!("{:width$}", units, width = width)) + } + + pub fn get_scale(&self) -> usize { + self.scale + } +} + +impl fmt::Display for HorizontalScale { + fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + writeln!( + formatter, + "Each {} represents a count of {}", + Red.paint(BAR_CHAR), + Blue.paint(self.scale.to_string()), + ) + } +} + #[cfg(test)] mod tests { use super::*; + use yansi::Paint; #[test] fn test_basic_format() { @@ -155,4 +197,39 @@ mod tests { "-0.0 M" ); } + + #[test] + fn test_horizontal_scale() { + Paint::disable(); + assert_eq!( + format!("{}", HorizontalScale::new(123)), + format!("Each {BAR_CHAR} represents a count of 123\n") + ); + } + + #[test] + fn test_horizontal_scale_with_zero_scale() { + Paint::disable(); + let scale = HorizontalScale::new(0); + assert_eq!(scale.get_scale(), 1); + assert_eq!( + format!("{}", scale), + format!("Each {BAR_CHAR} represents a count of 1\n") + ); + } + + #[test] + fn test_horizontal_scale_bar() { + let scale = HorizontalScale::new(10); + assert_eq!( + scale.get_bar(80), + Red.paint(format!("{:∎ fmt::Result { let width_range = self.get_width(hist); let width_count = ((hist.top as f64).log10().ceil() as usize).max(1); - let divisor = 1.max(hist.top / self.get_max_bar_len(width_range + width_count)); - writeln!( - f, - "each {} represents a count of {}", - Red.paint("∎"), - Blue.paint(divisor.to_string()), - )?; + let horizontal_scale = + HorizontalScale::new(hist.top / self.get_max_bar_len(width_range + width_count)); + writeln!(f, "{}", horizontal_scale)?; for x in hist.vec.iter() { - self.write_bucket(f, x, divisor, width_range, width_count)?; + self.write_bucket(f, x, &horizontal_scale, width_range, width_count)?; } Ok(()) } @@ -147,7 +143,7 @@ impl HistWriter { &self, f: &mut fmt::Formatter, bucket: &Bucket, - divisor: usize, + horizontal_scale: &HorizontalScale, width: usize, width_count: usize, ) -> fmt::Result { @@ -160,8 +156,8 @@ impl HistWriter { self.formatter.format(bucket.range.end), width = width, )), - count = Green.paint(format!("{:width$}", bucket.count, width = width_count)), - bar = Red.paint(format!("{:∎ fmt::Result { let width = f.width().unwrap_or(100); - let divisor = 1.max(self.top_values / width); + let horizontal_scale = HorizontalScale::new(self.top_values / width); let width_count = format!("{}", self.top_values).len(); writeln!( f, @@ -64,19 +66,14 @@ impl fmt::Display for MatchBar { self.vec.iter().map(|r| r.count).sum::() )), )?; - writeln!( - f, - "Each {} represents a count of {}", - Red.paint("∎"), - Blue.paint(divisor.to_string()), - )?; + writeln!(f, "{}", horizontal_scale)?; for row in self.vec.iter() { writeln!( f, "[{label}] [{count}] {bar}", label = Blue.paint(format!("{:width$}", row.label, width = self.top_lenght)), - count = Green.paint(format!("{:width$}", row.count, width = width_count)), - bar = Red.paint(format!("{:∎(); let top = self.vec.iter().map(|r| r.total()).max().unwrap_or(1); - let divisor = 1.max(top / width); + let horizontal_scale = HorizontalScale::new(top / width); // These are the widths of every count column let widths: Vec = (0..self.strings.len()) .map(|i| { @@ -163,15 +164,10 @@ impl fmt::Display for SplitTimeHistogram { let total = self.vec.iter().map(|r| r.count[i]).sum::(); writeln!(f, "{}: {}.", COLORS[i].paint(s), total)?; } - writeln!( - f, - "Each {} represents a count of {}", - Red.paint("∎"), - divisor - )?; + writeln!(f, "{}", horizontal_scale)?; let ts_fmt = date_fmt_string(self.step.num_seconds()); for row in self.vec.iter() { - self.fmt_row(f, row, divisor, &widths, ts_fmt)?; + self.fmt_row(f, row, horizontal_scale.get_scale(), &widths, ts_fmt)?; } Ok(()) } diff --git a/src/plot/terms.rs b/src/plot/terms.rs index 85be93c..204e802 100644 --- a/src/plot/terms.rs +++ b/src/plot/terms.rs @@ -1,7 +1,9 @@ use std::collections::HashMap; use std::fmt; -use yansi::Color::{Blue, Green, Red}; +use yansi::Color::Blue; + +use crate::format::HorizontalScale; #[derive(Debug)] /// A struct holding data to plot a Histogram of the most frequent terms in an @@ -42,21 +44,16 @@ impl fmt::Display for CommonTerms { counts.sort_by(|a, b| b.1.cmp(a.1)); let values = &counts[..self.lines.min(counts.len())]; let label_width = values.iter().fold(1, |acc, x| acc.max(x.0.len())); - let divisor = 1.max(counts[0].1 / width); + let horizontal_scale = HorizontalScale::new(counts[0].1 / width); let width_count = format!("{}", counts[0].1).len(); - writeln!( - f, - "Each {} represents a count of {}", - Red.paint("∎"), - Blue.paint(divisor.to_string()), - )?; + writeln!(f, "{}", horizontal_scale)?; for (term, count) in values.iter() { writeln!( f, "[{label}] [{count}] {bar}", label = Blue.paint(format!("{:>width$}", term, width = label_width)), - count = Green.paint(format!("{:width$}", count, width = width_count)), - bar = Red.paint(format!("{:∎ fmt::Result { let width = f.width().unwrap_or(100); - let divisor = 1.max(self.top / width); + let horizontal_scale = HorizontalScale::new(self.top / width); let width_count = format!("{}", self.top).len(); writeln!( f, @@ -107,20 +108,15 @@ impl fmt::Display for TimeHistogram { self.vec.iter().map(|r| r.count).sum::() )), )?; - writeln!( - f, - "Each {} represents a count of {}", - Red.paint("∎"), - Blue.paint(divisor.to_string()), - )?; + writeln!(f, "{}", horizontal_scale)?; let ts_fmt = date_fmt_string(self.step.num_seconds()); for row in self.vec.iter() { writeln!( f, "[{label}] [{count}] {bar}", label = Blue.paint(format!("{}", row.start.format(ts_fmt))), - count = Green.paint(format!("{:width$}", row.count, width = width_count)), - bar = Red.paint(format!("{:∎