aboutsummaryrefslogtreecommitdiff
path: root/src/filters/player.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/filters/player.rs')
-rw-r--r--src/filters/player.rs119
1 files changed, 119 insertions, 0 deletions
diff --git a/src/filters/player.rs b/src/filters/player.rs
new file mode 100644
index 0000000..4daeb22
--- /dev/null
+++ b/src/filters/player.rs
@@ -0,0 +1,119 @@
+//! This module contains filters pertaining to a single player.
+//!
+//! Additionally, it provides methods to lift a player filter to a log filter with [`any`][any] and
+//! [`all`][all].
+use super::{
+ super::{guilds, LogResult, Player, SearchField},
+ log::LogFilter,
+ Filter, Inclusion,
+};
+
+use evtclib::raw::parser::PartialEvtc;
+use evtclib::{Agent, AgentName};
+
+use regex::Regex;
+
+/// Filter type for filters that operate on single players.
+pub trait PlayerFilter = Filter<Agent, Player>;
+
+/// Struct that lifts a [`PlayerFilter`](traitalias.PlayerFilter.html) to a
+/// [`LogFilter`](../log/traitalias.LogFilter.html) by requiring all players to match.
+///
+/// This struct will short-circuit once the result is known.
+#[derive(Debug)]
+struct AllPlayers(Box<dyn PlayerFilter>);
+
+impl Filter<PartialEvtc, LogResult> for AllPlayers {
+ fn filter_early(&self, partial_evtc: &PartialEvtc) -> Inclusion {
+ let mut result = Inclusion::Include;
+ for agent in &partial_evtc.agents {
+ if !agent.is_player() {
+ continue;
+ }
+
+ let agent = Agent::from_raw(agent);
+ if let Ok(agent) = agent {
+ result = result & self.0.filter_early(&agent);
+ } else {
+ result = result & Inclusion::Unknown;
+ }
+ // Short circuit
+ if result == Inclusion::Exclude {
+ return result;
+ }
+ }
+ result
+ }
+
+ fn filter(&self, log: &LogResult) -> bool {
+ log.players.iter().all(|p| self.0.filter(p))
+ }
+}
+
+/// Construct a filter that requires the given `player_filter` to match for all players in a log.
+pub fn all(player_filter: Box<dyn PlayerFilter>) -> Box<dyn LogFilter> {
+ Box::new(AllPlayers(player_filter))
+}
+
+/// Construct a filter that requires the given `player_filter` to match for any player in a log.
+pub fn any(player_filter: Box<dyn PlayerFilter>) -> Box<dyn LogFilter> {
+ !all(!player_filter)
+}
+
+/// Filter that filters players according to their name.
+///
+/// The given SearchField determines in which field something should be searched.
+#[derive(Debug, Clone)]
+struct NameFilter(SearchField, Regex);
+
+impl Filter<Agent, Player> for NameFilter {
+ fn filter_early(&self, agent: &Agent) -> Inclusion {
+ if self.0 == SearchField::Guild {
+ return Inclusion::Unknown;
+ }
+
+ if let AgentName::Player {
+ ref account_name,
+ ref character_name,
+ ..
+ } = agent.name()
+ {
+ let field = match self.0 {
+ SearchField::Account => account_name,
+ SearchField::Character => character_name,
+ _ => unreachable!("We already checked for Guild earlier"),
+ };
+ self.1.is_match(field).into()
+ } else {
+ Inclusion::Unknown
+ }
+ }
+
+ fn filter(&self, player: &Player) -> bool {
+ match self.0 {
+ SearchField::Account => self.1.is_match(&player.account_name),
+ SearchField::Character => self.1.is_match(&player.character_name),
+ SearchField::Guild => player
+ .guild_id
+ .as_ref()
+ .and_then(|id| guilds::lookup(id))
+ .map(|guild| self.1.is_match(guild.tag()) || self.1.is_match(guild.name()))
+ .unwrap_or(false),
+ }
+ }
+}
+
+/// Construct a `PlayerFilter` that searches the given `field` with the given `regex`.
+pub fn name(field: SearchField, regex: Regex) -> Box<dyn PlayerFilter> {
+ Box::new(NameFilter(field, regex))
+}
+
+/// Construct a `PlayerFilter` that searches the character name with the given `regex`.
+pub fn character(regex: Regex) -> Box<dyn PlayerFilter> {
+ name(SearchField::Character, regex)
+}
+
+/// Construct a `PlayerFilter` that searches the account name with the given `regex`.
+pub fn account(regex: Regex) -> Box<dyn PlayerFilter> {
+ name(SearchField::Account, regex)
+}