This commit is contained in:
Robin Appelman 2025-06-02 23:14:57 +02:00
commit 95119b9347
13 changed files with 157 additions and 161 deletions

View file

@ -2,10 +2,11 @@
name = "tf-log-parser" name = "tf-log-parser"
version = "0.1.0" version = "0.1.0"
authors = ["Robin Appelman <robin@icewind.nl>"] authors = ["Robin Appelman <robin@icewind.nl>"]
edition = "2018" edition = "2021"
license = "MIT OR Apache-2.0" license = "MIT OR Apache-2.0"
exclude = ["test_data"] exclude = ["test_data"]
repository = "https://codeberg.org/icewind/tf-log-parser" repository = "https://codeberg.org/icewind/tf-log-parser"
rust-version = "1.80.1"
[dependencies] [dependencies]
steamid-ng = "1.0.0" steamid-ng = "1.0.0"

View file

@ -1,7 +1,8 @@
use chrono::NaiveDateTime; use chrono::NaiveDateTime;
use criterion::{black_box, criterion_group, criterion_main, Criterion}; use criterion::{criterion_group, criterion_main, Criterion};
use flate2::read::GzDecoder; use flate2::read::GzDecoder;
use std::fs::File; use std::fs::File;
use std::hint::black_box;
use std::io::Read; use std::io::Read;
use std::path::Path; use std::path::Path;
use std::time::Duration; use std::time::Duration;
@ -57,7 +58,7 @@ pub fn handle_event(c: &mut Criterion) {
black_box(&events) black_box(&events)
.iter() .iter()
.flat_map(|(event, raw_event)| { .flat_map(|(event, raw_event)| {
black_box(handler.process(&raw_event, &event, &mut start_time, &mut subjects)) black_box(handler.process(raw_event, event, &mut start_time, &mut subjects))
}) })
.count(); .count();
}) })

View file

