diff options
-rw-r--r-- | src/main.rs | 51 | ||||
-rw-r--r-- | src/playerclass.rs | 141 |
2 files changed, 147 insertions, 45 deletions
diff --git a/src/main.rs b/src/main.rs index 625e869..d9f0817 100644 --- a/src/main.rs +++ b/src/main.rs @@ -17,7 +17,7 @@ use structopt::StructOpt; use walkdir::{DirEntry, WalkDir}; use evtclib::raw::parser::PartialEvtc; -use evtclib::{EliteSpec, EventKind, Log, Profession}; +use evtclib::{EventKind, Log}; mod fexpr; mod filters; @@ -26,6 +26,8 @@ mod guilds; mod logger; mod output; mod paths; +mod playerclass; +use playerclass::PlayerClass; /// Application name, as it should be used in configuration directory paths. const APP_NAME: &str = "raidgrep"; @@ -145,8 +147,8 @@ pub struct Player { account_name: String, /// Character name of the player. character_name: String, - /// Profession (or elite specialization) as english name. - profession: String, + /// Profession or elite specialization. + profession: PlayerClass, /// Subsquad that the player was in. subgroup: u8, /// Guild ID, ready for API consumption. @@ -478,7 +480,7 @@ fn extract_info(entry: &DirEntry, log: &Log) -> LogResult { .map(|p| Player { account_name: p.account_name().to_owned(), character_name: p.character_name().to_owned(), - profession: get_profession_name(p.profession(), p.elite()).into(), + profession: (p.profession(), p.elite()).into(), subgroup: p.subgroup(), guild_id: guild_ids.get(&p.addr()).cloned(), }) @@ -570,44 +572,3 @@ fn get_encounter_name(log: &Log) -> Option<&'static str> { Boss::WhisperOfJormag => "Whisper of Jormag", }) } - -/// Get the (english) name for the given profession/elite specialization. -fn get_profession_name(profession: Profession, elite: Option<EliteSpec>) -> &'static str { - use EliteSpec::*; - use Profession::*; - - if let Some(elite) = elite { - match elite { - Dragonhunter => "Dragonhunter", - Firebrand => "Firebrand", - Berserker => "Berserker", - Spellbreaker => "Spellbreaker", - Herald => "Herald", - Renegade => "Renegade", - Scrapper => "Scrapper", - Holosmith => "Holosmith", - Druid => "Druid", - Soulbeast => "Soulbeast", - Daredevil => "Daredevil", - Deadeye => "Deadeye", - Tempest => "Tempest", - Weaver => "Weaver", - Chronomancer => "Chronomancer", - Mirage => "Mirage", - Reaper => "Reaper", - Scourge => "Scourge", - } - } else { - match profession { - Guardian => "Guardian", - Warrior => "Warrior", - Revenant => "Revenant", - Engineer => "Engineer", - Ranger => "Ranger", - Thief => "Thief", - Elementalist => "Elementalist", - Mesmer => "Mesmer", - Necromancer => "Necromancer", - } - } -} diff --git a/src/playerclass.rs b/src/playerclass.rs new file mode 100644 index 0000000..77b9794 --- /dev/null +++ b/src/playerclass.rs @@ -0,0 +1,141 @@ +use std::{fmt, str::FromStr}; + +use evtclib::{EliteSpec, Profession}; + +use thiserror::Error; + +/// An enum containing either a profession or an elite spec. +/// +/// This enum provides us with a variety of things: +/// +/// Game mechanic wise, a Dragonhunter is also a Guardian, because Dragonhunter is only the elite +/// specialization. However, when outputting that to the user, we usually only write Dragonhunter, +/// and not Guardian, as that is implied. Same when filtering, when we filter for Guardian, we +/// probably don't want any Dragonhunters. +/// +/// So this enum unifies the handling between core specs and elite specs, and provides them with a +/// convenient [`Display`][Display] implementation as well. +#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq)] +pub enum PlayerClass { + Profession(Profession), + EliteSpec(EliteSpec), +} + +#[derive(Debug, Clone, Hash, PartialEq, Eq, Error)] +#[error("could not parse the class: {0}")] +pub struct ParsePlayerClassError(String); + +impl From<Profession> for PlayerClass { + fn from(p: Profession) -> Self { + PlayerClass::Profession(p) + } +} + +impl From<EliteSpec> for PlayerClass { + fn from(e: EliteSpec) -> Self { + PlayerClass::EliteSpec(e) + } +} + +impl FromStr for PlayerClass { + type Err = ParsePlayerClassError; + + fn from_str(s: &str) -> Result<Self, Self::Err> { + let err = ParsePlayerClassError(s.to_owned()); + Profession::from_str(s) + .map(Into::into) + .map_err(|_| err.clone()) + .or_else(|_| EliteSpec::from_str(s).map(Into::into).map_err(|_| err)) + } +} + +impl From<(Profession, Option<EliteSpec>)> for PlayerClass { + fn from((profession, elite_spec): (Profession, Option<EliteSpec>)) -> Self { + if let Some(spec) = elite_spec { + spec.into() + } else { + profession.into() + } + } +} + +impl fmt::Display for PlayerClass { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + use EliteSpec::*; + use Profession::*; + + let name = match *self { + PlayerClass::EliteSpec(elite) => match elite { + Dragonhunter => "Dragonhunter", + Firebrand => "Firebrand", + Berserker => "Berserker", + Spellbreaker => "Spellbreaker", + Herald => "Herald", + Renegade => "Renegade", + Scrapper => "Scrapper", + Holosmith => "Holosmith", + Druid => "Druid", + Soulbeast => "Soulbeast", + Daredevil => "Daredevil", + Deadeye => "Deadeye", + Tempest => "Tempest", + Weaver => "Weaver", + Chronomancer => "Chronomancer", + Mirage => "Mirage", + Reaper => "Reaper", + Scourge => "Scourge", + }, + PlayerClass::Profession(prof) => match prof { + Guardian => "Guardian", + Warrior => "Warrior", + Revenant => "Revenant", + Engineer => "Engineer", + Ranger => "Ranger", + Thief => "Thief", + Elementalist => "Elementalist", + Mesmer => "Mesmer", + Necromancer => "Necromancer", + }, + }; + write!(f, "{}", name) + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_player_class_from() { + let tests: &[(Profession, Option<EliteSpec>, PlayerClass)] = &[ + ( + Profession::Guardian, + None, + PlayerClass::Profession(Profession::Guardian), + ), + ( + Profession::Guardian, + Some(EliteSpec::Dragonhunter), + PlayerClass::EliteSpec(EliteSpec::Dragonhunter), + ), + ]; + + for (prof, elite_spec, expected) in tests { + assert_eq!(PlayerClass::from((*prof, *elite_spec)), *expected); + } + } + + #[test] + fn test_parse_player_class() { + let tests: &[(&'static str, PlayerClass)] = &[ + ("guardian", Profession::Guardian.into()), + ("dragonhunter", EliteSpec::Dragonhunter.into()), + ("warrior", Profession::Warrior.into()), + ("scourge", EliteSpec::Scourge.into()), + ]; + + for (input, expected) in tests { + assert_eq!(input.parse(), Ok(*expected)); + } + } +} |