mirror of
https://codeberg.org/icewind/tf-log-parser.git
synced 2026-06-03 18:24:09 +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);
|
charge = Some(quoted(u_int)(value)?.1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
let (input, time) = opt(param_parse_with("time", quoted(float)))(input)?;
|
|
||||||
Ok((input, MedicDeathEvent { charge }))
|
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 serde::Serialize;
|
||||||
use std::collections::{BTreeMap, HashMap};
|
use std::collections::{BTreeMap, HashMap};
|
||||||
use std::convert::TryInto;
|
use std::convert::TryInto;
|
||||||
use std::fmt::{Debug, Formatter};
|
use std::fmt::Debug;
|
||||||
use std::ops::Index;
|
use std::ops::Index;
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
|
|
||||||
|
|
@ -18,35 +18,24 @@ mod event;
|
||||||
pub mod module;
|
pub mod module;
|
||||||
mod raw_event;
|
mod raw_event;
|
||||||
|
|
||||||
#[derive(Error)]
|
#[derive(Error, Debug)]
|
||||||
pub enum Error<Handler: EventHandler> {
|
pub enum Error {
|
||||||
#[error("Malformed logfile: {0}")]
|
#[error("Malformed logfile: {0}")]
|
||||||
Malformed(String),
|
Malformed(nom::error::Error<String>),
|
||||||
|
#[error("Incomplete logfile")]
|
||||||
|
Incomplete,
|
||||||
|
#[error("Malformed subject: {0}")]
|
||||||
|
Subject(#[from] SubjectError),
|
||||||
#[error("{0}")]
|
#[error("{0}")]
|
||||||
MalformedEvent(#[from] GameEventError),
|
MalformedEvent(#[from] GameEventError),
|
||||||
#[error("{0}")]
|
|
||||||
HandlerError(Handler::Error),
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<Handler: EventHandler> Debug for Error<Handler> {
|
impl From<nom::error::Error<&'_ str>> for Error {
|
||||||
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> {
|
|
||||||
fn from(e: nom::error::Error<&str>) -> Self {
|
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)
|
parse_with_handler::<LogHandler>(log)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn parse_with_handler<Handler: EventHandler>(
|
pub fn parse_with_handler<Handler: EventHandler>(log: &str) -> Result<Handler::Output, Error> {
|
||||||
log: &str,
|
|
||||||
) -> Result<Handler::Output, Error<Handler>> {
|
|
||||||
let events = log
|
let events = log
|
||||||
.lines()
|
.lines()
|
||||||
.filter(|line| line.starts_with("L "))
|
.filter(|line| line.starts_with("L "))
|
||||||
|
|
@ -104,9 +91,7 @@ pub fn parse_with_handler<Handler: EventHandler>(
|
||||||
};
|
};
|
||||||
if should_handle {
|
if should_handle {
|
||||||
let event = GameEvent::parse(&raw_event)?;
|
let event = GameEvent::parse(&raw_event)?;
|
||||||
handler
|
handler.handle(match_time, subjects.insert(&raw_event.subject)?, &event);
|
||||||
.handle(match_time, subjects.insert(&raw_event.subject)?, &event)
|
|
||||||
.map_err(Error::HandlerError)?;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -136,7 +121,6 @@ pub enum LogError {
|
||||||
|
|
||||||
impl EventHandler for LogHandler {
|
impl EventHandler for LogHandler {
|
||||||
type Output = LogOutput;
|
type Output = LogOutput;
|
||||||
type Error = LogError;
|
|
||||||
|
|
||||||
fn does_handle(&self, ty: RawEventType) -> bool {
|
fn does_handle(&self, ty: RawEventType) -> bool {
|
||||||
self.chat.does_handle(ty)
|
self.chat.does_handle(ty)
|
||||||
|
|
@ -144,16 +128,10 @@ impl EventHandler for LogHandler {
|
||||||
|| self.medic_stats.does_handle(ty)
|
|| self.medic_stats.does_handle(ty)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn handle(
|
fn handle(&mut self, time: u32, subject: SubjectId, event: &GameEvent) {
|
||||||
&mut self,
|
self.chat.handle(time, subject, event);
|
||||||
time: u32,
|
self.heal_spread.handle(time, subject, event);
|
||||||
subject: SubjectId,
|
self.medic_stats.handle(time, subject, event);
|
||||||
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 finish(self, subjects: &SubjectMap) -> Self::Output {
|
fn finish(self, subjects: &SubjectMap) -> Self::Output {
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,6 @@ use crate::module::EventHandler;
|
||||||
use crate::raw_event::RawEventType;
|
use crate::raw_event::RawEventType;
|
||||||
use crate::SubjectMap;
|
use crate::SubjectMap;
|
||||||
use serde::Serialize;
|
use serde::Serialize;
|
||||||
use std::convert::Infallible;
|
|
||||||
use steamid_ng::SteamID;
|
use steamid_ng::SteamID;
|
||||||
|
|
||||||
struct BareChatMessage {
|
struct BareChatMessage {
|
||||||
|
|
@ -52,20 +51,14 @@ pub struct ChatHandler(Vec<BareChatMessage>);
|
||||||
|
|
||||||
impl EventHandler for ChatHandler {
|
impl EventHandler for ChatHandler {
|
||||||
type Output = Vec<ChatMessage>;
|
type Output = Vec<ChatMessage>;
|
||||||
type Error = Infallible;
|
|
||||||
|
|
||||||
fn does_handle(&self, ty: RawEventType) -> bool {
|
fn does_handle(&self, ty: RawEventType) -> bool {
|
||||||
matches!(ty, RawEventType::SayTeam | RawEventType::Say)
|
matches!(ty, RawEventType::SayTeam | RawEventType::Say)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn handle(
|
fn handle(&mut self, time: u32, subject: SubjectId, event: &GameEvent) {
|
||||||
&mut self,
|
|
||||||
time: u32,
|
|
||||||
subject: SubjectId,
|
|
||||||
event: &GameEvent,
|
|
||||||
) -> Result<(), Infallible> {
|
|
||||||
if !matches!(subject, SubjectId::Player(_)) {
|
if !matches!(subject, SubjectId::Player(_)) {
|
||||||
return Ok(());
|
return;
|
||||||
}
|
}
|
||||||
match event {
|
match event {
|
||||||
GameEvent::SayTeam(message) => self.0.push(BareChatMessage {
|
GameEvent::SayTeam(message) => self.0.push(BareChatMessage {
|
||||||
|
|
@ -82,7 +75,6 @@ impl EventHandler for ChatHandler {
|
||||||
}),
|
}),
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
Ok(())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn finish(self, subjects: &SubjectMap) -> Self::Output {
|
fn finish(self, subjects: &SubjectMap) -> Self::Output {
|
||||||
|
|
|
||||||
|
|
@ -4,29 +4,23 @@ use crate::module::EventHandler;
|
||||||
use crate::raw_event::RawEventType;
|
use crate::raw_event::RawEventType;
|
||||||
use crate::SubjectMap;
|
use crate::SubjectMap;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::convert::{Infallible, TryFrom};
|
use std::convert::TryFrom;
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
pub struct HealSpreadHandler(HashMap<SteamId3, HashMap<SteamId3, u32>>);
|
pub struct HealSpreadHandler(HashMap<SteamId3, HashMap<SteamId3, u32>>);
|
||||||
|
|
||||||
impl EventHandler for HealSpreadHandler {
|
impl EventHandler for HealSpreadHandler {
|
||||||
type Output = HashMap<SteamId3, HashMap<SteamId3, u32>>;
|
type Output = HashMap<SteamId3, HashMap<SteamId3, u32>>;
|
||||||
type Error = Infallible;
|
|
||||||
|
|
||||||
fn does_handle(&self, ty: RawEventType) -> bool {
|
fn does_handle(&self, ty: RawEventType) -> bool {
|
||||||
matches!(ty, RawEventType::Healed)
|
matches!(ty, RawEventType::Healed)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn handle(
|
fn handle(&mut self, _time: u32, subject: SubjectId, event: &GameEvent) {
|
||||||
&mut self,
|
|
||||||
_time: u32,
|
|
||||||
subject: SubjectId,
|
|
||||||
event: &GameEvent,
|
|
||||||
) -> Result<(), Self::Error> {
|
|
||||||
let healer_steam_id = if let Some(steam_id) = subject.steam_id() {
|
let healer_steam_id = if let Some(steam_id) = subject.steam_id() {
|
||||||
steam_id
|
steam_id
|
||||||
} else {
|
} else {
|
||||||
return Ok(());
|
return;
|
||||||
};
|
};
|
||||||
if let GameEvent::Healed(heal_event) = event {
|
if let GameEvent::Healed(heal_event) = event {
|
||||||
if let Ok(target_subject) = SubjectId::try_from(&heal_event.target) {
|
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 {
|
fn finish(self, _subjects: &SubjectMap) -> Self::Output {
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,7 @@ use crate::module::EventHandler;
|
||||||
use crate::raw_event::RawEventType;
|
use crate::raw_event::RawEventType;
|
||||||
use crate::SubjectMap;
|
use crate::SubjectMap;
|
||||||
use chrono::{DateTime, FixedOffset, NaiveDateTime, TimeZone, Utc};
|
use chrono::{DateTime, FixedOffset, NaiveDateTime, TimeZone, Utc};
|
||||||
|
use std::num::ParseIntError;
|
||||||
use std::str::{FromStr, ParseBoolError};
|
use std::str::{FromStr, ParseBoolError};
|
||||||
use steamid_ng::SteamID;
|
use steamid_ng::SteamID;
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
|
|
@ -121,73 +122,93 @@ pub enum LobbySettingsError {
|
||||||
#[error("{0}")]
|
#[error("{0}")]
|
||||||
InvalidBool(#[from] ParseBoolError),
|
InvalidBool(#[from] ParseBoolError),
|
||||||
#[error("{0}")]
|
#[error("{0}")]
|
||||||
|
InvalidInt(#[from] ParseIntError),
|
||||||
|
#[error("{0}")]
|
||||||
InvalidDate(#[from] chrono::ParseError),
|
InvalidDate(#[from] chrono::ParseError),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Default)]
|
pub enum LobbySettingsHandler {
|
||||||
pub struct LobbySettingsHandler(Settings);
|
NotAvailable,
|
||||||
|
Active(Settings),
|
||||||
|
Err(LobbySettingsError),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for LobbySettingsHandler {
|
||||||
|
fn default() -> Self {
|
||||||
|
LobbySettingsHandler::NotAvailable
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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, " |"))
|
||||||
|
{
|
||||||
|
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" => 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" => {
|
||||||
|
settings.date = get_timezone(value)?
|
||||||
|
.from_local_datetime(&NaiveDateTime::parse_from_str(
|
||||||
|
value,
|
||||||
|
"%a %b %d %H:%M:%S %Z %Y",
|
||||||
|
)?)
|
||||||
|
.earliest()
|
||||||
|
.unwrap()
|
||||||
|
.into()
|
||||||
|
}
|
||||||
|
"Server" => settings.server = value.into(),
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl EventHandler for LobbySettingsHandler {
|
impl EventHandler for LobbySettingsHandler {
|
||||||
type Output = Option<Settings>;
|
type Output = Result<Option<Settings>, LobbySettingsError>;
|
||||||
type Error = LobbySettingsError;
|
|
||||||
|
|
||||||
fn does_handle(&self, ty: RawEventType) -> bool {
|
fn does_handle(&self, ty: RawEventType) -> bool {
|
||||||
matches!(ty, RawEventType::Say)
|
matches!(ty, RawEventType::Say)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn handle(
|
fn handle(&mut self, _time: u32, subject: SubjectId, event: &GameEvent) {
|
||||||
&mut self,
|
|
||||||
_time: u32,
|
|
||||||
subject: SubjectId,
|
|
||||||
event: &GameEvent,
|
|
||||||
) -> Result<(), Self::Error> {
|
|
||||||
if !matches!(subject, SubjectId::Console) {
|
if !matches!(subject, SubjectId::Console) {
|
||||||
return Ok(());
|
return;
|
||||||
}
|
}
|
||||||
if let GameEvent::Say(msg) = event {
|
if let GameEvent::Say(msg) = event {
|
||||||
if let Some((id, _)) = msg
|
if let Err(e) = self.try_handle(msg) {
|
||||||
.strip_prefix("TF2Center Lobby #")
|
*self = LobbySettingsHandler::Err(e)
|
||||||
.and_then(|s| str::split_once(s, " |"))
|
|
||||||
{
|
|
||||||
self.0.id = id
|
|
||||||
.parse()
|
|
||||||
.map_err(|_| LobbySettingsError::InvalidLobbyId(id.into()))?;
|
|
||||||
}
|
|
||||||
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()?,
|
|
||||||
"Launch date" => {
|
|
||||||
self.0.date = get_timezone(value)?
|
|
||||||
.from_local_datetime(&NaiveDateTime::parse_from_str(
|
|
||||||
value,
|
|
||||||
"%a %b %d %H:%M:%S %Z %Y",
|
|
||||||
)?)
|
|
||||||
.earliest()
|
|
||||||
.unwrap()
|
|
||||||
.into()
|
|
||||||
}
|
|
||||||
"Server" => self.0.server = value.into(),
|
|
||||||
_ => {}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Ok(())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn finish(self, _subjects: &SubjectMap) -> Self::Output {
|
fn finish(self, _subjects: &SubjectMap) -> Self::Output {
|
||||||
if self.0.id > 0 {
|
match self {
|
||||||
Some(self.0)
|
LobbySettingsHandler::NotAvailable => Ok(None),
|
||||||
} else {
|
LobbySettingsHandler::Active(settings) => Ok(Some(settings)),
|
||||||
None
|
LobbySettingsHandler::Err(e) => Err(e),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -66,7 +66,6 @@ pub struct MedicStatsHandler(HashMap<SteamId3, MedicStatsBuilder>);
|
||||||
|
|
||||||
impl EventHandler for MedicStatsHandler {
|
impl EventHandler for MedicStatsHandler {
|
||||||
type Output = HashMap<SteamId3, MedicStats>;
|
type Output = HashMap<SteamId3, MedicStats>;
|
||||||
type Error = InvalidMedicEvent;
|
|
||||||
|
|
||||||
fn does_handle(&self, ty: RawEventType) -> bool {
|
fn does_handle(&self, ty: RawEventType) -> bool {
|
||||||
matches!(
|
matches!(
|
||||||
|
|
@ -80,16 +79,11 @@ impl EventHandler for MedicStatsHandler {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn handle(
|
fn handle(&mut self, time: u32, subject: SubjectId, event: &GameEvent) {
|
||||||
&mut self,
|
|
||||||
time: u32,
|
|
||||||
subject: SubjectId,
|
|
||||||
event: &GameEvent,
|
|
||||||
) -> Result<(), Self::Error> {
|
|
||||||
let healer_steam_id = if let Some(steam_id) = subject.steam_id() {
|
let healer_steam_id = if let Some(steam_id) = subject.steam_id() {
|
||||||
steam_id
|
steam_id
|
||||||
} else {
|
} else {
|
||||||
return Ok(());
|
return;
|
||||||
};
|
};
|
||||||
match event {
|
match event {
|
||||||
GameEvent::ChargeEnded(end) => {
|
GameEvent::ChargeEnded(end) => {
|
||||||
|
|
@ -138,7 +132,6 @@ impl EventHandler for MedicStatsHandler {
|
||||||
}
|
}
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
Ok(())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn finish(self, _subjects: &SubjectMap) -> Self::Output {
|
fn finish(self, _subjects: &SubjectMap) -> Self::Output {
|
||||||
|
|
|
||||||
|
|
@ -8,10 +8,6 @@ pub use lobbysettings::{
|
||||||
LobbySettingsError, LobbySettingsHandler, Location, Settings as LobbySettings,
|
LobbySettingsError, LobbySettingsHandler, Location, Settings as LobbySettings,
|
||||||
};
|
};
|
||||||
pub use medicstats::{MedicStats, MedicStatsHandler};
|
pub use medicstats::{MedicStats, MedicStatsHandler};
|
||||||
use std::convert::Infallible;
|
|
||||||
use std::error::Error;
|
|
||||||
use std::fmt::Debug;
|
|
||||||
use thiserror::Error;
|
|
||||||
|
|
||||||
mod chat;
|
mod chat;
|
||||||
mod healspread;
|
mod healspread;
|
||||||
|
|
@ -20,16 +16,10 @@ mod medicstats;
|
||||||
|
|
||||||
pub trait EventHandler: Default {
|
pub trait EventHandler: Default {
|
||||||
type Output;
|
type Output;
|
||||||
type Error: Error + Debug;
|
|
||||||
|
|
||||||
fn does_handle(&self, ty: RawEventType) -> bool;
|
fn does_handle(&self, ty: RawEventType) -> bool;
|
||||||
|
|
||||||
fn handle(
|
fn handle(&mut self, time: u32, subject: SubjectId, event: &GameEvent);
|
||||||
&mut self,
|
|
||||||
time: u32,
|
|
||||||
subject: SubjectId,
|
|
||||||
event: &GameEvent,
|
|
||||||
) -> Result<(), Self::Error>;
|
|
||||||
|
|
||||||
fn finish(self, subjects: &SubjectMap) -> Self::Output;
|
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> {
|
impl<Head: EventHandler, Tail: EventHandler> EventHandler for HandlerStack<Head, Tail> {
|
||||||
type Output = (Head::Output, Tail::Output);
|
type Output = (Head::Output, Tail::Output);
|
||||||
type Error = EitherError<Head::Error, Tail::Error>;
|
|
||||||
|
|
||||||
fn does_handle(&self, ty: RawEventType) -> bool {
|
fn does_handle(&self, ty: RawEventType) -> bool {
|
||||||
self.head.does_handle(ty) || self.tail.does_handle(ty)
|
self.head.does_handle(ty) || self.tail.does_handle(ty)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn handle(
|
fn handle(&mut self, time: u32, subject: SubjectId, event: &GameEvent) {
|
||||||
&mut self,
|
self.head.handle(time, subject, event);
|
||||||
time: u32,
|
self.tail.handle(time, subject, event);
|
||||||
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 finish(self, subjects: &SubjectMap) -> Self::Output {
|
fn finish(self, subjects: &SubjectMap) -> Self::Output {
|
||||||
(self.head.finish(subjects), self.tail.finish(subjects))
|
(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