diff options
-rw-r--r-- | raidgrep.1.asciidoc | 8 | ||||
-rw-r--r-- | src/fexpr/grammar.lalrpop | 13 | ||||
-rw-r--r-- | src/fexpr/mod.rs | 4 | ||||
-rw-r--r-- | src/filters/player.rs | 26 | ||||
-rw-r--r-- | src/main.rs | 1 | ||||
-rw-r--r-- | src/playerclass.rs | 6 |
6 files changed, 55 insertions, 3 deletions
diff --git a/raidgrep.1.asciidoc b/raidgrep.1.asciidoc index 78dcf78..1cbdf85 100644 --- a/raidgrep.1.asciidoc +++ b/raidgrep.1.asciidoc @@ -139,6 +139,14 @@ The following predicates have to be wrapped in either a *any(player: ...)* or Shorthand that matches if the character or the account name match the given regular expression. +*-class* 'CLASSES':: + Match the player if they have one of the given classes. Note that a core + class won't match its elite specializations, so _Guardian_ won't match + _Dragonhunter_. + + + + Names can be comma separated, in which case the player must have any of the + listed classes. + === Boss Names Bosses can be referred to by their official name, although if that name diff --git a/src/fexpr/grammar.lalrpop b/src/fexpr/grammar.lalrpop index 654722b..f91da19 100644 --- a/src/fexpr/grammar.lalrpop +++ b/src/fexpr/grammar.lalrpop @@ -3,6 +3,7 @@ use super::{ FErrorKind, FightOutcome, filters, + PlayerClass, SearchField, }; use evtclib::Boss; @@ -77,6 +78,8 @@ PlayerPredicate: Box<dyn filters::player::PlayerFilter> = { filters::player::account(<>.clone()) | filters::player::character(<>), + "-class" <Comma<PlayerClass>> => filters::player::class(<>), + "(" <PlayerFilter> ")", } @@ -135,6 +138,16 @@ Boss: Boss = { }), } +PlayerClass: PlayerClass = { + <l:@L> <w:word> =>? w.parse().map_err(|_| ParseError::User { + error: FError { + location: l, + data: w.into(), + kind: FErrorKind::InvalidClass, + } + }), +} + Date: DateTime<Utc> = { <l:@L> <d:datetime> =>? Local.datetime_from_str(d, "%Y-%m-%d %H:%M:%S") .map_err(|error| ParseError::User { diff --git a/src/fexpr/mod.rs b/src/fexpr/mod.rs index 2bdbfe7..452d66c 100644 --- a/src/fexpr/mod.rs +++ b/src/fexpr/mod.rs @@ -3,7 +3,7 @@ //! This module contains methods to parse a given string into an abstract filter tree, check its //! type and convert it to a [`Filter`][super::filters::Filter]. // Make it available in the grammar mod. -use super::{filters, FightOutcome, SearchField}; +use super::{filters, playerclass::PlayerClass, FightOutcome, SearchField}; use std::{error, fmt}; @@ -44,6 +44,8 @@ pub enum FErrorKind { InvalidTimestamp(#[from] chrono::format::ParseError), #[error("invalid boss name")] InvalidBoss, + #[error("invalid class name")] + InvalidClass, } /// Shortcut to create a new parser and parse the given input. diff --git a/src/filters/player.rs b/src/filters/player.rs index 3af2be2..2b14eb0 100644 --- a/src/filters/player.rs +++ b/src/filters/player.rs @@ -3,12 +3,12 @@ //! Additionally, it provides methods to lift a player filter to a log filter with [`any`][any] and //! [`all`][all]. use super::{ - super::{guilds, EarlyLogResult, LogResult, Player, SearchField}, + super::{guilds, playerclass::PlayerClass, EarlyLogResult, LogResult, Player, SearchField}, log::LogFilter, Filter, Inclusion, }; -use std::convert::TryFrom; +use std::{collections::HashSet, convert::TryFrom}; use evtclib::{Agent, AgentKind}; @@ -113,3 +113,25 @@ pub fn character(regex: Regex) -> Box<dyn PlayerFilter> { 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)) +} diff --git a/src/main.rs b/src/main.rs index d9f0817..bf4c472 100644 --- a/src/main.rs +++ b/src/main.rs @@ -43,6 +43,7 @@ const APP_NAME: &str = "raidgrep"; /// -character REGEX True if the character name matches the regex. /// -account REGEX True if the account name matches the regex. /// -name REGEX True if either character or account name match. +/// -class CLASSES True if the player has one of the listed classes. /// /// -success Only include successful logs. /// -wipe Only include failed logs. diff --git a/src/playerclass.rs b/src/playerclass.rs index 77b9794..247e8b1 100644 --- a/src/playerclass.rs +++ b/src/playerclass.rs @@ -59,6 +59,12 @@ impl From<(Profession, Option<EliteSpec>)> for PlayerClass { } } +impl From<&evtclib::Player> for PlayerClass { + fn from(player: &evtclib::Player) -> Self { + (player.profession(), player.elite()).into() + } +} + impl fmt::Display for PlayerClass { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { use EliteSpec::*; |