move event parsing to derive macros

This commit is contained in:
Robin Appelman 2023-03-05 22:31:48 +01:00
commit 14c1fbe0d9
13 changed files with 591 additions and 460 deletions

2
.gitignore vendored
View file

@ -1,4 +1,4 @@
/target target
/result /result
/.direnv /.direnv
Cargo.lock Cargo.lock

View file

@ -20,6 +20,7 @@ paste = "1"
logos = "0.12" logos = "0.12"
memchr = "2.5.0" memchr = "2.5.0"
ahash = "0.8.3" ahash = "0.8.3"
tf-log-parser-derive = { version = "0.1", path = "./derive" }
[dev-dependencies] [dev-dependencies]
criterion = "0.4" criterion = "0.4"

17
derive/Cargo.toml Normal file
View file

@ -0,0 +1,17 @@
[package]
name = "tf-log-parser-derive"
version = "0.1.0"
edition = "2021"
[lib]
name = "tf_log_parser_derive"
proc-macro = true
[dependencies]
syn = "1.0"
quote = "1.0"
proc-macro2 = "1.0"
syn_util = "0.4"
[dev-dependencies]
tf-log-parser = { version = "0.1", path = ".." }

23
derive/examples/expand.rs Normal file
View file

@ -0,0 +1,23 @@
use tf_log_parser::{
event::{param_parse, parse_field, quoted, ParamIter},
Event, IResult,
};
#[derive(Event)]
pub struct DamageEvent<'a> {
#[event(name = "against")]
pub target: RawSubject<'a>,
pub damage: Option<NonZeroU32>,
#[event(name = "realdamage")]
pub real_damage: Option<NonZeroU32>,
pub weapon: Option<&'a str>,
}
#[derive(Event)]
pub struct ShotFiredEvent {
#[event(quoted)]
pub weapon: u32,
pub damage: Option<u32>,
}
pub fn main() {}

233
derive/src/event.rs Normal file
View file

