From 0d4677dbd830fec0ccc3ebc33d438783dc9d4912 Mon Sep 17 00:00:00 2001 From: Daniel Schadt Date: Mon, 27 Apr 2020 16:05:17 +0200 Subject: rework Log::players/npcs Since we know that we're only returning Agents which are Players, we can save downstream users some time and also provide access to the &Player. Ideally, we'd want something like PlayerAgent, or Agent, but that not only incurrs code duplication (in the first case), it'd also mean cloning the player data (second case), as we couldn't just return a reference into the pool with all agents. For now, this is still the better option, until other ways have been explored. Maybe cloning here wouldn't be too bad, but we'd also run into the issue that we cannot use record unpacking for records that have different generic parameters, so going from Agent to Agent would mean manually copying over all record fields. We now no longer need the matches! macro, as we can simply use AgentKind::as_{player,character}. --- src/lib.rs | 128 ++++++++++++++++++++++++++++++++++++++++++------------------- 1 file changed, 88 insertions(+), 40 deletions(-) (limited to 'src') diff --git a/src/lib.rs b/src/lib.rs index 637f1b2..51c1394 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -28,27 +28,6 @@ pub use event::{Event, EventKind}; pub mod gamedata; pub use gamedata::Boss; -/// A macro that returns `true` when the given expression matches the pattern. -/// -/// ```rust -/// assert!(matches!(Some(4), Some(_))); -/// assert!(!matches!(Some(2), None)); -/// ``` -macro_rules! matches { - ($expression:expr, $($pattern:pat)|*) => ( - match $expression { - $($pattern)|+ => true, - _ => false, - } - ); - ($expression:expr, $pattern:pat if $condi:expr) => ( - match $expression { - $pattern if $condi => true, - _ => false, - } - ); -} - #[derive(Error, Debug)] pub enum EvtcError { #[error("invalid data has been provided")] @@ -57,18 +36,79 @@ pub enum EvtcError { Utf8Error(#[from] std::string::FromUtf8Error), } +/// Player-specific agent data. +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct Player { + profession: u32, + elite: u32, + character_name: String, + account_name: String, + subgroup: u8, +} + +/// Gadget-specific agent data. +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct Gadget { + id: u16, + name: String, +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct Character { + id: u16, + name: String, +} + /// The type of an agent. #[derive(Debug, Clone, PartialEq, Eq)] pub enum AgentKind { - Player { - profession: u32, - elite: u32, - character_name: String, - account_name: String, - subgroup: u8, - }, - Gadget(u16, String), - Character(u16, String), + Player(Player), + Gadget(Gadget), + Character(Character), +} + +impl AgentKind { + /// Accesses the inner [`Player`][Player] struct, if available. + pub fn as_player(&self) -> Option<&Player> { + if let AgentKind::Player(ref player) = *self { + Some(player) + } else { + None + } + } + + /// Determines whether this `AgentKind` contains a player. + pub fn is_player(&self) -> bool { + self.as_player().is_some() + } + + /// Accesses the inner [`Gadget`][Gadget] struct, if available. + pub fn as_gadget(&self) -> Option<&Gadget> { + if let AgentKind::Gadget(ref gadget) = *self { + Some(gadget) + } else { + None + } + } + + /// Determines whether this `AgentKind` contains a gadget. + pub fn is_gadget(&self) -> bool { + self.as_gadget().is_some() + } + + /// Accesses the inner [`Character`][Character] struct, if available. + pub fn as_character(&self) -> Option<&Character> { + if let AgentKind::Character(ref character) = *self { + Some(character) + } else { + None + } + } + + /// Determines whether this `AgentKind` contains a player. + pub fn is_character(&self) -> bool { + self.as_character().is_some() + } } /// An agent. @@ -107,9 +147,15 @@ impl Agent { .take_while(|c| *c != 0) .collect::>())?; if raw_agent.is_character() { - AgentKind::Character(raw_agent.prof as u16, name) + AgentKind::Character(Character { + id: raw_agent.prof as u16, + name, + }) } else { - AgentKind::Gadget(raw_agent.prof as u16, name) + AgentKind::Gadget(Gadget { + id: raw_agent.prof as u16, + name, + }) } } else if raw_agent.is_player() { let first = raw_agent @@ -126,13 +172,13 @@ impl Agent { .take_while(|c| *c != 0) .collect::>(); let third = raw_agent.name[first.len() + second.len() + 2] - b'0'; - AgentKind::Player { + AgentKind::Player(Player { profession: raw_agent.prof, elite: raw_agent.is_elite, character_name: String::from_utf8(first)?, account_name: String::from_utf8(second)?, subgroup: third, - } + }) } else { return Err(EvtcError::InvalidData); }; @@ -186,17 +232,17 @@ impl Log { } /// Return an iterator over all agents that represent player characters. - pub fn players(&self) -> impl Iterator { + pub fn players(&self) -> impl Iterator { self.agents .iter() - .filter(|a| matches!(a.kind, AgentKind::Player { .. })) + .filter_map(|a| a.kind().as_player().map(|p| (a, p))) } /// Return an iterator over all agents that are NPCs. - pub fn npcs(&self) -> impl Iterator { + pub fn npcs(&self) -> impl Iterator { self.agents .iter() - .filter(|a| matches!(a.kind, AgentKind::Character(..))) + .filter_map(|a| a.kind().as_character().map(|c| (a, c))) } /// Return the boss agent. @@ -205,7 +251,8 @@ impl Log { /// and Xera. pub fn boss(&self) -> &Agent { self.npcs() - .find(|n| matches!(n.kind, AgentKind::Character(x, _) if x == self.boss_id)) + .find(|(_, c)| c.id == self.boss_id) + .map(|t| t.0) .expect("Boss has no agent!") } @@ -220,7 +267,8 @@ impl Log { vec![self.boss_id] }; self.npcs() - .filter(|a| matches!(a.kind, AgentKind::Character(x, _) if boss_ids.contains(&x))) + .filter(|(_, c)| boss_ids.contains(&c.id)) + .map(|t| t.0) .collect() } -- cgit v1.2.3