aboutsummaryrefslogtreecommitdiff
path: root/src/main.rs
diff options
context:
space:
mode:
authorDaniel <kingdread@gmx.de>2020-04-20 14:27:42 +0200
committerDaniel <kingdread@gmx.de>2020-04-20 14:27:42 +0200
commit0e4e148a0890ba206df40cffe5a5f1cc47c8079e (patch)
tree8df2906ee40c84fbab0aa223b13f89a787aa22c6 /src/main.rs
parent569c17607297dbbd57462a9603861d9fe619fd2d (diff)
downloadraidgrep-0e4e148a0890ba206df40cffe5a5f1cc47c8079e.tar.gz
raidgrep-0e4e148a0890ba206df40cffe5a5f1cc47c8079e.tar.bz2
raidgrep-0e4e148a0890ba206df40cffe5a5f1cc47c8079e.zip
hook up new expression parser to command line args
This method is not perfect yet, because 1. The items are not documented as they were before 2. You need to separate the args with --, otherwise Clap tries to parse them as optional flags This should be fixed (especially the documentation part) before merging into master.
Diffstat (limited to 'src/main.rs')
-rw-r--r--src/main.rs124
1 files changed, 25 insertions, 99 deletions
diff --git a/src/main.rs b/src/main.rs
index bea03f6..8edd75a 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -5,25 +5,22 @@ use std::io::{BufReader, Read, Seek};
use std::path::PathBuf;
use std::str::FromStr;
-use anyhow::{anyhow, Result};
-use chrono::{Duration, NaiveDateTime, Weekday};
+use anyhow::Result;
+use chrono::{NaiveDateTime, Weekday};
use log::debug;
use num_traits::cast::FromPrimitive;
-use regex::Regex;
use structopt::StructOpt;
use walkdir::{DirEntry, WalkDir};
use evtclib::{AgentKind, AgentName, EventKind, Log};
+mod fexpr;
mod filters;
use filters::{log::LogFilter, Inclusion};
mod guilds;
mod logger;
mod output;
-mod csl;
-use csl::CommaSeparatedList;
-
macro_rules! unwrap {
($p:pat = $e:expr => { $r:expr} ) => {
if let $p = $e {
@@ -43,18 +40,6 @@ pub struct Opt {
#[structopt(short = "d", long = "dir", default_value = ".", parse(from_os_str))]
path: PathBuf,
- /// The fields which should be searched.
- #[structopt(short = "f", long = "fields", default_value = "account,character")]
- field: CommaSeparatedList<SearchField>,
-
- /// Only display fights with the given outcome.
- #[structopt(short = "o", long = "outcome", default_value = "*")]
- outcome: CommaSeparatedList<FightOutcome>,
-
- /// Invert the regular expression (show fights that do not match)
- #[structopt(short = "v", long = "invert-match")]
- invert: bool,
-
/// Only show the name of matching files.
#[structopt(short = "l", long = "files-with-matches")]
file_name_only: bool,
@@ -63,35 +48,6 @@ pub struct Opt {
#[structopt(long = "no-color")]
no_color: bool,
- /// Only show logs that are younger than the given time.
- #[structopt(
- short = "a",
- long = "younger",
- parse(try_from_str = parse_time_arg)
- )]
- after: Option<NaiveDateTime>,
-
- /// Only show logs that are older than the given time.
- #[structopt(
- short = "b",
- long = "older",
- parse(try_from_str = parse_time_arg)
- )]
- before: Option<NaiveDateTime>,
-
- /// Only show logs from the given weekdays.
- #[structopt(
- short = "w",
- long = "weekdays",
- default_value = "*",
- parse(try_from_str = try_from_str_simple_error)
- )]
- weekdays: CommaSeparatedList<Weekday>,
-
- /// Only show logs from the given encounters.
- #[structopt(short = "e", long = "bosses", default_value = "*")]
- bosses: CommaSeparatedList<evtclib::statistics::gamedata::Boss>,
-
/// Print more debugging information to stderr.
#[structopt(long = "debug")]
debug: bool,
@@ -100,9 +56,8 @@ pub struct Opt {
#[structopt(long = "guilds")]
guilds: bool,
- /// The regular expression to search for.
- #[structopt(name = "EXPR")]
- expression: Regex,
+ /// The filter expression.
+ expression: Vec<String>,
}
/// A flag indicating which fields should be searched.
@@ -180,26 +135,6 @@ impl FromStr for FightOutcome {
}
}
-fn parse_time_arg(input: &str) -> Result<NaiveDateTime> {
- if let Ok(duration) = humantime::parse_duration(input) {
- let now = chrono::Local::now().naive_local();
- let chrono_dur = Duration::from_std(duration).expect("Duration out of range!");
- return Ok(now - chrono_dur);
- }
- if let Ok(time) = humantime::parse_rfc3339_weak(input) {
- let timestamp = time
- .duration_since(std::time::SystemTime::UNIX_EPOCH)
- .unwrap()
- .as_secs();
- return Ok(NaiveDateTime::from_timestamp(timestamp as i64, 0));
- }
- Err(anyhow!("unknown time format"))
-}
-
-fn try_from_str_simple_error<T: FromStr>(input: &str) -> Result<T, String> {
- T::from_str(input).map_err(|_| format!("'{}' is an invalid value", input))
-}
-
enum ZipWrapper<R: Read + Seek> {
Raw(Option<R>),
Zipped(zip::ZipArchive<R>),
@@ -223,6 +158,13 @@ impl<R: Read + Seek> ZipWrapper<R> {
}
fn main() {
+ let result = run();
+ if let Err(err) = result {
+ eprintln!("Error: {}", err);
+ }
+}
+
+fn run() -> Result<()> {
let opt = Opt::from_args();
if opt.no_color {
@@ -239,17 +181,15 @@ fn main() {
guilds::prepare_cache();
}
- let result = grep(&opt);
- match result {
- Ok(_) => {}
- Err(e) => {
- eprintln!("Error: {}", e);
- }
- }
+ let filter = build_filter(&opt)?;
+
+ grep(&opt, &*filter)?;
if opt.guilds {
guilds::save_cache();
}
+
+ Ok(())
}
/// Check if the given entry represents a log file, based on the file name.
@@ -261,32 +201,18 @@ fn is_log_file(entry: &DirEntry) -> bool {
.unwrap_or(false)
}
-fn build_filter(opt: &Opt) -> Box<dyn LogFilter> {
- let player_filter = opt
- .field
- .values()
- .iter()
- .map(|field| filters::player::NameFilter::new(*field, opt.expression.clone()))
- .fold(filters::Const::new(false), |a, f| a | f);
-
- let mut filter = filters::player::any(player_filter);
- if opt.invert {
- filter = !filter;
- }
-
- filter = filter
- & filters::log::BossFilter::new(opt.bosses.values().clone())
- & filters::log::OutcomeFilter::new(opt.outcome.values().clone())
- & filters::log::WeekdayFilter::new(opt.weekdays.values().clone())
- & filters::log::TimeFilter::new(opt.after, opt.before);
-
- filter
+fn build_filter(opt: &Opt) -> Result<Box<dyn LogFilter>> {
+ // Our error needs access to the string, so we make our lives easier by just leaking it into a
+ // 'static lifetime. Otherwise we'd need to build this string in main() and pass it in.
+ // We're fine with the small memory leak, as we're only dealing with a small string in a
+ // short-lived program.
+ let expr_string = Box::leak(Box::new(opt.expression.join(" ")));
+ Ok(fexpr::parse_logfilter(expr_string)?)
}
/// Run the grep search with the given options.
-fn grep(opt: &Opt) -> Result<()> {
+fn grep(opt: &Opt, filter: &dyn LogFilter) -> Result<()> {
let pipeline = &output::build_pipeline(opt);
- let filter: &dyn LogFilter = &*build_filter(opt);
rayon::scope(|s| {
let walker = WalkDir::new(&opt.path);
for entry in walker {