@ -0,0 +1,233 @@
use crate::{Derivable, DeriveParams};
use proc_macro2::{Ident, TokenStream};
use quote::{quote, quote_spanned};
use syn::{Data, DeriveInput, Error, Field, Fields, Generics, Lifetime, Result, Type, TypePath};
use syn_util::{contains_attribute, get_attribute_value};
macro_rules! bail {
($msg:expr $(,)?) => {
return Err(Error::new(Span::call_site(), &$msg[..]))
};
( $msg:expr => $span_to_blame:expr $(,)? ) => {
return Err(Error::new_spanned(&$span_to_blame, $msg))
};
}
pub struct Event;
impl Derivable for Event {
type Params = EventParams;
fn derive(params: EventParams) -> Result<TokenStream> {
let struct_ident = params.name;
let span = struct_ident.span();
let required_params = params.params.iter().filter(|param| !param.optional);
let optional_params = params.params.iter().filter(|param| param.optional);
let has_optional = params.params.iter().any(|param| param.optional);
let required_fields = required_params
.map(|param| {
let field_name = &param.field_name;
let parser = match (&param.param_name, param.quoted) {
(Some(param_name), true) => {
quote_spanned!(field_name.span() => param_parse_with(#param_name, quoted(parse_field)))
}
(Some(param_name), false) => {
quote_spanned!(field_name.span() => param_parse_with(#param_name, parse_field))
}
(None, true) => {
quote_spanned!(field_name.span() => quoted(parse_field))
}
(None, false) => {
quote_spanned!(field_name.span() => parse_field)
}
};
let skip_after = param.skip_after as usize;
let after = if skip_after > 0 {
quote_spanned!(field_name.span() => let input = &input[#skip_after..];)
} else {
quote!()
};
Ok(quote_spanned!(field_name.span() =>
#[allow(unused_variables)]
let (input, #field_name) = #parser(input)?;
#after
))
})
.collect::<Result<Vec<_>>>()?;
let initiators = params.params.iter().map(|param| {
let field_name = &param.field_name;
if param.optional {
quote_spanned!(field_name.span() => #field_name: Default::default())
} else {
quote_spanned!(field_name.span() => #field_name)
}
});
let initiator = quote!(
#[allow(unused_mut)]
let mut event = #struct_ident {
#(#initiators),*
};
);
let update = if has_optional {
let matches = optional_params
.map(|param| {
let field_name = &param.field_name;
let Some(param_name) = param.param_name.as_deref() else {
bail!("optional fields can't be unnamed" => param.field_name)
};
let parser = if param.quoted {
quote_spanned!(field_name.span() => quoted(parse_field))
} else {
quote_spanned!(field_name.span() => parse_field)
};
Ok(quote_spanned!(
field_name.span() => #param_name => event.#field_name = #parser(value)?.1
))
})
.collect::<Result<Vec<_>>>()?;
quote_spanned!(span => for (key, value) in ParamIter::new(input) {
match key {
#(#matches,)*
_ => {}
}
})
} else {
quote!()
};
let (impl_generics, ty_generics, where_clause) = params.generics.split_for_impl();
let lifetime = params.lifetime;
Ok(
quote_spanned!(span => impl #impl_generics Event<#lifetime> for #struct_ident #ty_generics #where_clause {
fn parse(input: & #lifetime str) -> IResult<Self> {
#(#required_fields)*
#initiator
#update
Ok(("", event))
}
}
),
)
}
}
#[derive(Debug)]
pub struct EventParams {
name: Ident,
lifetime: Lifetime,
generics: Generics,
params: Vec<EventParam>,
}
impl DeriveParams for EventParams {
fn parse(input: &DeriveInput) -> Result<EventParams> {
let Data::Struct(data) = &input.data else {
bail!("only supported on structs" => input)
};
let Fields::Named(fields) = &data.fields else {
bail!("only supported with named fields" => input)
};
let name = input.ident.clone();
let generics = input.generics.clone();
let params = fields
.named
.iter()
.map(EventParam::parse)
.collect::<Result<Vec<_>>>()?;
let mut last_optional = false;
for param in params.iter() {
if last_optional > param.optional {
bail!("optional fields are required to be at the end" => param.field_name)
}
last_optional = param.optional;
}
let lifetime = if let Some(lifetime) =
get_attribute_value::<String>(&input.attrs, &["event", "lifetime"])
{
Lifetime::new(&lifetime, name.span())
} else {
let mut lifetimes = input.generics.lifetimes();
let lifetime = lifetimes
.next()
.cloned()
.map(|lifetime| lifetime.lifetime)
.unwrap_or_else(|| Lifetime::new("'_", name.span()));
if lifetimes.next().is_some() {
bail!("For structs with more than one lifetime, manually specifiying the lifetime is required" => name);
}
lifetime
};
Ok(EventParams {
name,
lifetime,
generics,
params,
})
}
}
#[derive(Debug)]
pub struct EventParam {
field_name: Ident,
param_name: Option<String>,
optional: bool,
skip_after: u64,
quoted: bool,
}
impl EventParam {
pub fn parse(input: &Field) -> Result<EventParam> {
let field_name = input.ident.clone().expect("no name on named fields");
let param_name = if contains_attribute(&input.attrs, &["event", "unnamed"]) {
None
} else {
Some(
get_attribute_value(&input.attrs, &["event", "name"])
.unwrap_or_else(|| field_name.to_string()),
)
};
let is_option = match &input.ty {
Type::Path(TypePath { path, .. }) => {
path.segments
.first()
.map(|segment| segment.ident.to_string())
.as_deref()
== Some("Option")
}
_ => false,
};
let optional = is_option || contains_attribute(&input.attrs, &["event", "default"]);
let skip_after =
get_attribute_value(&input.attrs, &["event", "skip_after"]).unwrap_or_default();
if optional && skip_after > 0 {
bail!("skip_after can't be used with optional fields" => input);
}
let quoted = contains_attribute(&input.attrs, &["event", "quoted"]);
Ok(EventParam {
field_name,
param_name,
optional,
skip_after,
quoted,
})
}
}

38
derive/src/lib.rs Normal file
View file

@ -0,0 +1,38 @@
//! Derive macros for tf-log-parser
extern crate proc_macro;
mod event;
use crate::event::Event;
use proc_macro2::TokenStream;
use std::fmt::Debug;
use syn::{parse_macro_input, DeriveInput, Result};
/// Derive the `Event` trait for a struct
#[proc_macro_derive(Event, attributes(event))]
pub fn derive_pod(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
let expanded = derive_trait::<Event>(parse_macro_input!(input as DeriveInput));
proc_macro::TokenStream::from(expanded)
}
/// Basic wrapper for error handling
fn derive_trait<Trait: Derivable>(input: DeriveInput) -> TokenStream {
derive_trait_inner::<Trait>(input).unwrap_or_else(|err| err.into_compile_error())
}
fn derive_trait_inner<Trait: Derivable>(input: DeriveInput) -> Result<TokenStream> {
let params = Trait::Params::parse(&input)?;
Trait::derive(params)
}
trait Derivable {
type Params: DeriveParams;
fn derive(params: Self::Params) -> Result<TokenStream>;
}
trait DeriveParams: Sized + Debug {
fn parse(input: &DeriveInput) -> Result<Self>;
}

View file

@ -1,3 +1,4 @@
use crate::event::EventFieldFromStr;
use crate::raw_event::{split_player_subject, RawSubject}; use crate::raw_event::{split_player_subject, RawSubject};
use crate::{Error, Result}; use crate::{Error, Result};
use enum_iterator::{all, Sequence}; use enum_iterator::{all, Sequence};
@ -18,6 +19,8 @@ pub enum Team {
Spectator, Spectator,
} }
impl EventFieldFromStr for Team {}
impl Default for Team { impl Default for Team {
fn default() -> Self { fn default() -> Self {
Team::Spectator Team::Spectator
@ -67,6 +70,8 @@ pub enum Class {
Spy, Spy,
} }
impl EventFieldFromStr for Class {}
impl Class { impl Class {
pub fn as_str(self) -> &'static str { pub fn as_str(self) -> &'static str {
match self { match self {
@ -94,15 +99,15 @@ impl FromStr for Class {
fn from_str(s: &str) -> Result<Self, Self::Err> { fn from_str(s: &str) -> Result<Self, Self::Err> {
match s { match s {
"scout" => Ok(Class::Scout), "Scout" | "scout" => Ok(Class::Scout),
"soldier" => Ok(Class::Soldier), "Soldier" | "soldier" => Ok(Class::Soldier),
"pyro" => Ok(Class::Pyro), "Pyro" | "pyro" => Ok(Class::Pyro),
"demoman" => Ok(Class::DemoMan), "Demoman" | "demoman" => Ok(Class::DemoMan),
"heavyweapons" => Ok(Class::HeavyWeapons), "Heavyweapons" | "heavyweapons" | "Heavy" | "heavy" => Ok(Class::HeavyWeapons),
"engineer" => Ok(Class::Engineer), "Engineer" | "engineer" => Ok(Class::Engineer),
"medic" => Ok(Class::Medic), "Medic" | "medic" => Ok(Class::Medic),
"sniper" => Ok(Class::Sniper), "Sniper" | "sniper" => Ok(Class::Sniper),
"spy" => Ok(Class::Spy), "Spy" | "spy" => Ok(Class::Spy),
_ => Err(()), _ => Err(()),
} }
} }

View file

@ -1,89 +1,53 @@
use crate::event::{param_parse, param_parse_with, position, u_int, ParamIter}; use crate::event::{param_parse, param_parse_with, parse_field, ParamIter};
use crate::raw_event::{against_subject_parser, RawSubject}; use crate::raw_event::RawSubject;
use crate::{Event, IResult};
use nom::bytes::complete::{tag, take_while}; use nom::bytes::complete::{tag, take_while};
use nom::combinator::opt; use nom::combinator::opt;
use nom::number::complete::float;
use nom::IResult;
#[derive(Debug)] #[derive(Debug, Event)]
pub struct RoundWinEvent<'a> { pub struct RoundWinEvent<'a> {
#[event(name = "winner")]
pub team: Option<&'a str>, pub team: Option<&'a str>,
} }
pub fn round_win_event_parser(input: &str) -> IResult<&str, RoundWinEvent> { #[derive(Debug, Event)]
let (input, team) = opt(param_parse("against"))(input)?;
Ok((input, RoundWinEvent { team }))
}
#[derive(Debug)]
pub struct RoundLengthEvent { pub struct RoundLengthEvent {
#[event(name = "seconds")]
pub length: Option<f32>, pub length: Option<f32>,
} }
pub fn round_length_event_parser(input: &str) -> IResult<&str, RoundLengthEvent> { #[derive(Debug, Event)]
let (input, length) = opt(param_parse_with("against", float))(input)?;
Ok((input, RoundLengthEvent { length }))
}
#[derive(Debug)]
pub struct LogFileStartedEvent<'a> { pub struct LogFileStartedEvent<'a> {
pub file: Option<&'a str>, pub file: Option<&'a str>,
pub game: Option<&'a str>, pub game: Option<&'a str>,
pub version: Option<&'a str>, pub version: Option<&'a str>,
} }
pub fn log_file_started_event_parser(input: &str) -> IResult<&str, LogFileStartedEvent> {
let (input, file) = opt(param_parse("file"))(input)?;
let (input, game) = opt(param_parse("game"))(input)?;
let (input, version) = opt(param_parse("version"))(input)?;
Ok((
input,
LogFileStartedEvent {
file,
game,
version,
},
))
}
#[derive(Debug)] #[derive(Debug)]
pub struct TournamentModeStartedEvent<'a> { pub struct TournamentModeStartedEvent<'a> {
pub blue: &'a str, pub blue: &'a str,
pub red: &'a str, pub red: &'a str,
} }
pub fn tournament_mode_started_event_parser( impl<'a> Event<'a> for TournamentModeStartedEvent<'a> {
input: &str, fn parse(input: &'a str) -> IResult<Self> {
) -> IResult<&str, TournamentModeStartedEvent> { let (input, _) = tag("\nBlue Team: ")(input)?;
let (input, _) = tag("\nBlue Team: ")(input)?; let (input, blue) = take_while(|c| c != '\n')(input)?;
let (input, blue) = take_while(|c| c != '\n')(input)?; let (input, _) = tag("\nRed Team: ")(input)?;
let (input, _) = tag("\nRed Team: ")(input)?; let (input, red) = take_while(|c| c != '\n')(input)?;
let (input, red) = take_while(|c| c != '\n')(input)?; Ok((input, TournamentModeStartedEvent { blue, red }))
Ok((input, TournamentModeStartedEvent { blue, red })) }
} }
#[derive(Debug)] #[derive(Debug, Event)]
pub struct CaptureBlockedEvent<'a> { pub struct CaptureBlockedEvent<'a> {
pub cp: Option<u8>, pub cp: Option<u8>,
#[event(name = "cpname")]
pub cp_name: Option<&'a str>, pub cp_name: Option<&'a str>,
pub position: Option<(i32, i32, i32)>, pub position: Option<(i32, i32, i32)>,
} }
pub fn capture_blocked_event_parser(input: &str) -> IResult<&str, CaptureBlockedEvent> {
let (input, cp) = opt(param_parse_with("cp", u_int))(input)?;
let (input, cp_name) = opt(param_parse("map"))(input)?;
let (input, position) = opt(param_parse_with("red", position))(input)?;
Ok((
input,
CaptureBlockedEvent {
cp: cp.map(|cp| cp as u8),
cp_name,
position,
},
))
}
#[derive(Debug)] #[derive(Debug)]
pub struct PointCapturedEvent<'a> { pub struct PointCapturedEvent<'a> {
pub cp: Option<u8>, pub cp: Option<u8>,
@ -92,78 +56,53 @@ pub struct PointCapturedEvent<'a> {
pub players: Vec<(RawSubject<'a>, (i32, i32, i32))>, pub players: Vec<(RawSubject<'a>, (i32, i32, i32))>,
} }
pub fn point_captures_event_parser(input: &str) -> IResult<&str, PointCapturedEvent> { impl<'a> Event<'a> for PointCapturedEvent<'a> {
let (input, cp) = opt(param_parse_with("cp", u_int))(input)?; fn parse(input: &'a str) -> IResult<Self> {
let (input, cp_name) = opt(param_parse("map"))(input)?; let (input, cp) = opt(param_parse("cp"))(input)?;
let (input, num_cappers) = opt(param_parse_with("numcappers", u_int))(input)?; let (input, cp_name) = opt(param_parse("cpname"))(input)?;
let (input, num_cappers) = opt(param_parse("numcappers"))(input)?;
let mut players = Vec::new(); let mut players = Vec::new();
let mut params = ParamIter::new(input); let mut params = ParamIter::new(input);
match (params.next(), params.next()) { match (params.next(), params.next()) {
(Some((subject_key, subject)), Some((position_key, position_str))) (Some((subject_key, subject)), Some((position_key, position_str)))
if subject_key.starts_with("player") && position_key.starts_with("position") => if subject_key.starts_with("player") && position_key.starts_with("position") =>
{ {
let (_, subject) = against_subject_parser(subject)?; players.push((parse_field(subject)?.1, parse_field(position_str)?.1));
let (_, position) = position(position_str)?; }
players.push((subject, position)); _ => {}
} }
_ => {}
Ok((
input,
PointCapturedEvent {
cp,
num_cappers,
cp_name,
players,
},
))
} }
Ok((
input,
PointCapturedEvent {
cp: cp.map(|cp| cp as u8),
num_cappers: num_cappers.map(|num| num as u8),
cp_name,
players,
},
))
} }
#[derive(Debug)] #[derive(Debug, Event)]
pub struct CurrentScoreEvent { pub struct CurrentScoreEvent {
#[event(unnamed)]
pub score: u8, pub score: u8,
pub player: u8, #[event(name = "with")]
pub players: u8,
} }
pub fn current_score_event_parser(input: &str) -> IResult<&str, CurrentScoreEvent> { #[derive(Debug, Event)]
let (input, score) = param_parse_with("cp", u_int)(input)?;
let (input, player) = param_parse_with("with", u_int)(input)?;
Ok((
input,
CurrentScoreEvent {
score: score as u8,
player: player as u8,
},
))
}
#[derive(Debug)]
pub struct GameOverEvent<'a> { pub struct GameOverEvent<'a> {
pub reason: &'a str, pub reason: &'a str,
} }
pub fn game_over_event_parser(input: &str) -> IResult<&str, GameOverEvent> { #[derive(Debug, Event)]
let (input, reason) = param_parse("reason")(input)?;
Ok((input, GameOverEvent { reason }))
}
#[derive(Debug)]
pub struct FinalScoreEvent { pub struct FinalScoreEvent {
#[event(unnamed)]
pub score: u8, pub score: u8,
pub player: u8, #[event(name = "with")]
} pub players: u8,
pub fn final_score_event_parser(input: &str) -> IResult<&str, FinalScoreEvent> {
let (input, score) = param_parse_with("cp", u_int)(input)?;
let (input, player) = param_parse_with("with", u_int)(input)?;
Ok((
input,
FinalScoreEvent {
score: score as u8,
player: player as u8,
},
))
} }

View file

@ -1,93 +1,43 @@
use crate::event::{param_parse, param_parse_with, quoted, u_int, ParamIter}; use crate::event::{param_parse_with, parse_field, quoted, ParamIter};
use crate::raw_event::{against_subject_parser, RawSubject}; use crate::raw_event::RawSubject;
use nom::combinator::opt; use crate::{Event, IResult};
use nom::number::complete::float;
use nom::IResult;
#[derive(Debug)] #[derive(Debug, Event)]
pub struct HealedEvent<'a> { pub struct HealedEvent<'a> {
#[event(name = "against")]
pub target: RawSubject<'a>, pub target: RawSubject<'a>,
#[event(name = "healing")]
#[event(quoted)]
pub amount: u32, pub amount: u32,
} }
pub fn healed_event_parser(input: &str) -> IResult<&str, HealedEvent> { #[derive(Debug, Event)]
let (input, subject) = param_parse_with("against", against_subject_parser)(input)?;
let (input, amount) = param_parse_with("healing", quoted(u_int))(input)?;
Ok((
input,
HealedEvent {
target: subject,
amount,
},
))
}
#[derive(Debug)]
pub struct ChargeDeployedEvent<'a> { pub struct ChargeDeployedEvent<'a> {
pub medigun: Option<&'a str>, pub medigun: Option<&'a str>,
} }
pub fn charge_deployed_event_parser(input: &str) -> IResult<&str, ChargeDeployedEvent> { #[derive(Debug, Event)]
let (input, medigun) = opt(param_parse("healing"))(input)?;
Ok((input, ChargeDeployedEvent { medigun }))
}
#[derive(Debug)]
pub struct ChargeEndedEvent { pub struct ChargeEndedEvent {
pub duration: Option<f32>, pub duration: Option<f32>,
} }
pub fn charge_ended_event_parser(input: &str) -> IResult<&str, ChargeEndedEvent> { #[derive(Debug, Event)]
let (input, duration) = opt(param_parse_with("duration", quoted(float)))(input)?;
Ok((input, ChargeEndedEvent { duration }))
}
#[derive(Debug)]
pub struct AdvantageLostEvent { pub struct AdvantageLostEvent {
pub time: Option<f32>, pub time: Option<f32>,
} }
pub fn advantage_lost_event_parser(input: &str) -> IResult<&str, AdvantageLostEvent> { #[derive(Debug, Event)]
let (input, time) = opt(param_parse_with("time", quoted(float)))(input)?;
Ok((input, AdvantageLostEvent { time }))
}
#[derive(Debug)]
pub struct FirstHealEvent { pub struct FirstHealEvent {
pub time: Option<f32>, pub time: Option<f32>,
} }
pub fn first_heal_event_parser(input: &str) -> IResult<&str, FirstHealEvent> { #[derive(Debug, Event)]
let (input, time) = opt(param_parse_with("time", quoted(float)))(input)?;
Ok((input, FirstHealEvent { time }))
}
#[derive(Debug)]
pub struct MedicDeathEvent { pub struct MedicDeathEvent {
#[event(name = "ubercharge")]
pub charge: Option<u32>, pub charge: Option<u32>,
} }
pub fn medic_death_event_parser(input: &str) -> IResult<&str, MedicDeathEvent> { #[derive(Debug, Event)]
let mut charge = None;
for (key, value) in ParamIter::new(input) {
if key == "ubercharge" {
charge = Some(quoted(u_int)(value)?.1);
}
}
Ok((input, MedicDeathEvent { charge }))
}
#[derive(Debug)]
pub struct MedicDeathExEvent { pub struct MedicDeathExEvent {
pub charge_percentage: Option<u8>, pub charge_percentage: Option<u8>,
} }
pub fn medic_death_ex_event_parser(input: &str) -> IResult<&str, MedicDeathExEvent> {
let (input, charge_percentage) = opt(param_parse_with("time", quoted(u_int)))(input)?;
Ok((
input,
MedicDeathExEvent {
charge_percentage: charge_percentage.map(|charge: u32| charge as u8),
},
))
}

View file

@ -3,15 +3,19 @@ mod medic;
mod player; mod player;
use crate::event::game::{RoundLengthEvent, RoundWinEvent}; use crate::event::game::{RoundLengthEvent, RoundWinEvent};
use crate::{RawEvent, RawEventType, SubjectId}; use crate::raw_event::{against_subject_parser, RawSubject};
use crate::{IResult, RawEvent, RawEventType, SubjectId};
pub use game::*; pub use game::*;
pub use medic::*; pub use medic::*;
use nom::bytes::complete::{tag, take_while}; use nom::bytes::complete::{tag, take_while};
use nom::character::complete::{alpha1, digit1}; use nom::character::complete::{alpha1, digit1};
use nom::combinator::opt; use nom::combinator::opt;
use nom::error::{ErrorKind, ParseError}; use nom::error::{ErrorKind, ParseError};
use nom::{Err, IResult}; use nom::number::complete::float;
use nom::Err;
pub use player::*; pub use player::*;
use std::net::SocketAddr;
use std::num::NonZeroU32;
use std::str::FromStr; use std::str::FromStr;
use thiserror::Error; use thiserror::Error;
@ -30,7 +34,7 @@ trait GameEventErrTrait<T> {
fn with_type(self, ty: RawEventType) -> Result<T, GameEventError>; fn with_type(self, ty: RawEventType) -> Result<T, GameEventError>;
} }
impl<'a, T> GameEventErrTrait<T> for IResult<&str, T> { impl<'a, T> GameEventErrTrait<T> for IResult<'a, T> {
fn with_type(self, ty: RawEventType) -> Result<T, GameEventError> { fn with_type(self, ty: RawEventType) -> Result<T, GameEventError> {
self.map_err(|err| match err { self.map_err(|err| match err {
nom::Err::Error(e) | nom::Err::Failure(e) => GameEventError::Error { nom::Err::Error(e) | nom::Err::Failure(e) => GameEventError::Error {
@ -47,6 +51,14 @@ impl<'a, T> GameEventErrTrait<T> for IResult<&str, T> {
} }
} }
pub trait Event<'a>: Sized + 'a {
fn parse(input: &'a str) -> IResult<Self>;
}
fn parse_event<'a, T: Event<'a>>(input: &'a str) -> IResult<T> {
T::parse(input)
}
#[derive(Debug)] #[derive(Debug)]
pub struct EventMeta { pub struct EventMeta {
pub time: u32, pub time: u32,
@ -104,116 +116,100 @@ impl<'a> GameEvent<'a> {
pub fn parse(raw: &RawEvent<'a>) -> Result<GameEvent<'a>, GameEventError> { pub fn parse(raw: &RawEvent<'a>) -> Result<GameEvent<'a>, GameEventError> {
Ok(match raw.ty { Ok(match raw.ty {
RawEventType::ShotFired => { RawEventType::ShotFired => {
GameEvent::ShotFired(shot_fired_event_parser(raw.params).with_type(raw.ty)?) GameEvent::ShotFired(parse_event(raw.params).with_type(raw.ty)?)
}
RawEventType::ShotHit => {
GameEvent::ShotHit(shot_hit_event_parser(raw.params).with_type(raw.ty)?)
}
RawEventType::Damage => {
GameEvent::Damage(damage_event_parser(raw.params).with_type(raw.ty)?)
}
RawEventType::Killed => {
GameEvent::Kill(kill_event_parser(raw.params).with_type(raw.ty)?)
} }
RawEventType::ShotHit => GameEvent::ShotHit(parse_event(raw.params).with_type(raw.ty)?),
RawEventType::Damage => GameEvent::Damage(parse_event(raw.params).with_type(raw.ty)?),
RawEventType::Killed => GameEvent::Kill(parse_event(raw.params).with_type(raw.ty)?),
RawEventType::KillAssist => { RawEventType::KillAssist => {
GameEvent::KillAssist(kill_assist_event_parser(raw.params).with_type(raw.ty)?) GameEvent::KillAssist(parse_event(raw.params).with_type(raw.ty)?)
} }
RawEventType::SayTeam => GameEvent::SayTeam(raw.params.trim_matches('"')), RawEventType::SayTeam => GameEvent::SayTeam(raw.params.trim_matches('"')),
RawEventType::Say => GameEvent::Say(raw.params.trim_matches('"')), RawEventType::Say => GameEvent::Say(raw.params.trim_matches('"')),
RawEventType::Healed => { RawEventType::Healed => GameEvent::Healed(parse_event(raw.params).with_type(raw.ty)?),
GameEvent::Healed(healed_event_parser(raw.params).with_type(raw.ty)?) RawEventType::ChargeDeployed => {
GameEvent::ChargeDeployed(parse_event(raw.params).with_type(raw.ty)?)
} }
RawEventType::ChargeDeployed => GameEvent::ChargeDeployed(
charge_deployed_event_parser(raw.params).with_type(raw.ty)?,
),
RawEventType::ChargeEnd => { RawEventType::ChargeEnd => {
GameEvent::ChargeEnded(charge_ended_event_parser(raw.params).with_type(raw.ty)?) GameEvent::ChargeEnded(parse_event(raw.params).with_type(raw.ty)?)
} }
RawEventType::UberAdvantageLost => { RawEventType::UberAdvantageLost => {
GameEvent::AdvantageLost(advantage_lost_event_parser(raw.params).with_type(raw.ty)?) GameEvent::AdvantageLost(parse_event(raw.params).with_type(raw.ty)?)
} }
RawEventType::FirstHealAfterSpawn => { RawEventType::FirstHealAfterSpawn => {
GameEvent::FirstHeal(first_heal_event_parser(raw.params).with_type(raw.ty)?) GameEvent::FirstHeal(parse_event(raw.params).with_type(raw.ty)?)
} }
RawEventType::ChargeReady => GameEvent::ChargeReady, RawEventType::ChargeReady => GameEvent::ChargeReady,
RawEventType::MedicDeath => { RawEventType::MedicDeath => {
GameEvent::MedicDeath(medic_death_event_parser(raw.params).with_type(raw.ty)?) GameEvent::MedicDeath(parse_event(raw.params).with_type(raw.ty)?)
} }
RawEventType::MedicDeathEx => { RawEventType::MedicDeathEx => {
GameEvent::MedicDeathEx(medic_death_ex_event_parser(raw.params).with_type(raw.ty)?) GameEvent::MedicDeathEx(parse_event(raw.params).with_type(raw.ty)?)
}
RawEventType::Spawned => {
GameEvent::Spawned(spawn_event_parser(raw.params).with_type(raw.ty)?)
} }
RawEventType::Spawned => GameEvent::Spawned(parse_event(raw.params).with_type(raw.ty)?),
RawEventType::ChangedRole => { RawEventType::ChangedRole => {
GameEvent::RoleChange(role_changed_event_parser(raw.params).with_type(raw.ty)?) GameEvent::RoleChange(parse_event(raw.params).with_type(raw.ty)?)
} }
RawEventType::RoundStart => GameEvent::RoundStart, RawEventType::RoundStart => GameEvent::RoundStart,
RawEventType::RoundLength => { RawEventType::RoundLength => {
GameEvent::RoundLength(round_length_event_parser(raw.params).with_type(raw.ty)?) GameEvent::RoundLength(parse_event(raw.params).with_type(raw.ty)?)
} }
RawEventType::RoundWin => { RawEventType::RoundWin => {
GameEvent::RoundWin(round_win_event_parser(raw.params).with_type(raw.ty)?) GameEvent::RoundWin(parse_event(raw.params).with_type(raw.ty)?)
} }
RawEventType::RoundOvertime => GameEvent::RoundOverTime, RawEventType::RoundOvertime => GameEvent::RoundOverTime,
RawEventType::LogFileStarted => GameEvent::LogFileStarted( RawEventType::LogFileStarted => {
log_file_started_event_parser(raw.params).with_type(raw.ty)?, GameEvent::LogFileStarted(parse_event(raw.params).with_type(raw.ty)?)
), }
RawEventType::Connected => { RawEventType::Connected => {
GameEvent::Connected(connected_event_parser(raw.params).with_type(raw.ty)?) GameEvent::Connected(parse_event(raw.params).with_type(raw.ty)?)
} }
RawEventType::Disconnected => { RawEventType::Disconnected => {
GameEvent::Disconnect(disconnected_event_parser(raw.params).with_type(raw.ty)?) GameEvent::Disconnect(parse_event(raw.params).with_type(raw.ty)?)
} }
RawEventType::SteamIdValidated => GameEvent::SteamIdValidated, RawEventType::SteamIdValidated => GameEvent::SteamIdValidated,
RawEventType::Entered => GameEvent::Entered, RawEventType::Entered => GameEvent::Entered,
RawEventType::Joined => { RawEventType::Joined => GameEvent::Joined(parse_event(raw.params).with_type(raw.ty)?),
GameEvent::Joined(joined_team_event_parser(raw.params).with_type(raw.ty)?) RawEventType::Suicide => GameEvent::Suicide(parse_event(raw.params).with_type(raw.ty)?),
}
RawEventType::Suicide => {
GameEvent::Suicide(committed_suicide_event_parser(raw.params).with_type(raw.ty)?)
}
RawEventType::PickedUp => { RawEventType::PickedUp => {
GameEvent::PickedUp(picked_up_event_parser(raw.params).with_type(raw.ty)?) GameEvent::PickedUp(parse_event(raw.params).with_type(raw.ty)?)
} }
RawEventType::Domination => { RawEventType::Domination => {
GameEvent::Domination(domination_event_parser(raw.params).with_type(raw.ty)?) GameEvent::Domination(parse_event(raw.params).with_type(raw.ty)?)
} }
RawEventType::EmptyUber => GameEvent::EmptyUber, RawEventType::EmptyUber => GameEvent::EmptyUber,
RawEventType::Revenge => { RawEventType::Revenge => GameEvent::Revenge(parse_event(raw.params).with_type(raw.ty)?),
GameEvent::Revenge(revenge_event_parser(raw.params).with_type(raw.ty)?) RawEventType::TournamentStart => {
GameEvent::TournamentModeStarted(parse_event(raw.params).with_type(raw.ty)?)
}
RawEventType::CaptureBlocked => {
GameEvent::CaptureBlocked(parse_event(raw.params).with_type(raw.ty)?)
} }
RawEventType::TournamentStart => GameEvent::TournamentModeStarted(
tournament_mode_started_event_parser(raw.params).with_type(raw.ty)?,
),
RawEventType::CaptureBlocked => GameEvent::CaptureBlocked(
capture_blocked_event_parser(raw.params).with_type(raw.ty)?,
),
RawEventType::PointCaptured => { RawEventType::PointCaptured => {
GameEvent::PointCaptured(point_captures_event_parser(raw.params).with_type(raw.ty)?) GameEvent::PointCaptured(parse_event(raw.params).with_type(raw.ty)?)
} }
RawEventType::CurrentScore => { RawEventType::CurrentScore => {
GameEvent::CurrentScore(current_score_event_parser(raw.params).with_type(raw.ty)?) GameEvent::CurrentScore(parse_event(raw.params).with_type(raw.ty)?)
} }
RawEventType::PlayerBuiltObject => { RawEventType::PlayerBuiltObject => {
GameEvent::BuiltObject(built_object_event_parser(raw.params).with_type(raw.ty)?) GameEvent::BuiltObject(parse_event(raw.params).with_type(raw.ty)?)
} }
RawEventType::PlayerKilledObject => { RawEventType::PlayerKilledObject => {
GameEvent::KilledObject(killed_object_event_parser(raw.params).with_type(raw.ty)?) GameEvent::KilledObject(parse_event(raw.params).with_type(raw.ty)?)
} }
RawEventType::PlayerExtinguished => { RawEventType::PlayerExtinguished => {
GameEvent::Extinguished(extinguished_event_parser(raw.params).with_type(raw.ty)?) GameEvent::Extinguished(parse_event(raw.params).with_type(raw.ty)?)
} }
RawEventType::GameOver => { RawEventType::GameOver => {
GameEvent::GameOver(game_over_event_parser(raw.params).with_type(raw.ty)?) GameEvent::GameOver(parse_event(raw.params).with_type(raw.ty)?)
} }
RawEventType::FinalScore => { RawEventType::FinalScore => {
GameEvent::FinalScore(final_score_event_parser(raw.params).with_type(raw.ty)?) GameEvent::FinalScore(parse_event(raw.params).with_type(raw.ty)?)
} }
RawEventType::LogFileClosed => GameEvent::LogFileClosed, RawEventType::LogFileClosed => GameEvent::LogFileClosed,
RawEventType::ObjectDetonated => GameEvent::ObjectDetonated( RawEventType::ObjectDetonated => {
object_detonated_event_parser(raw.params).with_type(raw.ty)?, GameEvent::ObjectDetonated(parse_event(raw.params).with_type(raw.ty)?)
), }
_ => { _ => {
todo!("{:?} not parsed yet", raw.ty); todo!("{:?} not parsed yet", raw.ty);
} }
@ -221,7 +217,7 @@ impl<'a> GameEvent<'a> {
} }
} }
struct ParamIter<'a> { pub struct ParamIter<'a> {
input: &'a str, input: &'a str,
} }
@ -241,7 +237,7 @@ impl<'a> Iterator for ParamIter<'a> {
} }
} }
fn param_pair_parse(input: &str) -> IResult<&str, (&str, &str)> { fn param_pair_parse(input: &str) -> IResult<'_, (&str, &str)> {
let (input, open_tag) = opt(tag("("))(input)?; let (input, open_tag) = opt(tag("("))(input)?;
let (input, key) = alpha1(input)?; let (input, key) = alpha1(input)?;
@ -255,9 +251,9 @@ fn param_pair_parse(input: &str) -> IResult<&str, (&str, &str)> {
Ok((input, (key, value))) Ok((input, (key, value)))
} }
fn quoted<'a, T, P: Fn(&'a str) -> IResult<&'a str, T>>( fn quoted<'a, T, P: Fn(&'a str) -> IResult<'a, T>>(
parser: P, parser: P,
) -> impl Fn(&'a str) -> IResult<&'a str, T> { ) -> impl Fn(&'a str) -> IResult<'a, T> {
move |input| { move |input| {
let (input, _) = tag(r#"""#)(input)?; let (input, _) = tag(r#"""#)(input)?;
let (input, res) = parser(input)?; let (input, res) = parser(input)?;
@ -266,16 +262,17 @@ fn quoted<'a, T, P: Fn(&'a str) -> IResult<&'a str, T>>(
} }
} }
fn param_parse<'a>(key: &'a str) -> impl Fn(&'a str) -> IResult<&'a str, &'a str> { pub fn param_parse<'a, T: EventField<'a>>(key: &'a str) -> impl Fn(&'a str) -> IResult<'a, T> {
param_parse_with(key, quoted(take_while(|c| c != '"'))) param_parse_with(key, T::parse_field)
} }
fn param_parse_with<'a, T, P: Fn(&'a str) -> IResult<&'a str, T>>( pub fn param_parse_with<'a, T, P: Fn(&'a str) -> IResult<'a, T>>(
key: &'a str, key: &'a str,
parser: P, parser: P,
) -> impl Fn(&'a str) -> IResult<&'a str, T> { ) -> impl Fn(&'a str) -> IResult<'a, T> {
move |input: &str| { move |input: &str| {
debug_assert!(input.as_bytes()[0] != b' '); debug_assert!(input.as_bytes()[0] != b' ');
let has_open = input.as_bytes()[0] == b'('; let has_open = input.as_bytes()[0] == b'(';
let input = &input[has_open as usize..]; let input = &input[has_open as usize..];
@ -296,13 +293,13 @@ fn param_parse_with<'a, T, P: Fn(&'a str) -> IResult<&'a str, T>>(
} }
} }
fn parse_from_str<T: FromStr>(input: &str) -> IResult<&str, T> { fn parse_from_str<'a, T: FromStr + 'a>(input: &'a str) -> IResult<T> {
T::from_str(input) T::from_str(input)
.map(|res| ("", res)) .map(|res| ("", res))
.map_err(|_| nom::Err::Error(nom::error::Error::from_error_kind(input, ErrorKind::IsNot))) .map_err(|_| nom::Err::Error(nom::error::Error::from_error_kind(input, ErrorKind::IsNot)))
} }
fn int(input: &str) -> IResult<&str, i32> { fn int(input: &str) -> IResult<i32> {
let (input, sign) = opt(tag("-"))(input)?; let (input, sign) = opt(tag("-"))(input)?;
let (input, raw) = digit1(input)?; let (input, raw) = digit1(input)?;
let val: i32 = raw let val: i32 = raw
@ -311,15 +308,23 @@ fn int(input: &str) -> IResult<&str, i32> {
Ok((input, if sign.is_some() { -val } else { val })) Ok((input, if sign.is_some() { -val } else { val }))
} }
fn u_int(input: &str) -> IResult<&str, u32> { fn u_int(input: &str) -> IResult<u32> {
let (input, quote) = opt(tag("\""))(input)?;
let (input, raw) = digit1(input)?; let (input, raw) = digit1(input)?;
let val = raw let val = raw
.parse() .parse()
.map_err(|_| nom::Err::Error(nom::error::Error::new(raw, ErrorKind::Digit)))?; .map_err(|_| nom::Err::Error(nom::error::Error::new(raw, ErrorKind::Digit)))?;
let input = if quote.is_some() {
tag("\"")(input)?.0
} else {
input
};
Ok((input, val)) Ok((input, val))
} }
fn position(input: &str) -> IResult<&str, (i32, i32, i32)> { pub fn position(input: &str) -> IResult<(i32, i32, i32)> {
let (input, x) = int(input)?; let (input, x) = int(input)?;
let (input, _) = tag(" ")(input)?; let (input, _) = tag(" ")(input)?;
let (input, y) = int(input)?; let (input, y) = int(input)?;
@ -327,3 +332,84 @@ fn position(input: &str) -> IResult<&str, (i32, i32, i32)> {
let (input, z) = int(input)?; let (input, z) = int(input)?;
Ok((input, (x, y, z))) Ok((input, (x, y, z)))
} }
pub trait EventField<'a>: Sized + 'a {
fn parse_field(input: &'a str) -> crate::IResult<Self>;
}
impl<'a> EventField<'a> for &'a str {
fn parse_field(input: &'a str) -> crate::IResult<Self> {
if input.starts_with('"') {
quoted(take_while(|c| c != '"'))(input)
} else {
Ok(("", input))
}
}
}
impl<'a> EventField<'a> for i32 {
fn parse_field(input: &'a str) -> crate::IResult<Self> {
int(input)
}
}
impl<'a> EventField<'a> for u32 {
fn parse_field(input: &'a str) -> crate::IResult<Self> {
u_int(input)
}
}
impl<'a> EventField<'a> for f32 {
fn parse_field(input: &'a str) -> crate::IResult<Self> {
float(input)
}
}
impl<'a> EventField<'a> for u8 {
fn parse_field(input: &'a str) -> crate::IResult<Self> {
u_int(input).map(|(rest, num)| (rest, num as u8))
}
}
impl<'a, T: EventField<'a>> EventField<'a> for Option<T> {
fn parse_field(input: &'a str) -> crate::IResult<Self> {
T::parse_field(input).map(|(rest, int)| (rest, Some(int)))
}
}
pub trait EventFieldFromStr: FromStr {}
impl<'a, T: EventFieldFromStr + 'a> EventField<'a> for T {
fn parse_field(input: &'a str) -> crate::IResult<Self> {
parse_from_str(input)
}
}
impl EventFieldFromStr for SocketAddr {}
impl<'a, T: EventField<'a>> EventField<'a> for (T, T, T) {
fn parse_field(input: &'a str) -> crate::IResult<Self> {
let (input, x) = T::parse_field(input)?;
let (input, _) = tag(" ")(input)?;
let (input, y) = T::parse_field(input)?;
let (input, _) = tag(" ")(input)?;
let (input, z) = T::parse_field(input)?;
Ok((input, (x, y, z)))
}
}
impl<'a> EventField<'a> for Option<NonZeroU32> {
fn parse_field(input: &'a str) -> crate::IResult<Self> {
u32::parse_field(input).map(|(rest, int)| (rest, NonZeroU32::new(int)))
}
}
impl<'a> EventField<'a> for RawSubject<'a> {
fn parse_field(input: &'a str) -> crate::IResult<Self> {
against_subject_parser(input)
}
}
pub fn parse_field<'a, T: EventField<'a>>(input: &'a str) -> crate::IResult<T> {
T::parse_field(input)
}

View file

@ -1,286 +1,124 @@
use crate::common::{Class, Team}; use crate::common::{Class, Team};
use crate::event::{param_parse, param_parse_with, parse_from_str, position, u_int, ParamIter}; use crate::event::{param_parse_with, parse_field, ParamIter};
use crate::raw_event::{against_subject_parser, RawSubject}; use crate::raw_event::RawSubject;
use nom::combinator::opt; use crate::{Event, IResult};
use nom::IResult;
use std::net::SocketAddr; use std::net::SocketAddr;
use std::num::NonZeroU32; use std::num::NonZeroU32;
#[derive(Debug)] #[derive(Debug, Event)]
pub struct ShotFiredEvent<'a> { pub struct ShotFiredEvent<'a> {
pub weapon: Option<&'a str>, pub weapon: Option<&'a str>,
} }
pub fn shot_fired_event_parser(input: &str) -> IResult<&str, ShotFiredEvent> { #[derive(Debug, Event)]
let (input, weapon) = opt(param_parse("weapon"))(input)?;
Ok((input, ShotFiredEvent { weapon }))
}
#[derive(Debug)]
pub struct ShotHitEvent<'a> { pub struct ShotHitEvent<'a> {
pub weapon: Option<&'a str>, pub weapon: Option<&'a str>,
} }
pub fn shot_hit_event_parser(input: &str) -> IResult<&str, ShotHitEvent> { #[derive(Debug, Event)]
let (input, weapon) = opt(param_parse("weapon"))(input)?;
Ok((input, ShotHitEvent { weapon }))
}
#[derive(Debug)]
pub struct DamageEvent<'a> { pub struct DamageEvent<'a> {
#[event(name = "against")]
pub target: RawSubject<'a>, pub target: RawSubject<'a>,
pub damage: Option<NonZeroU32>, pub damage: Option<NonZeroU32>,
#[event(name = "realdamage")]
pub real_damage: Option<NonZeroU32>, pub real_damage: Option<NonZeroU32>,
pub weapon: Option<&'a str>, pub weapon: Option<&'a str>,
} }
pub fn damage_event_parser(input: &str) -> IResult<&str, DamageEvent> { #[derive(Debug, Event)]
let (input, target) = param_parse_with("against", against_subject_parser)(input)?;
let mut event = DamageEvent {
target,
damage: None,
real_damage: None,
weapon: None,
};
for (key, value) in ParamIter::new(input) {
match key {
"damage" => event.damage = NonZeroU32::new(u_int(value)?.1),
"realdamage" => event.real_damage = NonZeroU32::new(u_int(value)?.1),
"weapon" => event.weapon = Some(value.trim_matches('"')),
_ => {}
}
}
Ok(("", event))
}
#[derive(Debug)]
pub struct KillEvent<'a> { pub struct KillEvent<'a> {
#[event(unnamed)]
#[event(skip_after = 1)]
pub target: RawSubject<'a>, pub target: RawSubject<'a>,
#[event(name = "with")]
pub weapon: &'a str, pub weapon: &'a str,
pub attacker_position: Option<(i32, i32, i32)>, pub attacker_position: Option<(i32, i32, i32)>,
pub victim_position: Option<(i32, i32, i32)>, pub victim_position: Option<(i32, i32, i32)>,
} }
pub fn kill_event_parser(input: &str) -> IResult<&str, KillEvent> { #[derive(Debug, Event)]
let (input, target) = against_subject_parser(input)?;
let (input, weapon) = param_parse("with")(&input[1..])?;
let mut event = KillEvent {
target,
weapon,
attacker_position: None,
victim_position: None,
};
for (key, value) in ParamIter::new(input) {
match key {
"attacker_position" => event.attacker_position = Some(position(value)?.1),
"victim_position" => event.victim_position = Some(position(value)?.1),
_ => {}
}
}
Ok(("", event))
}
#[derive(Debug)]
pub struct KillAssistEvent<'a> { pub struct KillAssistEvent<'a> {
pub target: RawSubject<'a>, pub target: RawSubject<'a>,
pub attacker_position: Option<(i32, i32, i32)>, pub attacker_position: Option<(i32, i32, i32)>,
pub victim_position: Option<(i32, i32, i32)>, pub victim_position: Option<(i32, i32, i32)>,
} }
pub fn kill_assist_event_parser(input: &str) -> IResult<&str, KillAssistEvent> { #[derive(Debug, Event)]
let (input, target) = param_parse_with("against", against_subject_parser)(input)?;
let mut event = KillAssistEvent {
target,
attacker_position: None,
victim_position: None,
};
for (key, value) in ParamIter::new(input) {
match key {
"attacker_position" => event.attacker_position = Some(position(value)?.1),
"victim_position" => event.victim_position = Some(position(value)?.1),
_ => {}
}
}
Ok(("", event))
}
#[derive(Debug)]
pub struct SpawnEvent { pub struct SpawnEvent {
#[event(name = "as")]
pub class: Option<Class>, pub class: Option<Class>,
} }
pub fn spawn_event_parser(input: &str) -> IResult<&str, SpawnEvent> { #[derive(Debug, Event)]
let (input, class_str) = param_parse("as")(input)?;
Ok((
input,
SpawnEvent {
class: class_str.parse().ok(),
},
))
}
#[derive(Debug)]
pub struct RoleChangeEvent { pub struct RoleChangeEvent {
#[event(name = "to")]
pub class: Option<Class>, pub class: Option<Class>,
} }
pub fn role_changed_event_parser(input: &str) -> IResult<&str, RoleChangeEvent> { #[derive(Debug, Event)]
let (input, class_str) = param_parse("to")(input)?;
Ok((
input,
RoleChangeEvent {
class: class_str.parse().ok(),
},
))
}
#[derive(Debug)]
pub struct ConnectedEvent { pub struct ConnectedEvent {
#[event(name = "to")]
pub address: SocketAddr, pub address: SocketAddr,
} }
pub fn connected_event_parser(input: &str) -> IResult<&str, ConnectedEvent> { #[derive(Debug, Event)]
let (input, address) = param_parse_with("to", parse_from_str)(input)?;
Ok((input, ConnectedEvent { address }))
}
#[derive(Debug)]
pub struct JoinedTeamEvent { pub struct JoinedTeamEvent {
pub team: Team, pub team: Team,
} }
pub fn joined_team_event_parser(input: &str) -> IResult<&str, JoinedTeamEvent> { #[derive(Debug, Event)]
let (input, team) = param_parse_with("team", parse_from_str)(input)?;
Ok((input, JoinedTeamEvent { team }))
}
#[derive(Debug)]
pub struct CommittedSuicideEvent<'a> { pub struct CommittedSuicideEvent<'a> {
#[event(name = "with")]
pub weapon: &'a str, pub weapon: &'a str,
pub attacker_position: Option<(i32, i32, i32)>, pub attacker_position: Option<(i32, i32, i32)>,
} }
pub fn committed_suicide_event_parser(input: &str) -> IResult<&str, CommittedSuicideEvent> { #[derive(Debug, Event)]
let (input, weapon) = param_parse("with")(input)?;
let (input, attacker_position) = opt(param_parse_with("attacker_position", position))(input)?;
Ok((
input,
CommittedSuicideEvent {
weapon,
attacker_position,
},
))
}
#[derive(Debug)]
pub struct PickedUpEvent<'a> { pub struct PickedUpEvent<'a> {
pub item: &'a str, pub item: &'a str,
} }
pub fn picked_up_event_parser(input: &str) -> IResult<&str, PickedUpEvent> { #[derive(Debug, Event)]
let (input, item) = param_parse("item")(input)?;
Ok((input, PickedUpEvent { item }))
}
#[derive(Debug)]
pub struct DominationEvent<'a> { pub struct DominationEvent<'a> {
pub against: RawSubject<'a>, pub against: RawSubject<'a>,
} }
pub fn domination_event_parser(input: &str) -> IResult<&str, DominationEvent> { #[derive(Debug, Event)]
let (input, against) = param_parse_with("against", against_subject_parser)(input)?;
Ok((input, DominationEvent { against }))
}
#[derive(Debug)]
pub struct RevengeEvent<'a> { pub struct RevengeEvent<'a> {
pub against: RawSubject<'a>, pub against: RawSubject<'a>,
} }
pub fn revenge_event_parser(input: &str) -> IResult<&str, RevengeEvent> { #[derive(Debug, Event)]
let (input, against) = param_parse_with("against", against_subject_parser)(input)?;
Ok((input, RevengeEvent { against }))
}
#[derive(Debug)]
pub struct DisconnectEvent<'a> { pub struct DisconnectEvent<'a> {
pub reason: Option<&'a str>, pub reason: Option<&'a str>,
} }
pub fn disconnected_event_parser(input: &str) -> IResult<&str, DisconnectEvent> { #[derive(Debug, Event)]
let (input, reason) = opt(param_parse("reason"))(input)?;
Ok((input, DisconnectEvent { reason }))
}
#[derive(Debug)]
pub struct BuiltObjectEvent<'a> { pub struct BuiltObjectEvent<'a> {
pub object: Option<&'a str>, pub object: Option<&'a str>,
pub position: Option<(i32, i32, i32)>, pub position: Option<(i32, i32, i32)>,
} }
pub fn built_object_event_parser(input: &str) -> IResult<&str, BuiltObjectEvent> { #[derive(Debug, Event)]
let (input, object) = opt(param_parse("object"))(input)?;
let (input, position) = opt(param_parse_with("position", position))(input)?;
Ok((input, BuiltObjectEvent { object, position }))
}
#[derive(Debug)]
pub struct KilledObjectEvent<'a> { pub struct KilledObjectEvent<'a> {
pub object: Option<&'a str>, pub object: Option<&'a str>,
pub weapon: Option<&'a str>, pub weapon: Option<&'a str>,
#[event(name = "objectowner")]
pub object_owner: Option<RawSubject<'a>>, pub object_owner: Option<RawSubject<'a>>,
pub attacker_position: Option<(i32, i32, i32)>, pub attacker_position: Option<(i32, i32, i32)>,
} }
pub fn killed_object_event_parser(input: &str) -> IResult<&str, KilledObjectEvent> { #[derive(Debug, Event)]
let (input, object) = opt(param_parse("object"))(input)?;
let (input, weapon) = opt(param_parse("weapon"))(input)?;
let (input, object_owner) =
opt(param_parse_with("objectowner", against_subject_parser))(input)?;
let (input, attacker_position) = opt(param_parse_with("attacker_position", position))(input)?;
Ok((
input,
KilledObjectEvent {
object,
weapon,
object_owner,
attacker_position,
},
))
}
#[derive(Debug)]
pub struct ObjectDetonatedEvent<'a> { pub struct ObjectDetonatedEvent<'a> {
pub object: Option<&'a str>, pub object: Option<&'a str>,
#[event(name = "attacker_position")]
pub position: Option<(i32, i32, i32)>, pub position: Option<(i32, i32, i32)>,
} }
pub fn object_detonated_event_parser(input: &str) -> IResult<&str, ObjectDetonatedEvent> { #[derive(Debug, Event)]
let (input, object) = opt(param_parse("object"))(input)?;
let (input, position) = opt(param_parse_with("attacker_position", position))(input)?;
Ok((input, ObjectDetonatedEvent { object, position }))
}
#[derive(Debug)]
pub struct ExtinguishedEvent<'a> { pub struct ExtinguishedEvent<'a> {
pub against: RawSubject<'a>, pub against: RawSubject<'a>,
pub with: &'a str, pub with: &'a str,
pub attacker_position: Option<(i32, i32, i32)>, pub attacker_position: Option<(i32, i32, i32)>,
pub victim_position: Option<(i32, i32, i32)>, pub victim_position: Option<(i32, i32, i32)>,
} }
pub fn extinguished_event_parser(input: &str) -> IResult<&str, ExtinguishedEvent> {
let (input, against) = param_parse_with("against", against_subject_parser)(input)?;
let (input, with) = param_parse("with")(input)?;
let (input, attacker_position) = opt(param_parse_with("attacker_position", position))(input)?;
let (input, victim_position) = opt(param_parse_with("victim_position", position))(input)?;
Ok((
input,
ExtinguishedEvent {
against,
with,
attacker_position,
victim_position,
},
))
}

