diff options
author | Daniel <kingdread@gmx.de> | 2020-04-15 15:01:10 +0200 |
---|---|---|
committer | Daniel <kingdread@gmx.de> | 2020-04-15 15:01:10 +0200 |
commit | 7881ba85ff40f3d22237ef903c7241b56aa9c185 (patch) | |
tree | 975ad07930052191766f21515729c2880bbbc2e2 /src/main.rs | |
parent | 3c429432382dfad6d4ac97349c96e4a4eb292089 (diff) | |
download | raidgrep-7881ba85ff40f3d22237ef903c7241b56aa9c185.tar.gz raidgrep-7881ba85ff40f3d22237ef903c7241b56aa9c185.tar.bz2 raidgrep-7881ba85ff40f3d22237ef903c7241b56aa9c185.zip |
new filter pipeline
This is the groundwork for introducing more complex filter queries like
`find` has. Filters can be arbitrarily combined with and/or/not and
support an "early filter" mode.
So far, the filters have been translated pretty mechanically to match
the current command line arguments, so now new syntax has been
introduced.
The NameFilter is not yet in its final version. The goal is to support
something like PlayerAll/PlayerExists and have a PlayerFilter that works
on single players instead of the complete log, but that might introduce
some code duplication as we then need a PlayerFilterAnd, PlayerFilterOr,
...
Some digging has to be done into whether we can reduce that duplication
without devolving into madness or resorting to macros. Maybe some
type-level generic hackery could be done? Maybe an enum instead of
dynamic traits should be used, at least for the base functions?
Diffstat (limited to 'src/main.rs')
-rw-r--r-- | src/main.rs | 43 |
1 files changed, 31 insertions, 12 deletions
diff --git a/src/main.rs b/src/main.rs index 6b67875..41df732 100644 --- a/src/main.rs +++ b/src/main.rs @@ -6,15 +6,16 @@ use std::str::FromStr; use anyhow::{anyhow, Result}; use chrono::{Duration, NaiveDateTime, Weekday}; +use log::debug; use num_traits::cast::FromPrimitive; use regex::Regex; use structopt::StructOpt; use walkdir::{DirEntry, WalkDir}; -use log::debug; use evtclib::{AgentKind, AgentName, EventKind, Log}; mod filters; +use filters::{Filter, Inclusion}; mod guilds; mod logger; mod output; @@ -32,7 +33,6 @@ macro_rules! unwrap { }; } - /// A program that allows you to search through all your evtc logs for specific /// people. #[derive(StructOpt, Debug)] @@ -106,7 +106,7 @@ pub struct Opt { /// A flag indicating which fields should be searched. #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] -enum SearchField { +pub enum SearchField { /// Only search the account name. Account, /// Only search the character name. @@ -135,6 +135,8 @@ pub struct LogResult { log_file: PathBuf, /// The time of the recording. time: NaiveDateTime, + /// The numeric ID of the boss. + boss_id: u16, /// The name of the boss. boss_name: String, /// A vector of all participating players. @@ -258,16 +260,36 @@ fn is_log_file(entry: &DirEntry) -> bool { .unwrap_or(false) } +fn build_filter(opt: &Opt) -> Box<dyn Filter> { + let mut filter = filters::Const::new(false); + for field in opt.field.values() { + filter = filter | filters::NameFilter::new(*field, opt.expression.clone()); + } + + if opt.invert { + filter = !filter; + } + + filter = filter + & filters::BossFilter::new(opt.bosses.values().clone()) + & filters::OutcomeFilter::new(opt.outcome.values().clone()) + & filters::WeekdayFilter::new(opt.weekdays.values().clone()) + & filters::TimeFilter::new(opt.after, opt.before); + + filter +} + /// Run the grep search with the given options. fn grep(opt: &Opt) -> Result<()> { let pipeline = &output::build_pipeline(opt); + let filter: &dyn Filter = &*build_filter(opt); rayon::scope(|s| { let walker = WalkDir::new(&opt.path); for entry in walker { let entry = entry?; s.spawn(move |_| { if is_log_file(&entry) { - let search = search_log(&entry, opt); + let search = search_log(&entry, filter); match search { Ok(None) => (), Ok(Some(result)) => pipeline.push_item(&result), @@ -287,7 +309,7 @@ fn grep(opt: &Opt) -> Result<()> { /// If the log matches, returns `Ok(Some(..))`. /// If the log doesn't match, returns `Ok(None)`. /// If there was a fatal error, returns `Err(..)`. -fn search_log(entry: &DirEntry, opt: &Opt) -> Result<Option<LogResult>> { +fn search_log(entry: &DirEntry, filter: &dyn Filter) -> Result<Option<LogResult>> { let file_stream = BufReader::new(File::open(entry.path())?); let is_zip = entry .file_name() @@ -302,10 +324,9 @@ fn search_log(entry: &DirEntry, opt: &Opt) -> Result<Option<LogResult>> { let mut stream = wrapper.get_stream(); let partial = evtclib::raw::parser::parse_partial_file(&mut stream)?; - let early_ok = - filters::filter_name(&partial, opt) != opt.invert && filters::filter_boss(&partial, opt); + let early_ok = filter.filter_early(&partial); - if !early_ok { + if early_ok == Inclusion::Exclude { return Ok(None); } @@ -320,10 +341,7 @@ fn search_log(entry: &DirEntry, opt: &Opt) -> Result<Option<LogResult>> { let info = extract_info(entry, &log); - let take_log = filters::filter_outcome(&info, opt) - && filters::filter_weekday(&info, opt) - && filters::filter_time(&info, opt) - && filters::filter_guilds(&info, opt); + let take_log = filter.filter(&info); if take_log { Ok(Some(info)) @@ -372,6 +390,7 @@ fn extract_info(entry: &DirEntry, log: &Log) -> LogResult { LogResult { log_file: entry.path().to_path_buf(), time: NaiveDateTime::from_timestamp(i64::from(get_start_timestamp(log)), 0), + boss_id: log.boss_id(), boss_name, players, outcome: get_fight_outcome(log), |