Browse Source

refactor: organize code in modules

pull/2/head
JuanLeon Lahoz 5 years ago
parent
commit
c6dce04b94
  1. 38
      src/main.rs
  2. 0
      src/plot/histogram.rs
  3. 0
      src/plot/matchbar.rs
  4. 9
      src/plot/mod.rs
  5. 0
      src/plot/timehist.rs
  6. 14
      src/plot/xy.rs
  7. 134
      src/read/buckets.rs
  8. 0
      src/read/dateparser.rs
  9. 24
      src/read/mod.rs
  10. 128
      src/read/times.rs
  11. 0
      src/stats/mod.rs
  12. 2
      tests/integration_tests.rs

38
src/main.rs

@ -1,25 +1,19 @@
use clap::ArgMatches;
use std::env;
mod app;
mod plot;
mod read;
mod stats;
use isatty::stdout_isatty;
use regex::Regex;
use yansi::Paint;
use std::env;
#[macro_use]
extern crate derive_builder;
#[macro_use]
extern crate log;
use clap::ArgMatches;
use isatty::stdout_isatty;
use regex::Regex;
use simplelog::{ColorChoice, ConfigBuilder, LevelFilter, TermLogger, TerminalMode};
mod app;
mod dateparser;
mod histogram;
mod matchbar;
mod plot;
mod reader;
mod stats;
mod timehist;
use yansi::Paint;
fn configure_output(option: &str, verbose: bool) {
let mut color_choice = ColorChoice::Auto;
@ -58,8 +52,8 @@ fn configure_output(option: &str, verbose: bool) {
.unwrap();
}
fn get_reader(matches: &ArgMatches) -> reader::DataReader {
let mut builder = reader::DataReaderBuilder::default();
fn get_reader(matches: &ArgMatches) -> read::DataReader {
let mut builder = read::DataReaderBuilder::default();
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);
@ -96,7 +90,7 @@ fn histogram(matches: &ArgMatches) {
intervals = intervals.min(vec.len());
let mut histogram =
histogram::Histogram::new(intervals, (stats.max - stats.min) / intervals as f64, stats);
plot::Histogram::new(intervals, (stats.max - stats.min) / intervals as f64, stats);
histogram.load(&vec);
print!("{:width$}", histogram, width = width);
}
@ -108,7 +102,7 @@ fn plot(matches: &ArgMatches) {
warn!("No data to process");
std::process::exit(0);
}
let mut plot = plot::Plot::new(
let mut plot = plot::XyPlot::new(
matches.value_of_t("width").unwrap(),
matches.value_of_t("height").unwrap(),
stats::Stats::new(&vec),
@ -118,7 +112,7 @@ fn plot(matches: &ArgMatches) {
}
fn matchbar(matches: &ArgMatches) {
let reader = reader::DataReader::default();
let reader = read::DataReader::default();
let width = matches.value_of_t("width").unwrap();
print!(
"{:width$}",
@ -131,7 +125,7 @@ fn matchbar(matches: &ArgMatches) {
}
fn timehist(matches: &ArgMatches) {
let mut builder = reader::TimeReaderBuilder::default();
let mut builder = read::TimeReaderBuilder::default();
if let Some(string) = matches.value_of("regex") {
match Regex::new(&string) {
Ok(re) => {
@ -153,7 +147,7 @@ fn timehist(matches: &ArgMatches) {
warn!("Not enough data to process");
std::process::exit(0);
}
let mut timehist = timehist::TimeHistogram::new(matches.value_of_t("intervals").unwrap(), &vec);
let mut timehist = plot::TimeHistogram::new(matches.value_of_t("intervals").unwrap(), &vec);
timehist.load(&vec);
print!("{:width$}", timehist, width = width);

0
src/histogram.rs → src/plot/histogram.rs

0
src/matchbar.rs → src/plot/matchbar.rs

9
src/plot/mod.rs

@ -0,0 +1,9 @@
pub use self::histogram::Histogram;
pub use self::matchbar::{MatchBar, MatchBarRow};
pub use self::timehist::TimeHistogram;
pub use self::xy::XyPlot;
mod histogram;
mod matchbar;
mod timehist;
mod xy;

0
src/timehist.rs → src/plot/timehist.rs

14
src/plot.rs → src/plot/xy.rs

@ -6,7 +6,7 @@ use yansi::Color::{Blue, Red};
use crate::stats::Stats;
#[derive(Debug)]
pub struct Plot {
pub struct XyPlot {
x_axis: Vec<f64>,
y_axis: Vec<f64>,
width: usize,
@ -14,9 +14,9 @@ pub struct Plot {
stats: Stats,
}
impl Plot {
pub fn new(width: usize, height: usize, stats: Stats) -> Plot {
Plot {
impl XyPlot {
pub fn new(width: usize, height: usize, stats: Stats) -> XyPlot {
XyPlot {
x_axis: Vec::with_capacity(width),
y_axis: Vec::with_capacity(height),
width,
@ -40,7 +40,7 @@ impl Plot {
}
}
impl fmt::Display for Plot {
impl fmt::Display for XyPlot {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self.stats)?;
let _step = (self.stats.max - self.stats.min) / self.height as f64;
@ -86,7 +86,7 @@ mod tests {
#[test]
fn basic_test() {
let stats = Stats::new(&[-1.0, 4.0]);
let mut plot = Plot::new(3, 5, stats);
let mut plot = XyPlot::new(3, 5, stats);
plot.load(&[-1.0, 0.0, 1.0, 2.0, 3.0, 4.0, -1.0]);
assert_float_eq!(plot.x_axis[0], -0.5, rmax <= f64::EPSILON);
assert_float_eq!(plot.x_axis[1], 1.5, rmax <= f64::EPSILON);
@ -100,7 +100,7 @@ mod tests {
#[test]
fn display_test() {
let stats = Stats::new(&[-1.0, 4.0]);
let mut plot = Plot::new(3, 5, stats);
let mut plot = XyPlot::new(3, 5, stats);
plot.load(&[-1.0, 0.0, 1.0, 2.0, 3.0, 4.0, -1.0]);
Paint::disable();
let display = format!("{}", plot);

134
src/reader.rs → src/read/buckets.rs

@ -1,12 +1,10 @@
use std::fs::File;
use std::io::{self, BufRead, BufReader};
use std::io::BufRead;
use std::ops::Range;
use chrono::{DateTime, FixedOffset};
use regex::Regex;
use crate::dateparser::LogDateParser;
use crate::matchbar::{MatchBar, MatchBarRow};
use crate::plot::{MatchBar, MatchBarRow};
use crate::read::open_file;
#[derive(Debug, Default, Builder)]
pub struct DataReader {
@ -90,78 +88,6 @@ impl DataReader {
}
}
#[derive(Default, Builder)]
pub struct TimeReader {
#[builder(setter(strip_option), default)]
regex: Option<Regex>,
#[builder(setter(strip_option), default)]
ts_format: Option<String>,
}
impl TimeReader {
pub fn read(&self, path: &str) -> Vec<DateTime<FixedOffset>> {
let mut vec: Vec<DateTime<FixedOffset>> = Vec::new();
let mut iterator = open_file(path).lines();
let first_line = match iterator.next() {
Some(Ok(as_string)) => as_string,
Some(Err(error)) => {
error!("{}", error);
return vec;
}
_ => return vec,
};
let parser = match &self.ts_format {
Some(ts_format) => match LogDateParser::new_with_format(&first_line, &ts_format) {
Ok(p) => p,
Err(error) => {
error!("Could not figure out parsing strategy: {}", error);
return vec;
}
},
None => match LogDateParser::new_with_guess(&first_line) {
Ok(p) => p,
Err(error) => {
error!("Could not figure out parsing strategy: {}", error);
return vec;
}
},
};
if let Ok(x) = parser.parse(&first_line) {
vec.push(x);
}
for line in iterator {
match line {
Ok(string) => {
if let Ok(x) = parser.parse(&string) {
if let Some(re) = &self.regex {
if re.is_match(&string) {
vec.push(x);
}
} else {
vec.push(x);
}
}
}
Err(error) => error!("{}", error),
}
}
vec
}
}
fn open_file(path: &str) -> Box<dyn io::BufRead> {
match path {
"-" => Box::new(BufReader::new(io::stdin())),
_ => match File::open(path) {
Ok(fd) => Box::new(io::BufReader::new(fd)),
Err(error) => {
error!("Could not open {}: {}", path, error);
std::process::exit(0);
}
},
}
}
#[cfg(test)]
mod tests {
@ -278,58 +204,4 @@ mod tests {
Err(_) => assert!(false, "Could not create temp file"),
}
}
#[test]
fn time_reader_guessing_with_regex() {
let mut builder = TimeReaderBuilder::default();
builder.regex(Regex::new("f.o").unwrap());
let reader = builder.build().unwrap();
match NamedTempFile::new() {
Ok(ref mut file) => {
writeln!(file, "[2021-04-15T06:25:31+00:00] foobar").unwrap();
writeln!(file, "[2021-04-15T06:26:31+00:00] bar").unwrap();
writeln!(file, "[2021-04-15T06:27:31+00:00] foobar").unwrap();
writeln!(file, "[2021-04-15T06:28:31+00:00] foobar").unwrap();
writeln!(file, "none").unwrap();
let ts = reader.read(file.path().to_str().unwrap());
assert_eq!(ts.len(), 3);
assert_eq!(
ts[0],
DateTime::parse_from_rfc3339("2021-04-15T06:25:31+00:00").unwrap()
);
assert_eq!(
ts[2],
DateTime::parse_from_rfc3339("2021-04-15T06:28:31+00:00").unwrap()
);
}
Err(_) => assert!(false, "Could not create temp file"),
}
}
#[test]
fn time_reader_with_format() {
let mut builder = TimeReaderBuilder::default();
builder.ts_format(String::from("%Y_%m_%d %H:%M"));
let reader = builder.build().unwrap();
match NamedTempFile::new() {
Ok(ref mut file) => {
writeln!(file, "_2021_04_15 06:25] foobar").unwrap();
writeln!(file, "_2021_04_15 06:26] bar").unwrap();
writeln!(file, "_2021_04_15 06:27] foobar").unwrap();
writeln!(file, "_2021_04_15 06:28] foobar").unwrap();
writeln!(file, "none").unwrap();
let ts = reader.read(file.path().to_str().unwrap());
assert_eq!(ts.len(), 4);
assert_eq!(
ts[0],
DateTime::parse_from_rfc3339("2021-04-15T06:25:00+00:00").unwrap()
);
assert_eq!(
ts[3],
DateTime::parse_from_rfc3339("2021-04-15T06:28:00+00:00").unwrap()
);
}
Err(_) => assert!(false, "Could not create temp file"),
}
}
}

0
src/dateparser.rs → src/read/dateparser.rs

24
src/read/mod.rs

@ -0,0 +1,24 @@
pub use self::buckets::{DataReader, DataReaderBuilder};
pub use self::times::TimeReaderBuilder;
mod buckets;
mod dateparser;
mod times;
use std::fs::File;
use std::io::{self, BufReader};
/// Return io::BufRead from a path, falling back to using stdin if path is "-".
/// Exits the program with exit code 1 if path does not exist.
fn open_file(path: &str) -> Box<dyn io::BufRead> {
match path {
"-" => Box::new(BufReader::new(io::stdin())),
_ => match File::open(path) {
Ok(fd) => Box::new(io::BufReader::new(fd)),
Err(error) => {
error!("Could not open {}: {}", path, error);
std::process::exit(1);
}
},
}
}

128
src/read/times.rs

@ -0,0 +1,128 @@
use std::io::BufRead;
use chrono::{DateTime, FixedOffset};
use regex::Regex;
use crate::read::dateparser::LogDateParser;
use crate::read::open_file;
#[derive(Default, Builder)]
pub struct TimeReader {
#[builder(setter(strip_option), default)]
regex: Option<Regex>,
#[builder(setter(strip_option), default)]
ts_format: Option<String>,
}
impl TimeReader {
pub fn read(&self, path: &str) -> Vec<DateTime<FixedOffset>> {
let mut vec: Vec<DateTime<FixedOffset>> = Vec::new();
let mut iterator = open_file(path).lines();
let first_line = match iterator.next() {
Some(Ok(as_string)) => as_string,
Some(Err(error)) => {
error!("{}", error);
return vec;
}
_ => return vec,
};
let parser = match &self.ts_format {
Some(ts_format) => match LogDateParser::new_with_format(&first_line, &ts_format) {
Ok(p) => p,
Err(error) => {
error!("Could not figure out parsing strategy: {}", error);
return vec;
}
},
None => match LogDateParser::new_with_guess(&first_line) {
Ok(p) => p,
Err(error) => {
error!("Could not figure out parsing strategy: {}", error);
return vec;
}
},
};
if let Ok(x) = parser.parse(&first_line) {
vec.push(x);
}
for line in iterator {
match line {
Ok(string) => {
if let Ok(x) = parser.parse(&string) {
if let Some(re) = &self.regex {
if re.is_match(&string) {
vec.push(x);
}
} else {
vec.push(x);
}
}
}
Err(error) => error!("{}", error),
}
}
vec
}
}
#[cfg(test)]
mod tests {
use super::*;
use std::io::Write;
use tempfile::NamedTempFile;
#[test]
fn time_reader_guessing_with_regex() {
let mut builder = TimeReaderBuilder::default();
builder.regex(Regex::new("f.o").unwrap());
let reader = builder.build().unwrap();
match NamedTempFile::new() {
Ok(ref mut file) => {
writeln!(file, "[2021-04-15T06:25:31+00:00] foobar").unwrap();
writeln!(file, "[2021-04-15T06:26:31+00:00] bar").unwrap();
writeln!(file, "[2021-04-15T06:27:31+00:00] foobar").unwrap();
writeln!(file, "[2021-04-15T06:28:31+00:00] foobar").unwrap();
writeln!(file, "none").unwrap();
let ts = reader.read(file.path().to_str().unwrap());
assert_eq!(ts.len(), 3);
assert_eq!(
ts[0],
DateTime::parse_from_rfc3339("2021-04-15T06:25:31+00:00").unwrap()
);
assert_eq!(
ts[2],
DateTime::parse_from_rfc3339("2021-04-15T06:28:31+00:00").unwrap()
);
}
Err(_) => assert!(false, "Could not create temp file"),
}
}
#[test]
fn time_reader_with_format() {
let mut builder = TimeReaderBuilder::default();
builder.ts_format(String::from("%Y_%m_%d %H:%M"));
let reader = builder.build().unwrap();
match NamedTempFile::new() {
Ok(ref mut file) => {
writeln!(file, "_2021_04_15 06:25] foobar").unwrap();
writeln!(file, "_2021_04_15 06:26] bar").unwrap();
writeln!(file, "_2021_04_15 06:27] foobar").unwrap();
writeln!(file, "_2021_04_15 06:28] foobar").unwrap();
writeln!(file, "none").unwrap();
let ts = reader.read(file.path().to_str().unwrap());
assert_eq!(ts.len(), 4);
assert_eq!(
ts[0],
DateTime::parse_from_rfc3339("2021-04-15T06:25:00+00:00").unwrap()
);
assert_eq!(
ts[3],
DateTime::parse_from_rfc3339("2021-04-15T06:28:00+00:00").unwrap()
);
}
Err(_) => assert!(false, "Could not create temp file"),
}
}
}

0
src/stats.rs → src/stats/mod.rs

2
tests/integration_tests.rs

@ -103,6 +103,8 @@ fn test_plot() {
writeln!(file, "4").unwrap();
writeln!(file, "none").unwrap();
cmd.arg("--verbose")
.arg("--color")
.arg("no")
.arg("plot")
.arg(file.path().to_str().unwrap())
.arg("--height")

Loading…
Cancel
Save