View file

@ -1,5 +1,3 @@
extern crate core;
pub use crate::common::{SteamId3, SubjectData, SubjectError, SubjectId}; pub use crate::common::{SteamId3, SubjectData, SubjectError, SubjectId};
use crate::event::GameEventError; use crate::event::GameEventError;
pub use crate::module::EventHandler; pub use crate::module::EventHandler;
@ -8,13 +6,14 @@ use crate::module::{
}; };
pub use crate::subjectmap::SubjectMap; pub use crate::subjectmap::SubjectMap;
use chrono::NaiveDateTime; use chrono::NaiveDateTime;
pub use event::{EventMeta, GameEvent}; pub use event::{Event, EventMeta, GameEvent};
use memchr::memmem::{find_iter, FindIter}; use memchr::memmem::{find_iter, FindIter};
use nom::Err; use nom::Err;
pub use raw_event::{RawEvent, RawEventType}; pub use raw_event::{RawEvent, RawEventType};
use std::collections::BTreeMap; use std::collections::BTreeMap;
use std::convert::TryInto; use std::convert::TryInto;
use std::fmt::Debug; use std::fmt::Debug;
pub use tf_log_parser_derive::Event;
use thiserror::Error; use thiserror::Error;
mod common; mod common;
@ -53,6 +52,9 @@ impl From<nom::error::Error<&'_ str>> for Error {
type Result<O, E = Error> = std::result::Result<O, E>; type Result<O, E = Error> = std::result::Result<O, E>;
#[doc(hidden)]
pub type IResult<'a, O> = nom::IResult<&'a str, O>;
pub fn parse( pub fn parse(
log: &str, log: &str,
) -> Result< ) -> Result<
@ -74,9 +76,7 @@ pub fn parse_with_handler<Handler: EventHandler>(
), ),
Error, Error,
> { > {
let events = LineSplit::new(log) let events = LineSplit::new(log).map(RawEvent::parse);
.filter(|line| !line.is_empty())
.map(RawEvent::parse);
let mut handler = Handler::default(); let mut handler = Handler::default();
@ -160,7 +160,7 @@ impl<'a> Iterator for LineSplit<'a> {
None if self.start < self.input.len() => { None if self.start < self.input.len() => {
let line = &self.input[self.start..]; let line = &self.input[self.start..];
self.start = self.input.len(); self.start = self.input.len();
Some(line.trim_end_matches("\n")) Some(dbg!(line.trim_end_matches("\n")))
} }
_ => None, _ => None,
} }

View file

@ -19,6 +19,7 @@ pub struct RawEvent<'a> {
impl<'a> RawEvent<'a> { impl<'a> RawEvent<'a> {
pub fn parse(line: &'a str) -> Result<Self> { pub fn parse(line: &'a str) -> Result<Self> {
debug_assert!(!line.ends_with("\n"));
event_parser(line) event_parser(line)
} }
} }