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

View file

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

View file

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

View file

@ -1,93 +1,43 @@
use crate::event::{param_parse, param_parse_with, quoted, u_int, ParamIter};
use crate::raw_event::{against_subject_parser, RawSubject};
use nom::combinator::opt;
use nom::number::complete::float;
use nom::IResult;
use crate::event::{param_parse_with, parse_field, quoted, ParamIter};
use crate::raw_event::RawSubject;
use crate::{Event, IResult};
#[derive(Debug)]
#[derive(Debug, Event)]
pub struct HealedEvent<'a> {
#[event(name = "against")]
pub target: RawSubject<'a>,
#[event(name = "healing")]
#[event(quoted)]
pub amount: u32,
}
pub fn healed_event_parser(input: &str) -> IResult<&str, HealedEvent> {
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)]
#[derive(Debug, Event)]
pub struct ChargeDeployedEvent<'a> {
pub medigun: Option<&'a str>,
}
pub fn charge_deployed_event_parser(input: &str) -> IResult<&str, ChargeDeployedEvent> {
let (input, medigun) = opt(param_parse("healing"))(input)?;
Ok((input, ChargeDeployedEvent { medigun }))
}
#[derive(Debug)]
#[derive(Debug, Event)]
pub struct ChargeEndedEvent {
pub duration: Option<f32>,
}
pub fn charge_ended_event_parser(input: &str) -> IResult<&str, ChargeEndedEvent> {
let (input, duration) = opt(param_parse_with("duration", quoted(float)))(input)?;
Ok((input, ChargeEndedEvent { duration }))
}
#[derive(Debug)]
#[derive(Debug, Event)]
pub struct AdvantageLostEvent {
pub time: Option<f32>,
}
pub fn advantage_lost_event_parser(input: &str) -> IResult<&str, AdvantageLostEvent> {
let (input, time) = opt(param_parse_with("time", quoted(float)))(input)?;
Ok((input, AdvantageLostEvent { time }))
}
#[derive(Debug)]
#[derive(Debug, Event)]
pub struct FirstHealEvent {
pub time: Option<f32>,
}
pub fn first_heal_event_parser(input: &str) -> IResult<&str, FirstHealEvent> {
let (input, time) = opt(param_parse_with("time", quoted(float)))(input)?;
Ok((input, FirstHealEvent { time }))
}
#[derive(Debug)]
#[derive(Debug, Event)]
pub struct MedicDeathEvent {
#[event(name = "ubercharge")]
pub charge: Option<u32>,
}
pub fn medic_death_event_parser(input: &str) -> IResult<&str, MedicDeathEvent> {
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)]
#[derive(Debug, Event)]
pub struct MedicDeathExEvent {
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;
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 medic::*;
use nom::bytes::complete::{tag, take_while};
use nom::character::complete::{alpha1, digit1};
use nom::combinator::opt;
use nom::error::{ErrorKind, ParseError};
use nom::{Err, IResult};
use nom::number::complete::float;
use nom::Err;
pub use player::*;
use std::net::SocketAddr;
use std::num::NonZeroU32;
use std::str::FromStr;
use thiserror::Error;
@ -30,7 +34,7 @@ trait GameEventErrTrait<T> {
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> {
self.map_err(|err| match err {
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)]
pub struct EventMeta {
pub time: u32,
@ -104,116 +116,100 @@ impl<'a> GameEvent<'a> {
pub fn parse(raw: &RawEvent<'a>) -> Result<GameEvent<'a>, GameEventError> {
Ok(match raw.ty {
RawEventType::ShotFired => {
GameEvent::ShotFired(shot_fired_event_parser(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)?)
GameEvent::ShotFired(parse_event(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 => {
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::Say => GameEvent::Say(raw.params.trim_matches('"')),
RawEventType::Healed => {
GameEvent::Healed(healed_event_parser(raw.params).with_type(raw.ty)?)
RawEventType::Healed => GameEvent::Healed(parse_event(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 => {
GameEvent::ChargeEnded(charge_ended_event_parser(raw.params).with_type(raw.ty)?)
GameEvent::ChargeEnded(parse_event(raw.params).with_type(raw.ty)?)
}
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 => {
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::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 => {
GameEvent::MedicDeathEx(medic_death_ex_event_parser(raw.params).with_type(raw.ty)?)
}
RawEventType::Spawned => {
GameEvent::Spawned(spawn_event_parser(raw.params).with_type(raw.ty)?)
GameEvent::MedicDeathEx(parse_event(raw.params).with_type(raw.ty)?)
}
RawEventType::Spawned => GameEvent::Spawned(parse_event(raw.params).with_type(raw.ty)?),
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::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 => {
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::LogFileStarted => GameEvent::LogFileStarted(
log_file_started_event_parser(raw.params).with_type(raw.ty)?,
),
RawEventType::LogFileStarted => {
GameEvent::LogFileStarted(parse_event(raw.params).with_type(raw.ty)?)
}
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 => {
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::Entered => GameEvent::Entered,
RawEventType::Joined => {
GameEvent::Joined(joined_team_event_parser(raw.params).with_type(raw.ty)?)
}
RawEventType::Suicide => {
GameEvent::Suicide(committed_suicide_event_parser(raw.params).with_type(raw.ty)?)
}
RawEventType::Joined => GameEvent::Joined(parse_event(raw.params).with_type(raw.ty)?),
RawEventType::Suicide => GameEvent::Suicide(parse_event(raw.params).with_type(raw.ty)?),
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 => {
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::Revenge => {
GameEvent::Revenge(revenge_event_parser(raw.params).with_type(raw.ty)?)
RawEventType::Revenge => GameEvent::Revenge(parse_event(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 => {
GameEvent::PointCaptured(point_captures_event_parser(raw.params).with_type(raw.ty)?)
GameEvent::PointCaptured(parse_event(raw.params).with_type(raw.ty)?)
}
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 => {
GameEvent::BuiltObject(built_object_event_parser(raw.params).with_type(raw.ty)?)
GameEvent::BuiltObject(parse_event(raw.params).with_type(raw.ty)?)
}
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 => {
GameEvent::Extinguished(extinguished_event_parser(raw.params).with_type(raw.ty)?)
GameEvent::Extinguished(parse_event(raw.params).with_type(raw.ty)?)
}
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 => {
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::ObjectDetonated => GameEvent::ObjectDetonated(
object_detonated_event_parser(raw.params).with_type(raw.ty)?,
),
RawEventType::ObjectDetonated => {
GameEvent::ObjectDetonated(parse_event(raw.params).with_type(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,
}
@ -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, key) = alpha1(input)?;
@ -255,9 +251,9 @@ fn param_pair_parse(input: &str) -> IResult<&str, (&str, &str)> {
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,
) -> impl Fn(&'a str) -> IResult<&'a str, T> {
) -> impl Fn(&'a str) -> IResult<'a, T> {
move |input| {
let (input, _) = tag(r#"""#)(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> {
param_parse_with(key, quoted(take_while(|c| c != '"')))
pub fn param_parse<'a, T: EventField<'a>>(key: &'a str) -> impl Fn(&'a str) -> IResult<'a, T> {
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,
parser: P,
) -> impl Fn(&'a str) -> IResult<&'a str, T> {
) -> impl Fn(&'a str) -> IResult<'a, T> {
move |input: &str| {
debug_assert!(input.as_bytes()[0] != b' ');
let has_open = input.as_bytes()[0] == b'(';
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)
.map(|res| ("", res))
.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, raw) = digit1(input)?;
let val: i32 = raw
@ -311,15 +308,23 @@ fn int(input: &str) -> IResult<&str, i32> {
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 val = raw
.parse()
.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))
}
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, _) = tag(" ")(input)?;
let (input, y) = int(input)?;
@ -327,3 +332,84 @@ fn position(input: &str) -> IResult<&str, (i32, i32, i32)> {
let (input, z) = int(input)?;
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::event::{param_parse, param_parse_with, parse_from_str, position, u_int, ParamIter};
use crate::raw_event::{against_subject_parser, RawSubject};
use nom::combinator::opt;
use nom::IResult;
use crate::event::{param_parse_with, parse_field, ParamIter};
use crate::raw_event::RawSubject;
use crate::{Event, IResult};
use std::net::SocketAddr;
use std::num::NonZeroU32;
#[derive(Debug)]
#[derive(Debug, Event)]
pub struct ShotFiredEvent<'a> {
pub weapon: Option<&'a str>,
}
pub fn shot_fired_event_parser(input: &str) -> IResult<&str, ShotFiredEvent> {
let (input, weapon) = opt(param_parse("weapon"))(input)?;
Ok((input, ShotFiredEvent { weapon }))
}
#[derive(Debug)]
#[derive(Debug, Event)]
pub struct ShotHitEvent<'a> {
pub weapon: Option<&'a str>,
}
pub fn shot_hit_event_parser(input: &str) -> IResult<&str, ShotHitEvent> {
let (input, weapon) = opt(param_parse("weapon"))(input)?;
Ok((input, ShotHitEvent { weapon }))
}
#[derive(Debug)]
#[derive(Debug, 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>,
}
pub fn damage_event_parser(input: &str) -> IResult<&str, DamageEvent> {
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)]
#[derive(Debug, Event)]
pub struct KillEvent<'a> {
#[event(unnamed)]
#[event(skip_after = 1)]
pub target: RawSubject<'a>,
#[event(name = "with")]
pub weapon: &'a str,
pub attacker_position: Option<(i32, i32, i32)>,
pub victim_position: Option<(i32, i32, i32)>,
}
pub fn kill_event_parser(input: &str) -> IResult<&str, KillEvent> {
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)]
#[derive(Debug, Event)]
pub struct KillAssistEvent<'a> {
pub target: RawSubject<'a>,
pub attacker_position: Option<(i32, i32, i32)>,
pub victim_position: Option<(i32, i32, i32)>,
}
pub fn kill_assist_event_parser(input: &str) -> IResult<&str, KillAssistEvent> {
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)]
#[derive(Debug, Event)]
pub struct SpawnEvent {
#[event(name = "as")]
pub class: Option<Class>,
}
pub fn spawn_event_parser(input: &str) -> IResult<&str, SpawnEvent> {
let (input, class_str) = param_parse("as")(input)?;
Ok((
input,
SpawnEvent {
class: class_str.parse().ok(),
},
))
}
#[derive(Debug)]
#[derive(Debug, Event)]
pub struct RoleChangeEvent {
#[event(name = "to")]
pub class: Option<Class>,
}
pub fn role_changed_event_parser(input: &str) -> IResult<&str, RoleChangeEvent> {
let (input, class_str) = param_parse("to")(input)?;
Ok((
input,
RoleChangeEvent {
class: class_str.parse().ok(),
},
))
}
#[derive(Debug)]
#[derive(Debug, Event)]
pub struct ConnectedEvent {
#[event(name = "to")]
pub address: SocketAddr,
}
pub fn connected_event_parser(input: &str) -> IResult<&str, ConnectedEvent> {
let (input, address) = param_parse_with("to", parse_from_str)(input)?;
Ok((input, ConnectedEvent { address }))
}
#[derive(Debug)]
#[derive(Debug, Event)]
pub struct JoinedTeamEvent {
pub team: Team,
}
pub fn joined_team_event_parser(input: &str) -> IResult<&str, JoinedTeamEvent> {
let (input, team) = param_parse_with("team", parse_from_str)(input)?;
Ok((input, JoinedTeamEvent { team }))
}
#[derive(Debug)]
#[derive(Debug, Event)]
pub struct CommittedSuicideEvent<'a> {
#[event(name = "with")]
pub weapon: &'a str,
pub attacker_position: Option<(i32, i32, i32)>,
}
pub fn committed_suicide_event_parser(input: &str) -> IResult<&str, CommittedSuicideEvent> {
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)]
#[derive(Debug, Event)]
pub struct PickedUpEvent<'a> {
pub item: &'a str,
}
pub fn picked_up_event_parser(input: &str) -> IResult<&str, PickedUpEvent> {
let (input, item) = param_parse("item")(input)?;
Ok((input, PickedUpEvent { item }))
}
#[derive(Debug)]
#[derive(Debug, Event)]
pub struct DominationEvent<'a> {
pub against: RawSubject<'a>,
}
pub fn domination_event_parser(input: &str) -> IResult<&str, DominationEvent> {
let (input, against) = param_parse_with("against", against_subject_parser)(input)?;
Ok((input, DominationEvent { against }))
}
#[derive(Debug)]
#[derive(Debug, Event)]
pub struct RevengeEvent<'a> {
pub against: RawSubject<'a>,
}
pub fn revenge_event_parser(input: &str) -> IResult<&str, RevengeEvent> {
let (input, against) = param_parse_with("against", against_subject_parser)(input)?;
Ok((input, RevengeEvent { against }))
}
#[derive(Debug)]
#[derive(Debug, Event)]
pub struct DisconnectEvent<'a> {
pub reason: Option<&'a str>,
}
pub fn disconnected_event_parser(input: &str) -> IResult<&str, DisconnectEvent> {
let (input, reason) = opt(param_parse("reason"))(input)?;
Ok((input, DisconnectEvent { reason }))
}
#[derive(Debug)]
#[derive(Debug, Event)]
pub struct BuiltObjectEvent<'a> {
pub object: Option<&'a str>,
pub position: Option<(i32, i32, i32)>,
}
pub fn built_object_event_parser(input: &str) -> IResult<&str, BuiltObjectEvent> {
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)]
#[derive(Debug, Event)]
pub struct KilledObjectEvent<'a> {
pub object: Option<&'a str>,
pub weapon: Option<&'a str>,
#[event(name = "objectowner")]
pub object_owner: Option<RawSubject<'a>>,
pub attacker_position: Option<(i32, i32, i32)>,
}
pub fn killed_object_event_parser(input: &str) -> IResult<&str, KilledObjectEvent> {
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)]
#[derive(Debug, Event)]
pub struct ObjectDetonatedEvent<'a> {
pub object: Option<&'a str>,
#[event(name = "attacker_position")]
pub position: Option<(i32, i32, i32)>,
}
pub fn object_detonated_event_parser(input: &str) -> IResult<&str, ObjectDetonatedEvent> {
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)]
#[derive(Debug, Event)]
pub struct ExtinguishedEvent<'a> {
pub against: RawSubject<'a>,
pub with: &'a str,
pub attacker_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};
use crate::event::GameEventError;
pub use crate::module::EventHandler;
@ -8,13 +6,14 @@ use crate::module::{
};
pub use crate::subjectmap::SubjectMap;
use chrono::NaiveDateTime;
pub use event::{EventMeta, GameEvent};
pub use event::{Event, EventMeta, GameEvent};
use memchr::memmem::{find_iter, FindIter};
use nom::Err;
pub use raw_event::{RawEvent, RawEventType};
use std::collections::BTreeMap;
use std::convert::TryInto;
use std::fmt::Debug;
pub use tf_log_parser_derive::Event;
use thiserror::Error;
mod common;
@ -53,6 +52,9 @@ impl From<nom::error::Error<&'_ str>> for Error {
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(
log: &str,
) -> Result<
@ -74,9 +76,7 @@ pub fn parse_with_handler<Handler: EventHandler>(
),
Error,
> {
let events = LineSplit::new(log)
.filter(|line| !line.is_empty())
.map(RawEvent::parse);
let events = LineSplit::new(log).map(RawEvent::parse);
let mut handler = Handler::default();
@ -160,7 +160,7 @@ impl<'a> Iterator for LineSplit<'a> {
None if self.start < self.input.len() => {
let line = &self.input[self.start..];
self.start = self.input.len();
Some(line.trim_end_matches("\n"))
Some(dbg!(line.trim_end_matches("\n")))
}
_ => None,
}

View file

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