@ -11,7 +11,7 @@ pub fn parse_benchmark() {
pub fn parse_raw() { pub fn parse_raw() {
black_box( black_box(
LineSplit::new(black_box(&LOG)) LineSplit::new(black_box(LOG))
.filter(|line| !line.is_empty()) .filter(|line| !line.is_empty())
.flat_map(RawEvent::parse) .flat_map(RawEvent::parse)
.count(), .count(),

122
derive/Cargo.lock generated
View file

@ -1,6 +1,6 @@
# This file is automatically @generated by Cargo. # This file is automatically @generated by Cargo.
# It is not intended for manual editing. # It is not intended for manual editing.
version = 3 version = 4
[[package]] [[package]]
name = "ahash" name = "ahash"
@ -23,6 +23,12 @@ dependencies = [
"memchr", "memchr",
] ]
[[package]]
name = "android-tzdata"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0"
[[package]] [[package]]
name = "android_system_properties" name = "android_system_properties"
version = "0.1.5" version = "0.1.5"
@ -64,18 +70,17 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
[[package]] [[package]]
name = "chrono" name = "chrono"
version = "0.4.23" version = "0.4.41"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "16b0a3d9ed01224b22057780a37bb8c5dbfe1be8ba48678e7bf57ec4b385411f" checksum = "c469d952047f47f91b68d1cba3f10d63c11d73e4636f24f08daf0278abf01c4d"
dependencies = [ dependencies = [
"android-tzdata",
"iana-time-zone", "iana-time-zone",
"js-sys", "js-sys",
"num-integer",
"num-traits 0.2.15", "num-traits 0.2.15",
"serde", "serde",
"time",
"wasm-bindgen", "wasm-bindgen",
"winapi", "windows-link",
] ]
[[package]] [[package]]
@ -140,22 +145,22 @@ dependencies = [
[[package]] [[package]]
name = "enum-iterator" name = "enum-iterator"
version = "1.4.0" version = "2.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "706d9e7cf1c7664859d79cd524e4e53ea2b67ea03c98cc2870c5e539695d597e" checksum = "c280b9e6b3ae19e152d8e31cf47f18389781e119d4013a2a2bb0180e5facc635"
dependencies = [ dependencies = [
"enum-iterator-derive", "enum-iterator-derive",
] ]
[[package]] [[package]]
name = "enum-iterator-derive" name = "enum-iterator-derive"
version = "1.2.0" version = "1.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "355f93763ef7b0ae1c43c4d8eccc9d5848d84ad1a1d8ce61c421d1ac85a19d05" checksum = "a1ab991c1362ac86c61ab6f556cff143daa22e5a15e4e189df818b2fd19fe65b"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 1.0.109", "syn 2.0.101",
] ]
[[package]] [[package]]
@ -181,7 +186,7 @@ checksum = "c05aeb6a22b8f62540c194aac980f2115af067bfe15a0734d7277a768d396b31"
dependencies = [ dependencies = [
"cfg-if", "cfg-if",
"libc", "libc",
"wasi 0.11.0+wasi-snapshot-preview1", "wasi",
] ]
[[package]] [[package]]
@ -312,25 +317,24 @@ dependencies = [
[[package]] [[package]]
name = "miette" name = "miette"
version = "5.5.0" version = "7.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4afd9b301defa984bbdbe112b4763e093ed191750a0d914a78c1106b2d0fe703" checksum = "5f98efec8807c63c752b5bd61f862c165c115b0a35685bdcfd9238c7aeb592b7"
dependencies = [ dependencies = [
"cfg-if",
"miette-derive", "miette-derive",
"once_cell",
"thiserror",
"unicode-width", "unicode-width",
] ]
[[package]] [[package]]
name = "miette-derive" name = "miette-derive"
version = "5.5.0" version = "7.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "97c2401ab7ac5282ca5c8b518a87635b1a93762b0b90b9990c509888eeccba29" checksum = "db5b29714e950dbb20d5e6f74f9dcec4edbcc1067bb7f8ed198c097b8c1a818b"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 1.0.109", "syn 2.0.101",
] ]
[[package]] [[package]]
@ -426,9 +430,9 @@ checksum = "b7e5500299e16ebb147ae15a00a942af264cf3688f47923b8fc2cd5858f23ad3"
[[package]] [[package]]
name = "paste" name = "paste"
version = "1.0.11" version = "1.0.15"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d01a5bd0424d00070b0098dd17ebca6f961a959dead1dbcbbbc1d1cd8d3deeba" checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a"
[[package]] [[package]]
name = "proc-macro-error" name = "proc-macro-error"
@ -456,18 +460,18 @@ dependencies = [
[[package]] [[package]]
name = "proc-macro2" name = "proc-macro2"
version = "1.0.55" version = "1.0.95"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1d0dd4be24fcdcfeaa12a432d588dc59bbad6cad3510c67e74a2b6b2fc950564" checksum = "02b3e5e68a3a1a02aad3ec490a98007cbc13c37cbe84a3cd7b8e406d76e7f778"
dependencies = [ dependencies = [
"unicode-ident", "unicode-ident",
] ]
[[package]] [[package]]
name = "quote" name = "quote"
version = "1.0.26" version = "1.0.40"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4424af4bf778aae2051a77b60283332f386554255d722233d09fbfc7e30da2fc" checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
] ]
@ -512,31 +516,32 @@ checksum = "5d5e082f6ea090deaf0e6dd04b68360fd5cddb152af6ce8927c9d25db299f98c"
[[package]] [[package]]
name = "serde" name = "serde"
version = "1.0.152" version = "1.0.219"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bb7d1f0d3021d347a83e556fc4683dea2ea09d87bccdf88ff5c12545d89d5efb" checksum = "5f0e2c6ed6606019b4e29e69dbaba95b11854410e5347d525002456dbbb786b6"
dependencies = [ dependencies = [
"serde_derive", "serde_derive",
] ]
[[package]] [[package]]
name = "serde_derive" name = "serde_derive"
version = "1.0.152" version = "1.0.219"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "af487d118eecd09402d70a5d72551860e788df87b464af30e5ea6a38c75c541e" checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 1.0.109", "syn 2.0.101",
] ]
[[package]] [[package]]
name = "serde_json" name = "serde_json"
version = "1.0.93" version = "1.0.140"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cad406b69c91885b5107daf2c29572f6c8cdb3c66826821e286c533490c0bc76" checksum = "20068b6e96dc6c9bd23e01df8827e6c7e1f2fddd43c21810382803c136b99373"
dependencies = [ dependencies = [
"itoa", "itoa",
"memchr",
"ryu", "ryu",
"serde", "serde",
] ]
@ -553,7 +558,7 @@ dependencies = [
"regex", "regex",
"serde", "serde",
"serde_derive", "serde_derive",
"thiserror", "thiserror 1.0.38",
] ]
[[package]] [[package]]
@ -565,7 +570,7 @@ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"structmeta-derive", "structmeta-derive",
"syn 2.0.12", "syn 2.0.101",
] ]
[[package]] [[package]]
@ -576,7 +581,7 @@ checksum = "a60bcaff7397072dca0017d1db428e30d5002e00b6847703e2e42005c95fbe00"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.12", "syn 2.0.101",
] ]
[[package]] [[package]]
@ -592,9 +597,9 @@ dependencies = [
[[package]] [[package]]
name = "syn" name = "syn"
version = "2.0.12" version = "2.0.101"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "79d9531f94112cfc3e4c8f5f02cb2b58f72c97b7efd85f70203cc6d8efda5927" checksum = "8ce2b7fc941b3a24138a0a7cf8e858bfc6a992e7978a068a5c760deb0ed43caf"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
@ -627,7 +632,7 @@ dependencies = [
"serde_json", "serde_json",
"steamid-ng", "steamid-ng",
"tf-log-parser-derive", "tf-log-parser-derive",
"thiserror", "thiserror 2.0.12",
"walkdir", "walkdir",
] ]
@ -639,7 +644,7 @@ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"structmeta", "structmeta",
"syn 2.0.12", "syn 2.0.101",
"tf-log-parser", "tf-log-parser",
] ]
@ -649,7 +654,16 @@ version = "1.0.38"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6a9cd18aa97d5c45c6603caea1da6628790b37f7a34b6ca89522331c5180fed0" checksum = "6a9cd18aa97d5c45c6603caea1da6628790b37f7a34b6ca89522331c5180fed0"
dependencies = [ dependencies = [
"thiserror-impl", "thiserror-impl 1.0.38",
]
[[package]]
name = "thiserror"
version = "2.0.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "567b8a2dae586314f7be2a752ec7474332959c6460e02bde30d702a66d488708"
dependencies = [
"thiserror-impl 2.0.12",
] ]
[[package]] [[package]]
@ -664,14 +678,14 @@ dependencies = [
] ]
[[package]] [[package]]
name = "time" name = "thiserror-impl"
version = "0.1.45" version = "2.0.12"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1b797afad3f312d1c66a56d11d0316f916356d11bd158fbc6ca6389ff6bf805a" checksum = "7f7cf42b4507d8ea322120659672cf1b9dbb93f8f2d4ecfd6e51350ff5b17a1d"
dependencies = [ dependencies = [
"libc", "proc-macro2",
"wasi 0.10.0+wasi-snapshot-preview1", "quote",
"winapi", "syn 2.0.101",
] ]
[[package]] [[package]]
@ -682,9 +696,9 @@ checksum = "775c11906edafc97bc378816b94585fbd9a054eabaf86fdd0ced94af449efab7"
[[package]] [[package]]
name = "unicode-width" name = "unicode-width"
version = "0.1.10" version = "0.1.14"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c0edd1e5b14653f783770bce4a4dabb4a5108a5370a5f5d8cfe8710c361f6c8b" checksum = "7dd6e30e90baa6f72411720665d41d89b9a3d039dc45b8faea1ddd07f617f6af"
[[package]] [[package]]
name = "version_check" name = "version_check"
@ -702,12 +716,6 @@ dependencies = [
"winapi-util", "winapi-util",
] ]
[[package]]
name = "wasi"
version = "0.10.0+wasi-snapshot-preview1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1a143597ca7c7793eff794def352d41792a93c481eb1042423ff7ff72ba2c31f"
[[package]] [[package]]
name = "wasi" name = "wasi"
version = "0.11.0+wasi-snapshot-preview1" version = "0.11.0+wasi-snapshot-preview1"
@ -798,3 +806,9 @@ name = "winapi-x86_64-pc-windows-gnu"
version = "0.4.0" version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
[[package]]
name = "windows-link"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "76840935b766e1b0a05c0066835fb9ec80071d4c09a16f6bd5f7e655e3c14c38"

