You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
311 lines
10 KiB
311 lines
10 KiB
use clap::{self, Arg, Command}; |
|
|
|
fn add_input(cmd: Command) -> Command { |
|
cmd.arg( |
|
Arg::new("input") |
|
.help("Input file") |
|
.default_value("-") |
|
.long_help("If not present or a single dash, standard input will be used"), |
|
) |
|
} |
|
|
|
fn add_input_as_option(cmd: Command) -> Command { |
|
cmd.arg( |
|
Arg::new("input") |
|
.long("input") |
|
.default_value("-") |
|
.long_help("If not present or a single dash, standard input will be used") |
|
.takes_value(true), |
|
) |
|
} |
|
|
|
fn add_min_max(cmd: Command) -> Command { |
|
cmd.arg( |
|
Arg::new("max") |
|
.long("max") |
|
.short('M') |
|
.allow_hyphen_values(true) |
|
.help("Filter out values bigger than this") |
|
.takes_value(true), |
|
) |
|
.arg( |
|
Arg::new("min") |
|
.long("min") |
|
.short('m') |
|
.allow_hyphen_values(true) |
|
.help("Filter out values smaller than this") |
|
.takes_value(true), |
|
) |
|
} |
|
|
|
fn add_regex(cmd: Command) -> Command { |
|
const LONG_RE_ABOUT: &str = "\ |
|
A regular expression used for capturing the values to be plotted inside input |
|
lines. |
|
|
|
By default this will use a capture group named `value`. If not present, it will |
|
use first capture group. |
|
|
|
If no regex is used, the whole input lines will be matched. |
|
|
|
Examples of regex are ' 200 \\d+ ([0-9.]+)' (where there is one anonymous capture |
|
group) and 'a(a)? (?P<value>[0-9.]+)' (where there are two capture groups, and |
|
the named one will be used). |
|
"; |
|
cmd.arg( |
|
Arg::new("regex") |
|
.long("regex") |
|
.short('R') |
|
.help("Use a regex to capture input values") |
|
.long_help(LONG_RE_ABOUT) |
|
.takes_value(true), |
|
) |
|
} |
|
|
|
fn add_non_capturing_regex(cmd: Command) -> Command { |
|
cmd.arg( |
|
Arg::new("regex") |
|
.long("regex") |
|
.short('R') |
|
.help("Filter out lines where regex is not present") |
|
.takes_value(true), |
|
) |
|
} |
|
|
|
fn add_width(cmd: Command) -> Command { |
|
cmd.arg( |
|
Arg::new("width") |
|
.long("width") |
|
.short('w') |
|
.help("Use this many characters as terminal width") |
|
.default_value("110") |
|
.takes_value(true), |
|
) |
|
} |
|
|
|
fn add_intervals(cmd: Command) -> Command { |
|
cmd.arg( |
|
Arg::new("intervals") |
|
.long("intervals") |
|
.short('i') |
|
.help("Use no more than this amount of buckets to classify data") |
|
.default_value("20") |
|
.takes_value(true), |
|
) |
|
} |
|
|
|
fn add_precision(cmd: Command) -> Command { |
|
cmd.arg( |
|
Arg::new("precision") |
|
.long("precision") |
|
.short('p') |
|
.help("Show that number of decimals (if omitted, 'human' units will be used)") |
|
.default_value("-1") |
|
.takes_value(true), |
|
) |
|
} |
|
|
|
pub fn get_app() -> Command<'static> { |
|
let mut hist = Command::new("hist") |
|
.version(clap::crate_version!()) |
|
.about("Plot an histogram from input values"); |
|
hist = add_input(add_regex(add_width(add_min_max(add_precision( |
|
add_intervals(hist), |
|
))))); |
|
|
|
let mut plot = Command::new("plot") |
|
.version(clap::crate_version!()) |
|
.about("Plot an 2d x-y graph where y-values are averages of input values") |
|
.arg( |
|
Arg::new("height") |
|
.long("height") |
|
.short('H') |
|
.help("Use that many `rows` for the plot") |
|
.default_value("40") |
|
.takes_value(true), |
|
); |
|
plot = add_input(add_regex(add_width(add_min_max(add_precision(plot))))); |
|
|
|
let mut matches = Command::new("matches") |
|
.version(clap::crate_version!()) |
|
.allow_missing_positional(true) |
|
.about("Plot barchar with counts of occurences of matches params"); |
|
matches = add_input_as_option(add_width(matches)).arg( |
|
Arg::new("match") |
|
.help("Count maches for those strings") |
|
.required(true) |
|
.takes_value(true) |
|
.multiple_occurrences(true), |
|
); |
|
|
|
let mut timehist = |
|
Command::new("timehist") |
|
.version(clap::crate_version!()) |
|
.about("Plot histogram with amount of matches over time") |
|
.arg( |
|
Arg::new("format") |
|
.long("format") |
|
.short('f') |
|
.help("Use this string formatting") |
|
.takes_value(true), |
|
) |
|
.arg( |
|
Arg::new("duration") |
|
.long("duration") |
|
.help("Cap the time interval at that duration (example: '3h 5min')") |
|
.takes_value(true), |
|
) |
|
.arg(Arg::new("early-stop").long("early-stop").help( |
|
"If duration flag is used, assume monotonic times and stop as soon as possible", |
|
)); |
|
timehist = add_input(add_width(add_non_capturing_regex(add_intervals(timehist)))); |
|
|
|
let mut splittimehist = Command::new("split-timehist") |
|
.version(clap::crate_version!()) |
|
.about("Plot histogram of with amount of matches over time, split per match type") |
|
.arg( |
|
Arg::new("format") |
|
.long("format") |
|
.short('f') |
|
.help("Use this string formatting") |
|
.takes_value(true), |
|
); |
|
splittimehist = add_input_as_option(add_width(add_intervals(splittimehist))).arg( |
|
Arg::new("match") |
|
.help("Count maches for those strings") |
|
.required(true) |
|
.takes_value(true) |
|
.multiple_occurrences(true), |
|
); |
|
|
|
let mut common_terms = Command::new("common-terms") |
|
.version(clap::crate_version!()) |
|
.about("Plot histogram with most common terms in input lines"); |
|
common_terms = add_input(add_regex(add_width(common_terms))).arg( |
|
Arg::new("lines") |
|
.long("lines") |
|
.short('l') |
|
.help("Display that many lines, sorting by most frequent") |
|
.default_value("10") |
|
.takes_value(true), |
|
); |
|
|
|
Command::new("lowcharts") |
|
.author(clap::crate_authors!()) |
|
.version(clap::crate_version!()) |
|
.about(clap::crate_description!()) |
|
.max_term_width(100) |
|
.subcommand_required(true) |
|
.arg( |
|
Arg::new("color") |
|
.short('c') |
|
.long("color") |
|
.help("Use colors in the output") |
|
.possible_values(&["auto", "no", "yes"]) |
|
.default_value("auto") |
|
.takes_value(true), |
|
) |
|
.arg( |
|
Arg::new("verbose") |
|
.short('v') |
|
.long("verbose") |
|
.help("Be more verbose") |
|
.takes_value(false), |
|
) |
|
.subcommand(hist) |
|
.subcommand(plot) |
|
.subcommand(matches) |
|
.subcommand(timehist) |
|
.subcommand(splittimehist) |
|
.subcommand(common_terms) |
|
} |
|
|
|
#[cfg(test)] |
|
mod tests { |
|
|
|
use super::*; |
|
|
|
#[test] |
|
fn hist_subcommand_arg_parsing() { |
|
let arg_vec = vec!["lowcharts", "--verbose", "hist", "foo"]; |
|
let m = get_app().get_matches_from(arg_vec); |
|
assert!(m.is_present("verbose")); |
|
let sub_m = m.subcommand_matches("hist").unwrap(); |
|
assert_eq!("foo", sub_m.value_of("input").unwrap()); |
|
assert!(sub_m.value_of("max").is_none()); |
|
assert!(sub_m.value_of("min").is_none()); |
|
assert!(sub_m.value_of("regex").is_none()); |
|
assert_eq!("110", sub_m.value_of("width").unwrap()); |
|
assert_eq!("20", sub_m.value_of("intervals").unwrap()); |
|
} |
|
|
|
#[test] |
|
fn plot_subcommand_arg_parsing() { |
|
let arg_vec = vec![ |
|
"lowcharts", |
|
"plot", |
|
"--max", |
|
"1.1", |
|
"-m", |
|
"0.9", |
|
"--height", |
|
"11", |
|
]; |
|
let m = get_app().get_matches_from(arg_vec); |
|
assert!(!m.is_present("verbose")); |
|
let sub_m = m.subcommand_matches("plot").unwrap(); |
|
assert_eq!("-", sub_m.value_of("input").unwrap()); |
|
assert_eq!("1.1", sub_m.value_of("max").unwrap()); |
|
assert_eq!("0.9", sub_m.value_of("min").unwrap()); |
|
assert_eq!("11", sub_m.value_of("height").unwrap()); |
|
} |
|
|
|
#[test] |
|
fn matches_subcommand_arg_parsing() { |
|
let arg_vec = vec!["lowcharts", "matches", "A", "B", "C"]; |
|
let m = get_app().get_matches_from(arg_vec); |
|
let sub_m = m.subcommand_matches("matches").unwrap(); |
|
assert_eq!("-", sub_m.value_of("input").unwrap()); |
|
assert_eq!( |
|
vec!["A", "B", "C"], |
|
sub_m.values_of("match").unwrap().collect::<Vec<&str>>() |
|
); |
|
let arg_vec = vec!["lowcharts", "matches", "A", "--input", "B", "C"]; |
|
let m = get_app().get_matches_from(arg_vec); |
|
let sub_m = m.subcommand_matches("matches").unwrap(); |
|
assert_eq!("B", sub_m.value_of("input").unwrap()); |
|
assert_eq!( |
|
vec!["A", "C"], |
|
sub_m.values_of("match").unwrap().collect::<Vec<&str>>() |
|
); |
|
} |
|
|
|
#[test] |
|
fn timehist_subcommand_arg_parsing() { |
|
let arg_vec = vec!["lowcharts", "timehist", "--regex", "foo", "some"]; |
|
let m = get_app().get_matches_from(arg_vec); |
|
let sub_m = m.subcommand_matches("timehist").unwrap(); |
|
assert_eq!("some", sub_m.value_of("input").unwrap()); |
|
assert_eq!("foo", sub_m.value_of("regex").unwrap()); |
|
} |
|
|
|
#[test] |
|
fn splittimehist_subcommand_arg_parsing() { |
|
let arg_vec = vec!["lowcharts", "split-timehist", "foo", "bar"]; |
|
let m = get_app().get_matches_from(arg_vec); |
|
let sub_m = m.subcommand_matches("split-timehist").unwrap(); |
|
assert_eq!( |
|
vec!["foo", "bar"], |
|
sub_m.values_of("match").unwrap().collect::<Vec<&str>>() |
|
); |
|
} |
|
|
|
#[test] |
|
fn terms_subcommand_arg_parsing() { |
|
let arg_vec = vec!["lowcharts", "common-terms", "--regex", "foo", "some"]; |
|
let m = get_app().get_matches_from(arg_vec); |
|
let sub_m = m.subcommand_matches("common-terms").unwrap(); |
|
assert_eq!("some", sub_m.value_of("input").unwrap()); |
|
assert_eq!("foo", sub_m.value_of("regex").unwrap()); |
|
} |
|
}
|
|
|