|
|
|
|
@ -3,12 +3,15 @@ use std::env;
|
|
|
|
|
|
|
|
|
|
use isatty::stdout_isatty; |
|
|
|
|
use regex::Regex; |
|
|
|
|
use yansi::Color::{Red, Yellow}; |
|
|
|
|
use yansi::Paint; |
|
|
|
|
|
|
|
|
|
#[macro_use] |
|
|
|
|
extern crate derive_builder; |
|
|
|
|
|
|
|
|
|
#[macro_use] |
|
|
|
|
extern crate log; |
|
|
|
|
use simplelog::{ColorChoice, ConfigBuilder, LevelFilter, TermLogger, TerminalMode}; |
|
|
|
|
|
|
|
|
|
mod app; |
|
|
|
|
mod dateparser; |
|
|
|
|
mod histogram; |
|
|
|
|
@ -18,9 +21,13 @@ mod reader;
|
|
|
|
|
mod stats; |
|
|
|
|
mod timehist; |
|
|
|
|
|
|
|
|
|
fn disable_color_if_needed(option: &str) { |
|
|
|
|
fn configure_output(option: &str, verbose: bool) { |
|
|
|
|
let mut color_choice = ColorChoice::Auto; |
|
|
|
|
match option { |
|
|
|
|
"no" => Paint::disable(), |
|
|
|
|
"no" => { |
|
|
|
|
Paint::disable(); |
|
|
|
|
color_choice = ColorChoice::Never; |
|
|
|
|
} |
|
|
|
|
"auto" => match env::var("TERM") { |
|
|
|
|
Ok(value) if value == "dumb" => Paint::disable(), |
|
|
|
|
_ => { |
|
|
|
|
@ -29,21 +36,35 @@ fn disable_color_if_needed(option: &str) {
|
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
}, |
|
|
|
|
"yes" => { |
|
|
|
|
color_choice = ColorChoice::Always; |
|
|
|
|
} |
|
|
|
|
_ => (), |
|
|
|
|
} |
|
|
|
|
}; |
|
|
|
|
TermLogger::init( |
|
|
|
|
if verbose { |
|
|
|
|
LevelFilter::Info |
|
|
|
|
} else { |
|
|
|
|
LevelFilter::Debug |
|
|
|
|
}, |
|
|
|
|
ConfigBuilder::new() |
|
|
|
|
.set_time_level(LevelFilter::Trace) |
|
|
|
|
.set_thread_level(LevelFilter::Trace) |
|
|
|
|
.set_target_level(LevelFilter::Trace) |
|
|
|
|
.build(), |
|
|
|
|
TerminalMode::Stderr, |
|
|
|
|
color_choice, |
|
|
|
|
) |
|
|
|
|
.unwrap(); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
fn get_reader(matches: &ArgMatches, verbose: bool) -> reader::DataReader { |
|
|
|
|
fn get_reader(matches: &ArgMatches) -> reader::DataReader { |
|
|
|
|
let mut builder = reader::DataReaderBuilder::default(); |
|
|
|
|
builder.verbose(verbose); |
|
|
|
|
if matches.is_present("min") || matches.is_present("max") { |
|
|
|
|
let min = matches.value_of_t("min").unwrap_or(f64::NEG_INFINITY); |
|
|
|
|
let max = matches.value_of_t("max").unwrap_or(f64::INFINITY); |
|
|
|
|
if min > max { |
|
|
|
|
eprintln!( |
|
|
|
|
"[{}] Minimum should be smaller than maximum", |
|
|
|
|
Red.paint("ERROR") |
|
|
|
|
); |
|
|
|
|
error!("Minimum should be smaller than maximum"); |
|
|
|
|
std::process::exit(1); |
|
|
|
|
} |
|
|
|
|
builder.range(min..max); |
|
|
|
|
@ -54,7 +75,7 @@ fn get_reader(matches: &ArgMatches, verbose: bool) -> reader::DataReader {
|
|
|
|
|
builder.regex(re); |
|
|
|
|
} |
|
|
|
|
_ => { |
|
|
|
|
eprintln!("[{}]: Failed to parse regex {}", Red.paint("ERROR"), string); |
|
|
|
|
error!("Failed to parse regex {}", string); |
|
|
|
|
std::process::exit(1); |
|
|
|
|
} |
|
|
|
|
}; |
|
|
|
|
@ -62,11 +83,11 @@ fn get_reader(matches: &ArgMatches, verbose: bool) -> reader::DataReader {
|
|
|
|
|
builder.build().unwrap() |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
fn histogram(matches: &ArgMatches, verbose: bool) { |
|
|
|
|
let reader = get_reader(&matches, verbose); |
|
|
|
|
fn histogram(matches: &ArgMatches) { |
|
|
|
|
let reader = get_reader(&matches); |
|
|
|
|
let vec = reader.read(matches.value_of("input").unwrap()); |
|
|
|
|
if vec.is_empty() { |
|
|
|
|
eprintln!("[{}] No data to process", Yellow.paint("WARN")); |
|
|
|
|
warn!("No data to process"); |
|
|
|
|
std::process::exit(0); |
|
|
|
|
} |
|
|
|
|
let stats = stats::Stats::new(&vec); |
|
|
|
|
@ -77,14 +98,14 @@ fn histogram(matches: &ArgMatches, verbose: bool) {
|
|
|
|
|
let mut histogram = |
|
|
|
|
histogram::Histogram::new(intervals, (stats.max - stats.min) / intervals as f64, stats); |
|
|
|
|
histogram.load(&vec); |
|
|
|
|
println!("{:width$}", histogram, width = width); |
|
|
|
|
print!("{:width$}", histogram, width = width); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
fn plot(matches: &ArgMatches, verbose: bool) { |
|
|
|
|
let reader = get_reader(&matches, verbose); |
|
|
|
|
fn plot(matches: &ArgMatches) { |
|
|
|
|
let reader = get_reader(&matches); |
|
|
|
|
let vec = reader.read(matches.value_of("input").unwrap()); |
|
|
|
|
if vec.is_empty() { |
|
|
|
|
eprintln!("[{}] No data to process", Yellow.paint("WARN")); |
|
|
|
|
warn!("No data to process"); |
|
|
|
|
std::process::exit(0); |
|
|
|
|
} |
|
|
|
|
let mut plot = plot::Plot::new( |
|
|
|
|
@ -117,7 +138,7 @@ fn timehist(matches: &ArgMatches) {
|
|
|
|
|
builder.regex(re); |
|
|
|
|
} |
|
|
|
|
_ => { |
|
|
|
|
eprintln!("[{}]: Failed to parse regex {}", Red.paint("ERROR"), string); |
|
|
|
|
error!("Failed to parse regex {}", string); |
|
|
|
|
std::process::exit(1); |
|
|
|
|
} |
|
|
|
|
}; |
|
|
|
|
@ -129,7 +150,7 @@ fn timehist(matches: &ArgMatches) {
|
|
|
|
|
let reader = builder.build().unwrap(); |
|
|
|
|
let vec = reader.read(matches.value_of("input").unwrap()); |
|
|
|
|
if vec.len() <= 1 { |
|
|
|
|
eprintln!("[{}] Not enough data to process", Yellow.paint("WARN")); |
|
|
|
|
warn!("Not enough data to process"); |
|
|
|
|
std::process::exit(0); |
|
|
|
|
} |
|
|
|
|
let mut timehist = timehist::TimeHistogram::new(matches.value_of_t("intervals").unwrap(), &vec); |
|
|
|
|
@ -140,16 +161,16 @@ fn timehist(matches: &ArgMatches) {
|
|
|
|
|
|
|
|
|
|
fn main() { |
|
|
|
|
let matches = app::get_app().get_matches(); |
|
|
|
|
let verbose = matches.is_present("verbose"); |
|
|
|
|
if let Some(c) = matches.value_of("color") { |
|
|
|
|
disable_color_if_needed(c); |
|
|
|
|
} |
|
|
|
|
configure_output( |
|
|
|
|
matches.value_of("color").unwrap(), |
|
|
|
|
matches.is_present("verbose"), |
|
|
|
|
); |
|
|
|
|
match matches.subcommand() { |
|
|
|
|
Some(("hist", subcommand_matches)) => { |
|
|
|
|
histogram(subcommand_matches, verbose); |
|
|
|
|
histogram(subcommand_matches); |
|
|
|
|
} |
|
|
|
|
Some(("plot", subcommand_matches)) => { |
|
|
|
|
plot(subcommand_matches, verbose); |
|
|
|
|
plot(subcommand_matches); |
|
|
|
|
} |
|
|
|
|
Some(("matches", subcommand_matches)) => { |
|
|
|
|
matchbar(subcommand_matches); |
|
|
|
|
|