View file

@ -110,15 +110,15 @@ fn micros_as_sec(micros: usize) -> f32 {
micros as f32 / 1_000_000.0 micros as f32 / 1_000_000.0
} }
struct PanicCanary<F: Fn() -> ()>(F); struct PanicCanary<F: Fn()>(F);
impl<F: Fn() -> ()> Drop for PanicCanary<F> { impl<F: Fn()> Drop for PanicCanary<F> {
fn drop(&mut self) { fn drop(&mut self) {
(self.0)() (self.0)()
} }
} }
fn on_panic<F: Fn() -> (), P: Fn() -> ()>(f: F, panic: P) { fn on_panic<F: Fn(), P: Fn()>(f: F, panic: P) {
let canary = PanicCanary(panic); let canary = PanicCanary(panic);
f(); f();
forget(canary) forget(canary)

View file

@ -14,21 +14,16 @@ use std::ops::{Index, IndexMut};
use std::str::FromStr; use std::str::FromStr;
use steamid_ng::{AccountType, Instance, SteamID, Universe}; use steamid_ng::{AccountType, Instance, SteamID, Universe};
#[derive(Serialize, Copy, Clone, Eq, PartialEq, Debug, Ord, PartialOrd, Hash)] #[derive(Serialize, Copy, Clone, Eq, PartialEq, Debug, Ord, PartialOrd, Hash, Default)]
pub enum Team { pub enum Team {
Red, Red,
Blue, Blue,
#[default]
Spectator, Spectator,
} }
impl EventFieldFromStr for Team {} impl EventFieldFromStr for Team {}
impl Default for Team {
fn default() -> Self {
Team::Spectator
}
}
impl Team { impl Team {
pub fn as_str(self) -> &'static str { pub fn as_str(self) -> &'static str {
match self { match self {
@ -58,7 +53,9 @@ impl FromStr for Team {
} }
} }
#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Sequence, Serialize)] #[derive(
Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Sequence, Serialize, Default,
)]
#[serde(rename_all = "lowercase")] #[serde(rename_all = "lowercase")]
pub enum Class { pub enum Class {
Scout, Scout,
@ -70,6 +67,7 @@ pub enum Class {
Medic, Medic,
Sniper, Sniper,
Spy, Spy,
#[default]
Unknown, Unknown,
} }
@ -92,12 +90,6 @@ impl Class {
} }
} }
impl Default for Class {
fn default() -> Self {
Class::Unknown
}
}
impl FromStr for Class { impl FromStr for Class {
type Err = (); type Err = ();
@ -242,7 +234,7 @@ impl TryFrom<&RawSubject<'_>> for SubjectId {
} }
} }
let Ok((_, user_id, steam_id, _)) = split_player_subject(raw) else { let Ok((_, user_id, steam_id, _)) = split_player_subject(raw) else {
return Ok(SubjectId::MalformedPlayer(hash_player_str(*raw))); return Ok(SubjectId::MalformedPlayer(hash_player_str(raw)));
}; };
if let Ok(steam_id) = SteamID::from_steam2(steam_id) { if let Ok(steam_id) = SteamID::from_steam2(steam_id) {
SubjectId::Player(steam_id.account_id()) SubjectId::Player(steam_id.account_id())
@ -250,7 +242,7 @@ impl TryFrom<&RawSubject<'_>> for SubjectId {
user_id user_id
.parse() .parse()
.map(SubjectId::Bot) .map(SubjectId::Bot)
.unwrap_or_else(|_| SubjectId::MalformedPlayer(hash_player_str(*raw))) .unwrap_or_else(|_| SubjectId::MalformedPlayer(hash_player_str(raw)))
} }
} }
RawSubject::Team(team) => SubjectId::Team(*team), RawSubject::Team(team) => SubjectId::Team(*team),
@ -290,7 +282,7 @@ impl SubjectData {
SubjectData::Bot { user_id, .. } => SubjectId::Bot(*user_id), SubjectData::Bot { user_id, .. } => SubjectId::Bot(*user_id),
SubjectData::Console => SubjectId::Console, SubjectData::Console => SubjectId::Console,
SubjectData::World => SubjectId::World, SubjectData::World => SubjectId::World,
SubjectData::MalformedPlayer(raw) => SubjectId::MalformedPlayer(hash_player_str(&raw)), SubjectData::MalformedPlayer(raw) => SubjectId::MalformedPlayer(hash_player_str(raw)),
} }
} }
} }
@ -347,7 +339,7 @@ pub struct SteamId3(pub SteamID);
impl PartialOrd<Self> for SteamId3 { impl PartialOrd<Self> for SteamId3 {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> { fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
(u64::from(self.0)).partial_cmp(&u64::from(other.0)) Some(self.cmp(other))
} }
} }

