diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/filters/log.rs | 107 |
1 files changed, 95 insertions, 12 deletions
diff --git a/src/filters/log.rs b/src/filters/log.rs index 60e6912..1728d4b 100644 --- a/src/filters/log.rs +++ b/src/filters/log.rs @@ -7,12 +7,17 @@ use super::{ Filter, Inclusion, }; -use std::collections::HashSet; +use std::{collections::HashSet, ffi::OsStr}; use evtclib::Boss; -use chrono::{DateTime, Datelike, Utc, Weekday}; +use chrono::{DateTime, Datelike, Local, TimeZone, Utc, Weekday}; use num_traits::FromPrimitive as _; +use once_cell::sync::Lazy; +use regex::Regex; + +/// The regular expression used to extract datetimes from filenames. +static DATE_REGEX: Lazy<Regex> = Lazy::new(|| Regex::new(r"\d{8}-\d{6}").unwrap()); /// Filter trait used for filters that operate on complete logs. pub trait LogFilter = Filter<EarlyLogResult, LogResult>; @@ -90,20 +95,53 @@ pub fn weekday(weekdays: HashSet<Weekday>) -> Box<dyn LogFilter> { struct TimeFilter(Option<DateTime<Utc>>, Option<DateTime<Utc>>); impl Filter<EarlyLogResult, LogResult> for TimeFilter { + fn filter_early(&self, early_log: &EarlyLogResult) -> Inclusion { + early_log + .log_file + .file_name() + .and_then(datetime_from_filename) + .map(|d| time_is_between(d, self.0, self.1)) + .map(Into::into) + .unwrap_or(Inclusion::Unknown) + } + fn filter(&self, log: &LogResult) -> bool { - let after_ok = match self.0 { - Some(time) => time <= log.time, - None => true, - }; - let before_ok = match self.1 { - Some(time) => time >= log.time, - None => true, - }; - - after_ok && before_ok + time_is_between(log.time, self.0, self.1) } } +/// Check if the given time is after `after` but before `before`. +/// +/// If one of the bounds is `None`, the time is always in bounds w.r.t. that bound. +fn time_is_between( + time: DateTime<Utc>, + after: Option<DateTime<Utc>>, + before: Option<DateTime<Utc>>, +) -> bool { + let after_ok = match after { + Some(after) => after <= time, + None => true, + }; + let before_ok = match before { + Some(before) => before >= time, + None => true, + }; + + after_ok && before_ok +} + +/// Try to extract the log time from the filename. +/// +/// This expects the filename to have the datetime in the pattern `YYYYmmdd-HHMMSS` somewhere in +/// it. +fn datetime_from_filename(name: &OsStr) -> Option<DateTime<Utc>> { + let date_match = DATE_REGEX.find(name.to_str()?)?; + let local_time = Local + .datetime_from_str(date_match.as_str(), "%Y%m%d-%H%M%S") + .ok()?; + Some(local_time.with_timezone(&Utc)) +} + /// A `LogFilter` that only accepts logs in the given time frame. /// /// If a bound is not given, -Infinity is assumed for the lower bound, and Infinity for the upper @@ -125,3 +163,48 @@ pub fn after(when: DateTime<Utc>) -> Box<dyn LogFilter> { pub fn before(when: DateTime<Utc>) -> Box<dyn LogFilter> { time(None, Some(when)) } + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_time_is_between() { + assert!(time_is_between( + Utc.ymd(1955, 11, 5).and_hms(6, 15, 0), + None, + None, + )); + assert!(time_is_between( + Utc.ymd(1955, 11, 5).and_hms(6, 15, 0), + Some(Utc.ymd(1955, 11, 5).and_hms(5, 0, 0)), + None, + )); + assert!(time_is_between( + Utc.ymd(1955, 11, 5).and_hms(6, 15, 0), + None, + Some(Utc.ymd(1955, 11, 5).and_hms(7, 0, 0)), + )); + assert!(time_is_between( + Utc.ymd(1955, 11, 5).and_hms(6, 15, 0), + Some(Utc.ymd(1955, 11, 5).and_hms(5, 0, 0)), + Some(Utc.ymd(1955, 11, 5).and_hms(7, 0, 0)), + )); + + assert!(!time_is_between( + Utc.ymd(1955, 11, 5).and_hms(6, 15, 0), + Some(Utc.ymd(1955, 11, 5).and_hms(7, 0, 0)), + None, + )); + assert!(!time_is_between( + Utc.ymd(1955, 11, 5).and_hms(6, 15, 0), + None, + Some(Utc.ymd(1955, 11, 5).and_hms(5, 0, 0)), + )); + assert!(!time_is_between( + Utc.ymd(1955, 11, 5).and_hms(6, 15, 0), + Some(Utc.ymd(1955, 11, 5).and_hms(5, 0, 0)), + Some(Utc.ymd(1955, 11, 5).and_hms(6, 0, 0)), + )); + } +} |