diff options
| -rw-r--r-- | src/fexpr/grammar.lalrpop | 9 | ||||
| -rw-r--r-- | src/fexpr/mod.rs | 1 | ||||
| -rw-r--r-- | src/filters/values.rs | 42 | 
3 files changed, 51 insertions, 1 deletions
| diff --git a/src/fexpr/grammar.lalrpop b/src/fexpr/grammar.lalrpop index 9584536..9eef21a 100644 --- a/src/fexpr/grammar.lalrpop +++ b/src/fexpr/grammar.lalrpop @@ -7,6 +7,7 @@ use super::{      DateProducer,      DurationProducer, +    CountProducer,  };  use evtclib::Boss;  use std::collections::HashSet; @@ -73,6 +74,7 @@ LogPredicate: Box<dyn filters::log::LogFilter> = {      <Comparison<DateProducer>>,      <Comparison<DurationProducer>>, +    <Comparison<CountProducer>>,      "(" <LogFilter> ")",  } @@ -215,6 +217,11 @@ DurationProducer: Box<dyn DurationProducer> = {      "-duration" => filters::values::duration(),  } +CountProducer: Box<dyn CountProducer> = { +    <integer> => filters::values::constant(<>.parse().unwrap()), +    "count" "(" "player" ":" <PlayerFilter> ")" => filters::values::player_count(<>), +} +  match {      "player" => "player",      "not" => "not", @@ -223,10 +230,12 @@ match {      "any" => "any",      "all" => "all",      "exists" => "exists", +    "count" => "count",      r"\d\d\d\d-\d\d-\d\d \d\d:\d\d:\d\d" => datetime,      r"\d\d\d\d-\d\d-\d\d" => date,      r"((\d+m ?)?\d+s)|(\d+m)" => duration, +    r"\d+" => integer,      r"[[:alpha:]][\w]*" => word,      r#""[^"]*""# => string, diff --git a/src/fexpr/mod.rs b/src/fexpr/mod.rs index 1738e44..f765acf 100644 --- a/src/fexpr/mod.rs +++ b/src/fexpr/mod.rs @@ -13,6 +13,7 @@ use thiserror::Error;  trait DateProducer = filters::values::Producer<Output = chrono::DateTime<chrono::Utc>>;  trait DurationProducer = filters::values::Producer<Output = chrono::Duration>; +trait CountProducer = filters::values::Producer<Output = u8>;  lalrpop_mod!(#[allow(clippy::all)] pub grammar, "/fexpr/grammar.rs"); diff --git a/src/filters/values.rs b/src/filters/values.rs index 141aecd..df5b805 100644 --- a/src/filters/values.rs +++ b/src/filters/values.rs @@ -13,12 +13,14 @@  //! the two resulting values with the given comparison operator.  use std::{      cmp::Ordering, +    convert::TryFrom,      fmt::{self, Debug},  };  use chrono::{DateTime, Duration, Utc}; +use evtclib::Agent; -use super::{log::LogFilter, Filter, Inclusion}; +use super::{log::LogFilter, player::PlayerFilter, Filter, Inclusion};  use crate::{EarlyLogResult, LogResult};  /// A producer for a given value. @@ -194,6 +196,44 @@ pub fn duration() -> Box<dyn Producer<Output = Duration>> {      Box::new(DurationProducer)  } +#[derive(Debug)] +struct PlayerCountProducer(Box<dyn PlayerFilter>); + +impl Producer for PlayerCountProducer { +    type Output = u8; + +    fn produce_early(&self, early_log: &EarlyLogResult) -> Option<Self::Output> { +        let mut count = 0; +        for agent in &early_log.evtc.agents { +            if !agent.is_player() { +                continue; +            } + +            let agent = Agent::try_from(agent); +            if let Ok(agent) = agent { +                let result = self.0.filter_early(&agent); +                match result { +                    Inclusion::Include => count += 1, +                    Inclusion::Exclude => (), +                    Inclusion::Unknown => return None, +                } +            } else { +                return None; +            } +        } +        Some(count) +    } + +    fn produce(&self, log: &LogResult) -> Self::Output { +        log.players.iter().filter(|p| self.0.filter(p)).count() as u8 +    } +} + +/// A producer that counts the players matching the given filter. +pub fn player_count(filter: Box<dyn PlayerFilter>) -> Box<dyn Producer<Output = u8>> { +    Box::new(PlayerCountProducer(filter)) +} +  #[cfg(test)]  mod tests {      use super::*; | 