View file

@ -2,7 +2,6 @@ mod game;
mod medic; mod medic;
mod player; mod player;
use crate::event::game::{RoundLengthEvent, RoundWinEvent};
use crate::parsing::{skip, skip_matches, split_once, split_subject_end}; use crate::parsing::{skip, skip_matches, split_once, split_subject_end};
use crate::raw_event::{against_subject_parser, RawSubject}; use crate::raw_event::{against_subject_parser, RawSubject};
use crate::{Error, Events, IResult, RawEvent, RawEventType, Result, SubjectId}; use crate::{Error, Events, IResult, RawEvent, RawEventType, Result, SubjectId};
@ -29,7 +28,7 @@ trait GameEventErrTrait<T> {
fn with_raw(self, raw: &RawEvent) -> Result<T, GameEventError>; fn with_raw(self, raw: &RawEvent) -> Result<T, GameEventError>;
} }
impl<'a, T> GameEventErrTrait<T> for Result<T> { impl<T> GameEventErrTrait<T> for Result<T> {
fn with_raw(self, raw: &RawEvent) -> Result<T, GameEventError> { fn with_raw(self, raw: &RawEvent) -> Result<T, GameEventError> {
self.map_err(|err| GameEventError::Error { self.map_err(|err| GameEventError::Error {
err: Box::new(err), err: Box::new(err),
@ -275,7 +274,7 @@ impl<'a, T: EventField<'a>> EventField<'a> for (T, T, T) {
impl<'a> EventField<'a> for Option<NonZeroU32> { impl<'a> EventField<'a> for Option<NonZeroU32> {
fn parse_field(input: &'a str) -> Result<Self> { fn parse_field(input: &'a str) -> Result<Self> {
u32::parse_field(input).map(|int| NonZeroU32::new(int)) u32::parse_field(input).map(NonZeroU32::new)
} }
} }

View file

@ -4,7 +4,7 @@ use crate::module::{
ChatMessages, ClassStatsHandler, HealSpread, MedicStatsBuilder, PlayerHandler, ChatMessages, ClassStatsHandler, HealSpread, MedicStatsBuilder, PlayerHandler,
}; };
pub use crate::subjectmap::SubjectMap; pub use crate::subjectmap::SubjectMap;
use chrono::{Duration, NaiveDate, NaiveDateTime}; use chrono::{DateTime, Duration, NaiveDate, NaiveDateTime};
pub(crate) use error::ResultExt; pub(crate) use error::ResultExt;
pub use error::{Error, IResult, Result}; pub use error::{Error, IResult, Result};
pub use event::{Event, EventMeta, GameEvent}; pub use event::{Event, EventMeta, GameEvent};
@ -23,31 +23,24 @@ pub(crate) mod parsing;
pub mod raw_event; pub mod raw_event;
mod subjectmap; mod subjectmap;
pub type PerSubjectMap = BTreeMap<SteamId3, <LogHandler as EventHandler>::PerSubjectOutput>;
pub fn parse( pub fn parse(
log: &str, log: &str,
) -> Result< ) -> Result<(<LogHandler as EventHandler>::GlobalOutput, PerSubjectMap), Error> {
(
<LogHandler as EventHandler>::GlobalOutput,
BTreeMap<SteamId3, <LogHandler as EventHandler>::PerSubjectOutput>,
),
Error,
> {
parse_with_handler::<LogHandler>(log) parse_with_handler::<LogHandler>(log)
} }
pub fn raw_events<'a>(log: &'a str) -> impl Iterator<Item = Result<RawEvent<'a>>> + 'a { pub fn raw_events(log: &str) -> impl Iterator<Item = Result<RawEvent>> {
LineSplit::new(log).map(RawEvent::parse) LineSplit::new(log).map(RawEvent::parse)
} }
pub type PerSubjectMapForHandler<Handler> =
BTreeMap<SteamId3, <Handler as EventHandler>::PerSubjectOutput>;
pub fn parse_with_handler<Handler: EventHandler>( pub fn parse_with_handler<Handler: EventHandler>(
log: &str, log: &str,
) -> Result< ) -> Result<(Handler::GlobalOutput, PerSubjectMapForHandler<Handler>), Error> {
(
Handler::GlobalOutput,
BTreeMap<SteamId3, Handler::PerSubjectOutput>,
),
Error,
> {
let mut events = raw_events(log); let mut events = raw_events(log);
let mut handler = Handler::default(); let mut handler = Handler::default();
@ -63,15 +56,14 @@ pub fn parse_with_handler<Handler: EventHandler>(
Err(e) => return Err(e), Err(e) => return Err(e),
}; };
let should_handle = Handler::does_handle(raw_event.ty); let should_handle = Handler::does_handle(raw_event.ty);
if should_handle || start_time.is_none() { if should_handle && start_time.is_none() {
if should_handle {
let event = match GameEvent::parse(&raw_event) { let event = match GameEvent::parse(&raw_event) {
Ok(event) => event, Ok(event) => event,
Err(e) => { Err(e) => {
let old_date: NaiveDateTime = raw_event let old_date: NaiveDateTime = raw_event
.date .date
.try_into() .try_into()
.unwrap_or_else(|_| NaiveDateTime::from_timestamp(0, 0)); .unwrap_or_else(|_| DateTime::from_timestamp(0, 0).unwrap().naive_utc());
// truncated newline during log combining, ignore error // truncated newline during log combining, ignore error
if contains_line_start(raw_event.params, &old_date.date()) { if contains_line_start(raw_event.params, &old_date.date()) {
@ -84,10 +76,9 @@ pub fn parse_with_handler<Handler: EventHandler>(
}; };
if let Ok(next) = next { if let Ok(next) = next {
let new_date: NaiveDateTime = next let new_date: NaiveDateTime = next.date.try_into().unwrap_or_else(|_| {
.date DateTime::from_timestamp(0, 0).unwrap().naive_utc()
.try_into() });
.unwrap_or_else(|_| NaiveDateTime::from_timestamp(0, 0));
// truncated lines during log combining, ignore error // truncated lines during log combining, ignore error
if new_date.signed_duration_since(old_date) > Duration::seconds(60) { if new_date.signed_duration_since(old_date) > Duration::seconds(60) {
@ -101,7 +92,6 @@ pub fn parse_with_handler<Handler: EventHandler>(
handler.process(&raw_event, &event, &mut start_time, &mut subjects)?; handler.process(&raw_event, &event, &mut start_time, &mut subjects)?;
} }
} }
}
let just_subjects = subjects.to_just_subjects(); let just_subjects = subjects.to_just_subjects();
let per_player = subjects let per_player = subjects

View file

@ -19,11 +19,7 @@ impl PlayerSpecificData for HealSpread {
fn handle_event(&mut self, _meta: &EventMeta, _subject: SubjectId, event: &GameEvent) { fn handle_event(&mut self, _meta: &EventMeta, _subject: SubjectId, event: &GameEvent) {
if let GameEvent::Healed(heal_event) = event { if let GameEvent::Healed(heal_event) = event {
if let Some(Ok(target_subject)) = heal_event if let Some(Ok(target_subject)) = heal_event.target.as_ref().map(SubjectId::try_from) {
.target
.as_ref()
.map(|target| SubjectId::try_from(target))
{
if let Some(target_steam_id) = target_subject.steam_id() { if let Some(target_steam_id) = target_subject.steam_id() {
let healed = self.0.entry(SteamId3(target_steam_id)).or_default(); let healed = self.0.entry(SteamId3(target_steam_id)).or_default();
*healed += heal_event.amount *healed += heal_event.amount

View file

@ -3,7 +3,7 @@ use crate::event::GameEvent;
use crate::module::GlobalData; use crate::module::GlobalData;
use crate::raw_event::RawEventType; use crate::raw_event::RawEventType;
use crate::{EventMeta, SubjectMap}; use crate::{EventMeta, SubjectMap};
use chrono::{DateTime, FixedOffset, NaiveDateTime, TimeZone, Utc}; use chrono::{DateTime, FixedOffset, NaiveDate, NaiveDateTime, TimeZone, Utc};
use serde::{Serialize, Serializer}; use serde::{Serialize, Serializer};
use std::num::ParseIntError; use std::num::ParseIntError;
use std::str::{FromStr, ParseBoolError}; use std::str::{FromStr, ParseBoolError};
@ -108,7 +108,11 @@ impl Settings {
balancing: false, balancing: false,
restriction: "".to_string(), restriction: "".to_string(),
mumble_required: false, mumble_required: false,
date: Utc.ymd(1, 1, 1).and_hms(0, 0, 0), date: NaiveDate::from_ymd_opt(1, 1, 1)
.unwrap()
.and_hms_opt(0, 0, 0)
.unwrap()
.and_utc(),
server: "".to_string(), server: "".to_string(),
} }
} }
@ -143,18 +147,14 @@ impl Serialize for LobbySettingsError {
} }
} }
#[derive(Default)]
pub enum LobbySettingsHandler { pub enum LobbySettingsHandler {
#[default]
NotAvailable, NotAvailable,
Active(Settings), Active(Settings),
Err(LobbySettingsError), Err(LobbySettingsError),
} }
impl Default for LobbySettingsHandler {
fn default() -> Self {
LobbySettingsHandler::NotAvailable
}
}
impl LobbySettingsHandler { impl LobbySettingsHandler {
fn try_handle(&mut self, msg: &str) -> Result<(), LobbySettingsError> { fn try_handle(&mut self, msg: &str) -> Result<(), LobbySettingsError> {
match self { match self {
@ -230,9 +230,9 @@ impl GlobalData for LobbySettingsHandler {
fn get_timezone(date: &str) -> Result<FixedOffset, LobbySettingsError> { fn get_timezone(date: &str) -> Result<FixedOffset, LobbySettingsError> {
if date.contains("CEST") { if date.contains("CEST") {
Ok(FixedOffset::east(2 * 60 * 60)) Ok(FixedOffset::east_opt(2 * 60 * 60).unwrap())
} else if date.contains("CET") { } else if date.contains("CET") {
Ok(FixedOffset::east(60 * 60)) Ok(FixedOffset::east_opt(60 * 60).unwrap())
} else { } else {
Err(LobbySettingsError::UnknownTimezone(date.into())) Err(LobbySettingsError::UnknownTimezone(date.into()))
} }

View file

@ -47,7 +47,7 @@ pub trait EventHandler: Default {
time: match_time, time: match_time,
subject, subject,
}; };
self.handle(&meta, subject, data, &event); self.handle(&meta, subject, data, event);
Ok(()) Ok(())
} }

View file

@ -20,7 +20,7 @@ thread_local! {
static SUBJECT_END_FINDER: Lazy<Finder<'static>> = Lazy::new(|| Finder::new(br#">""#)); static SUBJECT_END_FINDER: Lazy<Finder<'static>> = Lazy::new(|| Finder::new(br#">""#));
} }
pub fn split_subject_end<'a>(input: &'a str, offset: usize) -> Result<(&'a str, &'a str)> { pub fn split_subject_end(input: &str, offset: usize) -> Result<(&str, &str)> {
let start_offset = 1; let start_offset = 1;
let end_offset = start_offset + offset; let end_offset = start_offset + offset;
debug_assert!(offset <= 2); debug_assert!(offset <= 2);
@ -51,11 +51,9 @@ pub fn skip(input: &str, count: usize) -> Result<&str> {
} }
pub fn skip_matches(input: &str, char: u8) -> (&str, bool) { pub fn skip_matches(input: &str, char: u8) -> (&str, bool) {
if input.as_bytes().get(0) == Some(&char) { match input.split_at_checked(1) {
// safety, we verified that the input has a length of at least 1 Some((first, rest)) if first.as_bytes() == [char] => (rest, true),
(unsafe { input.get_unchecked(1..) }, true) _ => (input, false),
} else {
(input, false)
} }
} }
@ -64,7 +62,7 @@ pub fn find_between_end(input: &str, start: u8, end: u8) -> Option<&str> {
let bytes = input.as_bytes(); let bytes = input.as_bytes();
let end = memrchr(end, bytes)?; let end = memrchr(end, bytes)?;
// safety, memchr returns indices that are inside the input // safety, memchr returns indices that are inside the input
let start = memrchr(start, unsafe { &bytes.get_unchecked(0..end) })?; let start = memrchr(start, unsafe { bytes.get_unchecked(0..end) })?;
// safety, memchr returns indices that are inside the input length and we only split on ascii // safety, memchr returns indices that are inside the input length and we only split on ascii
Some(unsafe { input.get_unchecked((start + 1)..end) }) Some(unsafe { input.get_unchecked((start + 1)..end) })
} }

View file

@ -52,12 +52,14 @@ impl<'a> TryFrom<RawDate<'a>> for NaiveDateTime {
fn try_from(RawDate(raw): RawDate<'a>) -> Result<Self, Self::Error> { fn try_from(RawDate(raw): RawDate<'a>) -> Result<Self, Self::Error> {
Ok( Ok(
NaiveDate::from_ymd(raw[6..10].parse()?, raw[0..2].parse()?, raw[3..5].parse()?) NaiveDate::from_ymd_opt(raw[6..10].parse()?, raw[0..2].parse()?, raw[3..5].parse()?)
.and_hms( .unwrap()
.and_hms_opt(
raw[13..15].parse()?, raw[13..15].parse()?,
raw[16..18].parse()?, raw[16..18].parse()?,
raw[19..21].parse()?, raw[19..21].parse()?,
), )
.unwrap(),
) )
} }
} }
@ -66,7 +68,10 @@ impl<'a> TryFrom<RawDate<'a>> for NaiveDateTime {
fn test_parse_date() { fn test_parse_date() {
let raw = RawDate("08/06/2018 - 21:13:57"); let raw = RawDate("08/06/2018 - 21:13:57");
assert_eq!( assert_eq!(
NaiveDate::from_ymd(2018, 08, 06).and_hms(21, 13, 57), NaiveDate::from_ymd_opt(2018, 8, 6)
.unwrap()
.and_hms_opt(21, 13, 57)
.unwrap(),
raw.try_into().unwrap() raw.try_into().unwrap()
); );
} }
@ -86,7 +91,7 @@ impl Default for RawSubject<'static> {
} }
} }
impl<'a> RawSubject<'a> { impl RawSubject<'_> {
pub fn id(&self) -> Result<SubjectId, SubjectError> { pub fn id(&self) -> Result<SubjectId, SubjectError> {
self.try_into() self.try_into()
} }