mirror of
https://codeberg.org/icewind/tf-log-parser.git
synced 2026-06-03 10:14:10 +02:00
handlers are now infalible
This commit is contained in:
parent
a5040ec355
commit
5be2b0a473
7 changed files with 101 additions and 204 deletions
|
|
@ -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 }))
|
||||
}
|
||||
|
|
|
|||
62
src/lib.rs
62
src/lib.rs
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue