mirror of
https://codeberg.org/icewind/tf-log-parser.git
synced 2026-06-03 10:14:10 +02:00
handler macro
This commit is contained in:
parent
5be2b0a473
commit
e846fcceae
5 changed files with 91 additions and 56 deletions
|
|
@ -7,11 +7,12 @@ edition = "2018"
|
||||||
steamid-ng = "1"
|
steamid-ng = "1"
|
||||||
nom = "6"
|
nom = "6"
|
||||||
enum-iterator = "0.7"
|
enum-iterator = "0.7"
|
||||||
chrono = "0.4"
|
chrono = { version = "0.4", features = ["serde"] }
|
||||||
thiserror = "1"
|
thiserror = "1"
|
||||||
serde = { version = "1", features = ["derive"] }
|
serde = { version = "1", features = ["derive"] }
|
||||||
serde_json = "1"
|
serde_json = "1"
|
||||||
main_error = "0.1"
|
main_error = "0.1"
|
||||||
|
paste = "1"
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
criterion = "0.3"
|
criterion = "0.3"
|
||||||
|
|
|
||||||
|
|
@ -1,16 +1,22 @@
|
||||||
use main_error::MainError;
|
use main_error::MainError;
|
||||||
use std::env::args;
|
use std::env::args;
|
||||||
use std::fs;
|
use std::fs;
|
||||||
use tf_log_parser::module::{ChatHandler, HandlerStack, LobbySettingsHandler, OptionalHandler};
|
use tf_log_parser::module::{ChatHandler, LobbySettingsHandler};
|
||||||
use tf_log_parser::parse_with_handler;
|
use tf_log_parser::{handler, parse_with_handler};
|
||||||
|
|
||||||
type Handler = HandlerStack<ChatHandler, OptionalHandler<LobbySettingsHandler>>;
|
handler!(Handler {
|
||||||
|
chat: ChatHandler,
|
||||||
|
lobby_settings: LobbySettingsHandler
|
||||||
|
});
|
||||||
|
|
||||||
fn main() -> Result<(), MainError> {
|
fn main() -> Result<(), MainError> {
|
||||||
let path = args().skip(1).next().expect("No path provided");
|
let path = args().skip(1).next().expect("No path provided");
|
||||||
let content = fs::read_to_string(path)?;
|
let content = fs::read_to_string(path)?;
|
||||||
|
|
||||||
let (chat, lobby_settings) = parse_with_handler::<Handler>(&content)?;
|
let HandlerOutput {
|
||||||
|
chat,
|
||||||
|
lobby_settings,
|
||||||
|
} = parse_with_handler::<Handler>(&content)?;
|
||||||
|
|
||||||
if let Ok(Some(settings)) = lobby_settings {
|
if let Ok(Some(settings)) = lobby_settings {
|
||||||
println!("Lobby settings: {:#?}", settings);
|
println!("Lobby settings: {:#?}", settings);
|
||||||
|
|
|
||||||
56
src/lib.rs
56
src/lib.rs
|
|
@ -1,13 +1,12 @@
|
||||||
pub use crate::common::{SteamId3, SubjectData, SubjectError, SubjectId};
|
pub use crate::common::{SteamId3, SubjectData, SubjectError, SubjectId};
|
||||||
use crate::event::{GameEvent, GameEventError};
|
use crate::event::GameEventError;
|
||||||
use crate::module::{
|
pub use crate::module::EventHandler;
|
||||||
ChatHandler, ChatMessage, EventHandler, HealSpreadHandler, MedicStats, MedicStatsHandler,
|
use crate::module::{ChatHandler, HealSpreadHandler, MedicStatsHandler};
|
||||||
};
|
|
||||||
use crate::raw_event::RawSubject;
|
use crate::raw_event::RawSubject;
|
||||||
use chrono::{DateTime, Utc};
|
use chrono::{DateTime, Utc};
|
||||||
|
pub use event::GameEvent;
|
||||||
pub use raw_event::{RawEvent, RawEventType};
|
pub use raw_event::{RawEvent, RawEventType};
|
||||||
use serde::Serialize;
|
use std::collections::BTreeMap;
|
||||||
use std::collections::{BTreeMap, HashMap};
|
|
||||||
use std::convert::TryInto;
|
use std::convert::TryInto;
|
||||||
use std::fmt::Debug;
|
use std::fmt::Debug;
|
||||||
use std::ops::Index;
|
use std::ops::Index;
|
||||||
|
|
@ -15,6 +14,7 @@ use thiserror::Error;
|
||||||
|
|
||||||
mod common;
|
mod common;
|
||||||
mod event;
|
mod event;
|
||||||
|
#[macro_use]
|
||||||
pub mod module;
|
pub mod module;
|
||||||
mod raw_event;
|
mod raw_event;
|
||||||
|
|
||||||
|
|
@ -62,7 +62,7 @@ impl SubjectMap {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn parse(log: &str) -> Result<LogOutput, Error> {
|
pub fn parse(log: &str) -> Result<<LogHandler as EventHandler>::Output, Error> {
|
||||||
parse_with_handler::<LogHandler>(log)
|
parse_with_handler::<LogHandler>(log)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -99,46 +99,8 @@ pub fn parse_with_handler<Handler: EventHandler>(log: &str) -> Result<Handler::O
|
||||||
Ok(handler.finish(&subjects))
|
Ok(handler.finish(&subjects))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Default)]
|
handler!(LogHandler {
|
||||||
pub struct LogHandler {
|
|
||||||
chat: ChatHandler,
|
chat: ChatHandler,
|
||||||
heal_spread: HealSpreadHandler,
|
heal_spread: HealSpreadHandler,
|
||||||
medic_stats: MedicStatsHandler,
|
medic_stats: MedicStatsHandler,
|
||||||
}
|
});
|
||||||
|
|
||||||
#[derive(Default, Serialize)]
|
|
||||||
pub struct LogOutput {
|
|
||||||
chat: Vec<ChatMessage>,
|
|
||||||
heal_spread: HashMap<SteamId3, HashMap<SteamId3, u32>>,
|
|
||||||
medic_stats: HashMap<SteamId3, MedicStats>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Error, Debug)]
|
|
||||||
pub enum LogError {
|
|
||||||
#[error("{0}")]
|
|
||||||
MalformedEvent(#[from] GameEventError),
|
|
||||||
}
|
|
||||||
|
|
||||||
impl EventHandler for LogHandler {
|
|
||||||
type Output = LogOutput;
|
|
||||||
|
|
||||||
fn does_handle(&self, ty: RawEventType) -> bool {
|
|
||||||
self.chat.does_handle(ty)
|
|
||||||
|| self.heal_spread.does_handle(ty)
|
|
||||||
|| self.medic_stats.does_handle(ty)
|
|
||||||
}
|
|
||||||
|
|
||||||
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 {
|
|
||||||
LogOutput {
|
|
||||||
chat: self.chat.finish(subjects),
|
|
||||||
heal_spread: self.heal_spread.finish(subjects),
|
|
||||||
medic_stats: self.medic_stats.finish(subjects),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -4,12 +4,13 @@ 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 serde::{Serialize, Serializer};
|
||||||
use std::num::ParseIntError;
|
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;
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug, Serialize)]
|
||||||
pub enum GameType {
|
pub enum GameType {
|
||||||
Sixes,
|
Sixes,
|
||||||
Highlander,
|
Highlander,
|
||||||
|
|
@ -27,7 +28,7 @@ impl FromStr for GameType {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug, Serialize)]
|
||||||
pub enum Location {
|
pub enum Location {
|
||||||
Europe,
|
Europe,
|
||||||
NorthAmerica,
|
NorthAmerica,
|
||||||
|
|
@ -45,7 +46,7 @@ impl FromStr for Location {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Default)]
|
#[derive(Debug, Default, Serialize)]
|
||||||
pub struct LobbyLeader {
|
pub struct LobbyLeader {
|
||||||
name: String,
|
name: String,
|
||||||
steam_id: SteamID,
|
steam_id: SteamID,
|
||||||
|
|
@ -70,7 +71,7 @@ impl FromStr for LobbyLeader {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug, Serialize)]
|
||||||
pub struct Settings {
|
pub struct Settings {
|
||||||
id: u32,
|
id: u32,
|
||||||
leader: LobbyLeader,
|
leader: LobbyLeader,
|
||||||
|
|
@ -127,6 +128,15 @@ pub enum LobbySettingsError {
|
||||||
InvalidDate(#[from] chrono::ParseError),
|
InvalidDate(#[from] chrono::ParseError),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Serialize for LobbySettingsError {
|
||||||
|
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||||
|
where
|
||||||
|
S: Serializer,
|
||||||
|
{
|
||||||
|
format!("{}", self).serialize(serializer)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub enum LobbySettingsHandler {
|
pub enum LobbySettingsHandler {
|
||||||
NotAvailable,
|
NotAvailable,
|
||||||
Active(Settings),
|
Active(Settings),
|
||||||
|
|
|
||||||
|
|
@ -46,3 +46,59 @@ impl<Head: EventHandler, Tail: EventHandler> EventHandler for HandlerStack<Head,
|
||||||
(self.head.finish(subjects), self.tail.finish(subjects))
|
(self.head.finish(subjects), self.tail.finish(subjects))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[macro_export]
|
||||||
|
macro_rules! handler {
|
||||||
|
($name:ident {$($child:ident: $ty:path),*}) => {
|
||||||
|
handler!($name { $($child: $ty,)* } );
|
||||||
|
};
|
||||||
|
($name:ident {$($child:ident: $ty:path,)*}) => {
|
||||||
|
paste::paste! {
|
||||||
|
#[derive(Default)]
|
||||||
|
pub struct $name {
|
||||||
|
$($child: $ty),*
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct [<$name Output>] {
|
||||||
|
$($child: <$ty as $crate::EventHandler>::Output),*
|
||||||
|
}
|
||||||
|
|
||||||
|
impl serde::Serialize for [<$name Output>] {
|
||||||
|
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||||
|
where
|
||||||
|
S: serde::Serializer,
|
||||||
|
{
|
||||||
|
use serde::ser::SerializeStruct;
|
||||||
|
let mut state = serializer.serialize_struct(concat!("$name", "output"), 3)?;
|
||||||
|
$(state.serialize_field("$child", &self.$child)?;)*
|
||||||
|
state.end()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
impl $crate::EventHandler for $name {
|
||||||
|
type Output = [<$name Output>];
|
||||||
|
|
||||||
|
fn does_handle(&self, ty: $crate::RawEventType) -> bool {
|
||||||
|
#[allow(unused_imports)]
|
||||||
|
use $crate::EventHandler;
|
||||||
|
$(self.$child.does_handle(ty))||*
|
||||||
|
}
|
||||||
|
|
||||||
|
fn handle(&mut self, time: u32, subject: $crate::SubjectId, event: &$crate::GameEvent) {
|
||||||
|
#[allow(unused_imports)]
|
||||||
|
use $crate::EventHandler;
|
||||||
|
$(self.$child.handle(time, subject, event);)*
|
||||||
|
}
|
||||||
|
|
||||||
|
fn finish(self, subjects: &$crate::SubjectMap) -> Self::Output {
|
||||||
|
#[allow(unused_imports)]
|
||||||
|
use $crate::EventHandler;
|
||||||
|
Self::Output {
|
||||||
|
$($child: self.$child.finish(subjects),)*
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue