aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/fexpr/grammar.lalrpop9
-rw-r--r--src/fexpr/mod.rs1
-rw-r--r--src/filters/values.rs42
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::*;