handlers are now infalible

This commit is contained in:
Robin Appelman 2021-08-08 00:25:34 +02:00
commit 5be2b0a473
7 changed files with 101 additions and 204 deletions

View file

@ -74,6 +74,5 @@ pub fn medic_death_event_parser(input: &str) -> IResult<&str, MedicDeathEvent> {
charge = Some(quoted(u_int)(value)?.1);
}
}
let (input, time) = opt(param_parse_with("time", quoted(float)))(input)?;
Ok((input, MedicDeathEvent { charge }))
}

View file

@ -9,7 +9,7 @@ pub use raw_event::{RawEvent, RawEventType};
use serde::Serialize;
use std::collections::{BTreeMap, HashMap};
use std::convert::TryInto;
use std::fmt::{Debug, Formatter};
use std::fmt::Debug;
use std::ops::Index;
use thiserror::Error;
@ -18,35 +18,24 @@ mod event;
pub mod module;
mod raw_event;
#[derive(Error)]
pub enum Error<Handler: EventHandler> {
#[derive(Error, Debug)]
pub enum Error {
#[error("Malformed logfile: {0}")]
Malformed(String),
Malformed(nom::error::Error<String>),
#[error("Incomplete logfile")]
Incomplete,
#[error("Malformed subject: {0}")]
Subject(#[from] SubjectError),
#[error("{0}")]
MalformedEvent(#[from] GameEventError),
#[error("{0}")]
HandlerError(Handler::Error),
}
impl<Handler: EventHandler> Debug for Error<Handler> {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
match self {
Error::Malformed(e) => e.fmt(f),
Error::MalformedEvent(e) => e.fmt(f),
Error::HandlerError(e) => e.fmt(f),
}
}
}
impl<Handler: EventHandler> From<SubjectError> for Error<Handler> {
fn from(e: SubjectError) -> Self {
Error::Malformed(e.to_string())
}
}
impl<Handler: EventHandler> From<nom::error::Error<&'_ str>> for Error<Handler> {
impl From<nom::error::Error<&'_ str>> for Error {
fn from(e: nom::error::Error<&str>) -> Self {
Error::Malformed(e.to_string())
Error::Malformed(nom::error::Error {
input: e.input.to_string(),
code: e.code,
})
}
}
@ -73,13 +62,11 @@ impl SubjectMap {
}
}
pub fn parse(log: &str) -> Result<LogOutput, Error<LogHandler>> {
pub fn parse(log: &str) -> Result<LogOutput, Error> {
parse_with_handler::<LogHandler>(log)
}
pub fn parse_with_handler<Handler: EventHandler>(
log: &str,
) -> Result<Handler::Output, Error<Handler>> {
pub fn parse_with_handler<Handler: EventHandler>(log: &str) -> Result<Handler::Output, Error> {
let events = log
.lines()
.filter(|line| line.starts_with("L "))
@ -104,9 +91,7 @@ pub fn parse_with_handler<Handler: EventHandler>(
};
if should_handle {
let event = GameEvent::parse(&raw_event)?;
handler
.handle(match_time, subjects.insert(&raw_event.subject)?, &event)
.map_err(Error::HandlerError)?;
handler.handle(match_time, subjects.insert(&raw_event.subject)?, &event);
}
}
}
@ -136,7 +121,6 @@ pub enum LogError {
impl EventHandler for LogHandler {
type Output = LogOutput;
type Error = LogError;
fn does_handle(&self, ty: RawEventType) -> bool {
self.chat.does_handle(ty)
@ -144,16 +128,10 @@ impl EventHandler for LogHandler {
|| self.medic_stats.does_handle(ty)
}
fn handle(
&mut self,
time: u32,
subject: SubjectId,
event: &GameEvent,
) -> Result<(), Self::Error> {
self.chat.handle(time, subject, event).unwrap();
self.heal_spread.handle(time, subject, event).unwrap();
self.medic_stats.handle(time, subject, event).unwrap();
Ok(())
fn handle(&mut self, time: u32, subject: SubjectId, event: &GameEvent) {
self.chat.handle(time, subject, event);
self.heal_spread.handle(time, subject, event);
self.medic_stats.handle(time, subject, event);
}
fn finish(self, subjects: &SubjectMap) -> Self::Output {

View file

@ -4,7 +4,6 @@ use crate::module::EventHandler;
use crate::raw_event::RawEventType;
use crate::SubjectMap;
use serde::Serialize;
use std::convert::Infallible;
use steamid_ng::SteamID;
struct BareChatMessage {
@ -52,20 +51,14 @@ pub struct ChatHandler(Vec<BareChatMessage>);
impl EventHandler for ChatHandler {
type Output = Vec<ChatMessage>;
type Error = Infallible;
fn does_handle(&self, ty: RawEventType) -> bool {
matches!(ty, RawEventType::SayTeam | RawEventType::Say)
}
fn handle(
&mut self,
time: u32,
subject: SubjectId,
event: &GameEvent,
) -> Result<(), Infallible> {
fn handle(&mut self, time: u32, subject: SubjectId, event: &GameEvent) {
if !matches!(subject, SubjectId::Player(_)) {
return Ok(());
return;
}
match event {
GameEvent::SayTeam(message) => self.0.push(BareChatMessage {
@ -82,7 +75,6 @@ impl EventHandler for ChatHandler {
}),
_ => {}
}
Ok(())
}
fn finish(self, subjects: &SubjectMap) -> Self::Output {

View file

@ -4,29 +4,23 @@ use crate::module::EventHandler;
use crate::raw_event::RawEventType;
use crate::SubjectMap;
use std::collections::HashMap;
use std::convert::{Infallible, TryFrom};
use std::convert::TryFrom;
#[derive(Default)]
pub struct HealSpreadHandler(HashMap<SteamId3, HashMap<SteamId3, u32>>);
impl EventHandler for HealSpreadHandler {
type Output = HashMap<SteamId3, HashMap<SteamId3, u32>>;
type Error = Infallible;
fn does_handle(&self, ty: RawEventType) -> bool {
matches!(ty, RawEventType::Healed)
}
fn handle(
&mut self,
_time: u32,
subject: SubjectId,
event: &GameEvent,
) -> Result<(), Self::Error> {
fn handle(&mut self, _time: u32, subject: SubjectId, event: &GameEvent) {
let healer_steam_id = if let Some(steam_id) = subject.steam_id() {
steam_id
} else {
return Ok(());
return;
};
if let GameEvent::Healed(heal_event) = event {
if let Ok(target_subject) = SubjectId::try_from(&heal_event.target) {
@ -41,7 +35,6 @@ impl EventHandler for HealSpreadHandler {
}
}
}
Ok(())
}
fn finish(self, _subjects: &SubjectMap) -> Self::Output {

View file

@ -4,6 +4,7 @@ use crate::module::EventHandler;
use crate::raw_event::RawEventType;
use crate::SubjectMap;
use chrono::{DateTime, FixedOffset, NaiveDateTime, TimeZone, Utc};
use std::num::ParseIntError;
use std::str::{FromStr, ParseBoolError};
use steamid_ng::SteamID;
use thiserror::Error;
@ -121,52 +122,51 @@ pub enum LobbySettingsError {
#[error("{0}")]
InvalidBool(#[from] ParseBoolError),
#[error("{0}")]
InvalidInt(#[from] ParseIntError),
#[error("{0}")]
InvalidDate(#[from] chrono::ParseError),
}
#[derive(Default)]
pub struct LobbySettingsHandler(Settings);
pub enum LobbySettingsHandler {
NotAvailable,
Active(Settings),
Err(LobbySettingsError),
}
impl EventHandler for LobbySettingsHandler {
type Output = Option<Settings>;
type Error = LobbySettingsError;
fn does_handle(&self, ty: RawEventType) -> bool {
matches!(ty, RawEventType::Say)
impl Default for LobbySettingsHandler {
fn default() -> Self {
LobbySettingsHandler::NotAvailable
}
}
fn handle(
&mut self,
_time: u32,
subject: SubjectId,
event: &GameEvent,
) -> Result<(), Self::Error> {
if !matches!(subject, SubjectId::Console) {
return Ok(());
}
if let GameEvent::Say(msg) = event {
impl LobbySettingsHandler {
fn try_handle(&mut self, msg: &str) -> Result<(), LobbySettingsError> {
match self {
LobbySettingsHandler::NotAvailable => {
if let Some((id, _)) = msg
.strip_prefix("TF2Center Lobby #")
.and_then(|s| str::split_once(s, " |"))
{
self.0.id = id
.parse()
.map_err(|_| LobbySettingsError::InvalidLobbyId(id.into()))?;
let mut settings = Settings::default();
settings.id = id.parse()?;
*self = LobbySettingsHandler::Active(settings);
}
}
LobbySettingsHandler::Active(settings) => {
if let Some((key, value)) = msg.split_once(": ") {
match key {
"Leader" => self.0.leader = value.parse()?,
"Map" => self.0.map = value.into(),
"GameType" => self.0.game_type = value.parse()?,
"Location" => self.0.location = value.parse()?,
"Advanced Lobby" => self.0.advanced = value.parse()?,
"Region lock" => self.0.region_lock = value.parse()?,
"Allow offclassing" => self.0.allow_offclassing = value.parse()?,
"Balancing" => self.0.balancing = value.parse()?,
"Restriction" => self.0.restriction = value.into(),
"Mumble required" => self.0.mumble_required = value.parse()?,
"Leader" => settings.leader = value.parse()?,
"Map" => settings.map = value.into(),
"GameType" => settings.game_type = value.parse()?,
"Location" => settings.location = value.parse()?,
"Advanced Lobby" => settings.advanced = value.parse()?,
"Region lock" => settings.region_lock = value.parse()?,
"Allow offclassing" => settings.allow_offclassing = value.parse()?,
"Balancing" => settings.balancing = value.parse()?,
"Restriction" => settings.restriction = value.into(),
"Mumble required" => settings.mumble_required = value.parse()?,
"Launch date" => {
self.0.date = get_timezone(value)?
settings.date = get_timezone(value)?
.from_local_datetime(&NaiveDateTime::parse_from_str(
value,
"%a %b %d %H:%M:%S %Z %Y",
@ -175,19 +175,40 @@ impl EventHandler for LobbySettingsHandler {
.unwrap()
.into()
}
"Server" => self.0.server = value.into(),
"Server" => settings.server = value.into(),
_ => {}
}
}
}
_ => {}
}
Ok(())
}
}
impl EventHandler for LobbySettingsHandler {
type Output = Result<Option<Settings>, LobbySettingsError>;
fn does_handle(&self, ty: RawEventType) -> bool {
matches!(ty, RawEventType::Say)
}
fn handle(&mut self, _time: u32, subject: SubjectId, event: &GameEvent) {
if !matches!(subject, SubjectId::Console) {
return;
}
if let GameEvent::Say(msg) = event {
if let Err(e) = self.try_handle(msg) {
*self = LobbySettingsHandler::Err(e)
}
}
}
fn finish(self, _subjects: &SubjectMap) -> Self::Output {
if self.0.id > 0 {
Some(self.0)
} else {
None
match self {
LobbySettingsHandler::NotAvailable => Ok(None),
LobbySettingsHandler::Active(settings) => Ok(Some(settings)),
LobbySettingsHandler::Err(e) => Err(e),
}
}
}

View file

@ -66,7 +66,6 @@ pub struct MedicStatsHandler(HashMap<SteamId3, MedicStatsBuilder>);
impl EventHandler for MedicStatsHandler {
type Output = HashMap<SteamId3, MedicStats>;
type Error = InvalidMedicEvent;
fn does_handle(&self, ty: RawEventType) -> bool {
matches!(
@ -80,16 +79,11 @@ impl EventHandler for MedicStatsHandler {
)
}
fn handle(
&mut self,
time: u32,
subject: SubjectId,
event: &GameEvent,
) -> Result<(), Self::Error> {
fn handle(&mut self, time: u32, subject: SubjectId, event: &GameEvent) {
let healer_steam_id = if let Some(steam_id) = subject.steam_id() {
steam_id
} else {
return Ok(());
return;
};
match event {
GameEvent::ChargeEnded(end) => {
@ -138,7 +132,6 @@ impl EventHandler for MedicStatsHandler {
}
_ => {}
}
Ok(())
}
fn finish(self, _subjects: &SubjectMap) -> Self::Output {

View file

@ -8,10 +8,6 @@ pub use lobbysettings::{
LobbySettingsError, LobbySettingsHandler, Location, Settings as LobbySettings,
};
pub use medicstats::{MedicStats, MedicStatsHandler};
use std::convert::Infallible;
use std::error::Error;
use std::fmt::Debug;
use thiserror::Error;
mod chat;
mod healspread;
@ -20,16 +16,10 @@ mod medicstats;
pub trait EventHandler: Default {
type Output;
type Error: Error + Debug;
fn does_handle(&self, ty: RawEventType) -> bool;
fn handle(
&mut self,
time: u32,
subject: SubjectId,
event: &GameEvent,
) -> Result<(), Self::Error>;
fn handle(&mut self, time: u32, subject: SubjectId, event: &GameEvent);
fn finish(self, subjects: &SubjectMap) -> Self::Output;
}
@ -42,86 +32,17 @@ pub struct HandlerStack<Head, Tail> {
impl<Head: EventHandler, Tail: EventHandler> EventHandler for HandlerStack<Head, Tail> {
type Output = (Head::Output, Tail::Output);
type Error = EitherError<Head::Error, Tail::Error>;
fn does_handle(&self, ty: RawEventType) -> bool {
self.head.does_handle(ty) || self.tail.does_handle(ty)
}
fn handle(
&mut self,
time: u32,
subject: SubjectId,
event: &GameEvent,
) -> Result<(), Self::Error> {
self.head
.handle(time, subject, event)
.map_err(|e| EitherError::E1(e))?;
self.tail
.handle(time, subject, event)
.map_err(|e| EitherError::E2(e))?;
Ok(())
fn handle(&mut self, time: u32, subject: SubjectId, event: &GameEvent) {
self.head.handle(time, subject, event);
self.tail.handle(time, subject, event);
}
fn finish(self, subjects: &SubjectMap) -> Self::Output {
(self.head.finish(subjects), self.tail.finish(subjects))
}
}
#[derive(Debug, Error)]
pub enum EitherError<E1: Error, E2: Error> {
#[error("{0}")]
E1(E1),
#[error("{0}")]
E2(E2),
}
/// A handler that doesn't stop the parsing on failure
pub enum OptionalHandler<Handler: EventHandler> {
Active(Handler),
Failed(Handler::Error),
}
impl<Handler: EventHandler> Default for OptionalHandler<Handler> {
fn default() -> Self {
OptionalHandler::Active(Handler::default())
}
}
impl<Handler: EventHandler> EventHandler for OptionalHandler<Handler> {
type Output = Result<Handler::Output, Handler::Error>;
type Error = Infallible;
fn does_handle(&self, ty: RawEventType) -> bool {
match self {
OptionalHandler::Active(handler) => handler.does_handle(ty),
OptionalHandler::Failed(_) => false,
}
}
fn handle(
&mut self,
time: u32,
subject: SubjectId,
event: &GameEvent,
) -> Result<(), Self::Error> {
let res = if let OptionalHandler::Active(handler) = self {
handler.handle(time, subject, event)
} else {
Ok(())
};
if let Err(e) = res {
dbg!(&e);
*self = OptionalHandler::Failed(e)
}
Ok(())
}
fn finish(self, subjects: &SubjectMap) -> Self::Output {
match self {
OptionalHandler::Active(handler) => Ok(handler.finish(subjects)),
OptionalHandler::Failed(e) => Err(e),
}
}
}