1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
|
//! 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, playerclass::PlayerClass, EarlyLogResult, LogResult, Player, SearchField},
log::LogFilter,
Filter, Inclusion,
};
use std::{collections::HashSet, convert::TryFrom};
use evtclib::{Agent, AgentKind};
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<EarlyLogResult, LogResult> for AllPlayers {
fn filter_early(&self, early_log: &EarlyLogResult) -> Inclusion {
let mut result = Inclusion::Include;
for agent in &early_log.evtc.agents {
if !agent.is_player() {
continue;
}
let agent = Agent::try_from(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 AgentKind::Player(ref player) = agent.kind() {
let field = match self.0 {
SearchField::Account => player.account_name(),
SearchField::Character => player.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)
}
#[derive(Clone, Debug)]
struct ClassFilter(HashSet<PlayerClass>);
impl Filter<Agent, Player> for ClassFilter {
fn filter_early(&self, agent: &Agent) -> Inclusion {
if let AgentKind::Player(ref player) = agent.kind() {
self.0.contains(&player.into()).into()
} else {
Inclusion::Unknown
}
}
fn filter(&self, player: &Player) -> bool {
self.0.contains(&player.profession)
}
}
/// Construct a `PlayerFilter` that matches only the given classes.
pub fn class(classes: HashSet<PlayerClass>) -> Box<dyn PlayerFilter> {
Box::new(ClassFilter(classes))
}
|