mirror of
https://codeberg.org/icewind/tf-log-parser.git
synced 2026-06-03 18:24:09 +02:00
remove some more nom
This commit is contained in:
parent
d326e1bb6a
commit
311345cf4e
15 changed files with 103 additions and 91 deletions
|
|
@ -4,7 +4,7 @@ use tf_log_parser::{parse, RawEvent};
|
|||
static LOG: &str = include_str!("../test_data/log_2892242.log");
|
||||
|
||||
pub fn parse_benchmark() {
|
||||
black_box(parse(black_box(&LOG))).ok();
|
||||
black_box(parse(black_box(LOG))).ok();
|
||||
}
|
||||
|
||||
pub fn parse_raw() {
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ handler!(Handler {
|
|||
});
|
||||
|
||||
fn main() -> Result<(), MainError> {
|
||||
let path = args().skip(1).next().expect("No path provided");
|
||||
let path = args().nth(1).expect("No path provided");
|
||||
let content = fs::read_to_string(path)?;
|
||||
|
||||
let (
|
||||
|
|
|
|||
|
|
@ -57,7 +57,7 @@ impl GlobalData for HighestDamageHandler {
|
|||
}
|
||||
|
||||
fn main() -> Result<(), MainError> {
|
||||
let path = args().skip(1).next().expect("No path provided");
|
||||
let path = args().nth(1).expect("No path provided");
|
||||
let content = fs::read_to_string(path)?;
|
||||
|
||||
let HighestDamage { user, damage } = parse_with_handler::<HighestDamageHandler>(&content)?
|
||||
|
|
|
|||
|
|
@ -263,7 +263,7 @@ impl TryFrom<&RawSubject<'_>> for SubjectData {
|
|||
fn try_from(raw: &RawSubject<'_>) -> Result<Self, Self::Error> {
|
||||
Ok(match raw {
|
||||
RawSubject::Player(raw) => {
|
||||
let (_, (name, user_id, steam_id, team)) =
|
||||
let (name, user_id, steam_id, team) =
|
||||
split_player_subject(raw).map_err(|_| SubjectError::InvalidUserId)?;
|
||||
SubjectData::Player {
|
||||
name: name.to_string(),
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
use crate::event::{param_parse, param_parse_with, position, u_int, ParamIter};
|
||||
use crate::raw_event::{subject_parser, RawSubject};
|
||||
use crate::raw_event::{against_subject_parser, RawSubject};
|
||||
|
||||
use nom::bytes::complete::{tag, take_while};
|
||||
use nom::combinator::opt;
|
||||
use nom::number::complete::float;
|
||||
|
|
@ -103,7 +104,7 @@ pub fn point_captures_event_parser(input: &str) -> IResult<&str, PointCapturedEv
|
|||
(Some((subject_key, subject)), Some((position_key, position_str)))
|
||||
if subject_key.starts_with("player") && position_key.starts_with("position") =>
|
||||
{
|
||||
let (_, subject) = subject_parser(subject)?;
|
||||
let (_, subject) = against_subject_parser(subject)?;
|
||||
let (_, position) = position(position_str)?;
|
||||
players.push((subject, position));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
use crate::event::{param_parse, param_parse_with, quoted, u_int, ParamIter};
|
||||
use crate::raw_event::{subject_parser, RawSubject};
|
||||
use crate::raw_event::{against_subject_parser, RawSubject};
|
||||
use nom::combinator::opt;
|
||||
use nom::number::complete::float;
|
||||
use nom::IResult;
|
||||
|
|
@ -11,7 +11,7 @@ pub struct HealedEvent<'a> {
|
|||
}
|
||||
|
||||
pub fn healed_event_parser(input: &str) -> IResult<&str, HealedEvent> {
|
||||
let (input, subject) = param_parse_with("against", subject_parser)(input)?;
|
||||
let (input, subject) = param_parse_with("against", against_subject_parser)(input)?;
|
||||
let (input, amount) = param_parse_with("healing", quoted(u_int))(input)?;
|
||||
Ok((
|
||||
input,
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
use crate::common::{Class, Team};
|
||||
use crate::event::{param_parse, param_parse_with, parse_from_str, position, u_int, ParamIter};
|
||||
use crate::raw_event::{subject_parser, RawSubject};
|
||||
use crate::raw_event::{against_subject_parser, RawSubject};
|
||||
use nom::combinator::opt;
|
||||
use nom::IResult;
|
||||
use std::net::SocketAddr;
|
||||
|
|
@ -35,7 +35,7 @@ pub struct DamageEvent<'a> {
|
|||
}
|
||||
|
||||
pub fn damage_event_parser(input: &str) -> IResult<&str, DamageEvent> {
|
||||
let (input, target) = param_parse_with("against", subject_parser)(input)?;
|
||||
let (input, target) = param_parse_with("against", against_subject_parser)(input)?;
|
||||
let mut event = DamageEvent {
|
||||
target,
|
||||
damage: None,
|
||||
|
|
@ -62,7 +62,7 @@ pub struct KillEvent<'a> {
|
|||
}
|
||||
|
||||
pub fn kill_event_parser(input: &str) -> IResult<&str, KillEvent> {
|
||||
let (input, target) = subject_parser(input)?;
|
||||
let (input, target) = against_subject_parser(input)?;
|
||||
let (input, weapon) = param_parse("with")(input)?;
|
||||
let mut event = KillEvent {
|
||||
target,
|
||||
|
|
@ -88,7 +88,7 @@ pub struct KillAssistEvent<'a> {
|
|||
}
|
||||
|
||||
pub fn kill_assist_event_parser(input: &str) -> IResult<&str, KillAssistEvent> {
|
||||
let (input, target) = param_parse_with("against", subject_parser)(input)?;
|
||||
let (input, target) = param_parse_with("against", against_subject_parser)(input)?;
|
||||
let mut event = KillAssistEvent {
|
||||
target,
|
||||
attacker_position: None,
|
||||
|
|
@ -188,7 +188,7 @@ pub struct DominationEvent<'a> {
|
|||
}
|
||||
|
||||
pub fn domination_event_parser(input: &str) -> IResult<&str, DominationEvent> {
|
||||
let (input, against) = param_parse_with("against", subject_parser)(input)?;
|
||||
let (input, against) = param_parse_with("against", against_subject_parser)(input)?;
|
||||
Ok((input, DominationEvent { against }))
|
||||
}
|
||||
|
||||
|
|
@ -198,7 +198,7 @@ pub struct RevengeEvent<'a> {
|
|||
}
|
||||
|
||||
pub fn revenge_event_parser(input: &str) -> IResult<&str, RevengeEvent> {
|
||||
let (input, against) = param_parse_with("against", subject_parser)(input)?;
|
||||
let (input, against) = param_parse_with("against", against_subject_parser)(input)?;
|
||||
Ok((input, RevengeEvent { against }))
|
||||
}
|
||||
|
||||
|
|
@ -235,7 +235,8 @@ pub struct KilledObjectEvent<'a> {
|
|||
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", subject_parser))(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,
|
||||
|
|
@ -257,7 +258,7 @@ pub struct ExtinguishedEvent<'a> {
|
|||
}
|
||||
|
||||
pub fn extinguished_event_parser(input: &str) -> IResult<&str, ExtinguishedEvent> {
|
||||
let (input, against) = param_parse_with("against", subject_parser)(input)?;
|
||||
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)?;
|
||||
|
|
|
|||
29
src/lib.rs
29
src/lib.rs
|
|
@ -1,10 +1,13 @@
|
|||
pub use crate::common::{SteamId3, SubjectData, SubjectError, SubjectId};
|
||||
use crate::event::GameEventError;
|
||||
pub use crate::module::EventHandler;
|
||||
use crate::module::{ChatMessages, ClassStatsHandler, HealSpread, PlayerHandler};
|
||||
use crate::module::{
|
||||
ChatMessages, ClassStatsHandler, HealSpread, MedicStatsBuilder, PlayerHandler,
|
||||
};
|
||||
pub use crate::subjectmap::SubjectMap;
|
||||
use chrono::NaiveDateTime;
|
||||
pub use event::{EventMeta, GameEvent};
|
||||
use nom::Err;
|
||||
pub use raw_event::{RawEvent, RawEventType};
|
||||
use std::collections::BTreeMap;
|
||||
use std::convert::TryInto;
|
||||
|
|
@ -20,8 +23,8 @@ mod subjectmap;
|
|||
|
||||
#[derive(Error, Debug)]
|
||||
pub enum Error {
|
||||
#[error("Malformed logfile: {0}")]
|
||||
Malformed(nom::error::Error<String>),
|
||||
#[error("Malformed logfile")]
|
||||
Malformed,
|
||||
#[error("Incomplete logfile")]
|
||||
Incomplete,
|
||||
#[error("Malformed subject: {0}")]
|
||||
|
|
@ -29,16 +32,24 @@ pub enum Error {
|
|||
#[error("{0}")]
|
||||
MalformedEvent(#[from] GameEventError),
|
||||
}
|
||||
impl From<nom::Err<nom::error::Error<&'_ str>>> for Error {
|
||||
fn from(e: nom::Err<nom::error::Error<&'_ str>>) -> Self {
|
||||
match e {
|
||||
Err::Incomplete(_) => Error::Incomplete,
|
||||
Err::Error(_) => Error::Malformed,
|
||||
Err::Failure(_) => Error::Malformed,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<nom::error::Error<&'_ str>> for Error {
|
||||
fn from(e: nom::error::Error<&str>) -> Self {
|
||||
Error::Malformed(nom::error::Error {
|
||||
input: e.input.to_string(),
|
||||
code: e.code,
|
||||
})
|
||||
fn from(_: nom::error::Error<&str>) -> Self {
|
||||
Error::Malformed
|
||||
}
|
||||
}
|
||||
|
||||
type Result<O, E = Error> = std::result::Result<O, E>;
|
||||
|
||||
pub fn parse(
|
||||
log: &str,
|
||||
) -> Result<
|
||||
|
|
@ -113,6 +124,6 @@ pub fn parse_with_handler<Handler: EventHandler>(
|
|||
handler!(LogHandler {
|
||||
chat: ChatMessages,
|
||||
heal_spread: PlayerHandler::<HealSpread>,
|
||||
// medic_stats: MedicStatsHandler,
|
||||
medic_stats: PlayerHandler::<MedicStatsBuilder>,
|
||||
class_stats: ClassStatsHandler,
|
||||
});
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ use std::io::stdout;
|
|||
use tf_log_parser::parse;
|
||||
|
||||
fn main() -> Result<(), MainError> {
|
||||
let path = args().skip(1).next().expect("No path provided");
|
||||
let path = args().nth(1).expect("No path provided");
|
||||
let content = fs::read_to_string(path)?;
|
||||
|
||||
let log = parse(&content)?;
|
||||
|
|
|
|||
|
|
@ -25,7 +25,7 @@ pub struct ChatMessage {
|
|||
impl ChatMessage {
|
||||
fn from_bare(bare: BareChatMessage, subjects: &SubjectMap) -> Self {
|
||||
let (name, steam_id) = match &subjects[bare.subject] {
|
||||
(SubjectData::Player { name, steam_id, .. }, _) => (name.clone(), steam_id.clone()),
|
||||
(SubjectData::Player { name, steam_id, .. }, _) => (name.clone(), *steam_id),
|
||||
_ => {
|
||||
unreachable!("only player messages are added");
|
||||
}
|
||||
|
|
|
|||
|
|
@ -96,7 +96,7 @@ impl EventHandler for ClassStatsHandler {
|
|||
}
|
||||
|
||||
fn finish_global(self, _subjects: &SubjectMap) -> Self::GlobalOutput {
|
||||
()
|
||||
|
||||
}
|
||||
|
||||
fn finish_per_subject(
|
||||
|
|
|
|||
|
|
@ -57,7 +57,7 @@ impl FromStr for LobbyLeader {
|
|||
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
if let Some((name, steam_id)) = s.rsplit_once(" (") {
|
||||
if let Ok(steam_id) = steam_id.trim_end_matches(")").parse::<u64>() {
|
||||
if let Ok(steam_id) = steam_id.trim_end_matches(')').parse::<u64>() {
|
||||
Ok(LobbyLeader {
|
||||
name: name.into(),
|
||||
steam_id: steam_id.into(),
|
||||
|
|
@ -90,8 +90,14 @@ pub struct Settings {
|
|||
|
||||
impl Default for Settings {
|
||||
fn default() -> Self {
|
||||
Self::with_id(0)
|
||||
}
|
||||
}
|
||||
|
||||
impl Settings {
|
||||
pub fn with_id(id: u32) -> Self {
|
||||
Settings {
|
||||
id: 0,
|
||||
id,
|
||||
leader: LobbyLeader::default(),
|
||||
map: "".to_string(),
|
||||
game_type: GameType::Sixes,
|
||||
|
|
@ -157,8 +163,7 @@ impl LobbySettingsHandler {
|
|||
.strip_prefix("TF2Center Lobby #")
|
||||
.and_then(|s| str::split_once(s, " |"))
|
||||
{
|
||||
let mut settings = Settings::default();
|
||||
settings.id = id.parse()?;
|
||||
let settings = Settings::with_id(id.parse()?);
|
||||
*self = LobbySettingsHandler::Active(settings);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -104,7 +104,7 @@ impl PlayerSpecificData for MedicStatsBuilder {
|
|||
}
|
||||
GameEvent::MedicDeath(death) => {
|
||||
let charge = death.charge.unwrap_or_default();
|
||||
if charge >= 95 && charge < 100 {
|
||||
if (95..100).contains(&charge) {
|
||||
self.near_full_charge_death += 1;
|
||||
} else if charge >= 100 {
|
||||
self.drops += 1;
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ pub use healspread::HealSpread;
|
|||
pub use lobbysettings::{
|
||||
LobbySettingsError, LobbySettingsHandler, Location, Settings as LobbySettings,
|
||||
};
|
||||
pub use medicstats::MedicStats;
|
||||
pub use medicstats::{MedicStats, MedicStatsBuilder};
|
||||
use serde::Serialize;
|
||||
use std::marker::PhantomData;
|
||||
|
||||
|
|
@ -108,11 +108,11 @@ macro_rules! handler {
|
|||
paste::paste! {
|
||||
#[derive(Default)]
|
||||
pub struct $name {
|
||||
$($child: $ty),*
|
||||
pub $($child: $ty),*
|
||||
}
|
||||
|
||||
pub struct [<$name GlobalOutput>] {
|
||||
$($child: <$ty as $crate::EventHandler>::GlobalOutput),*
|
||||
pub $($child: <$ty as $crate::EventHandler>::GlobalOutput),*
|
||||
}
|
||||
|
||||
impl serde::Serialize for [<$name GlobalOutput>] {
|
||||
|
|
@ -144,7 +144,7 @@ macro_rules! handler {
|
|||
}
|
||||
|
||||
pub struct [<$name PerSubjectOutput>] {
|
||||
$($child: <$ty as $crate::EventHandler>::PerSubjectOutput),*
|
||||
pub $($child: <$ty as $crate::EventHandler>::PerSubjectOutput),*
|
||||
}
|
||||
|
||||
impl serde::Serialize for [<$name PerSubjectOutput>] {
|
||||
|
|
@ -235,7 +235,7 @@ impl<T: GlobalData> EventHandler for T {
|
|||
_subject: &SubjectData,
|
||||
_data: Self::PerSubjectData,
|
||||
) -> Self::PerSubjectOutput {
|
||||
()
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -270,7 +270,7 @@ impl<T: PlayerSpecificData + Default> EventHandler for PlayerHandler<T> {
|
|||
}
|
||||
|
||||
fn finish_global(self, _subjects: &SubjectMap) -> Self::GlobalOutput {
|
||||
()
|
||||
|
||||
}
|
||||
|
||||
fn finish_per_subject(
|
||||
|
|
|
|||
|
|
@ -1,11 +1,9 @@
|
|||
use crate::common::Team;
|
||||
use crate::{Error, Result};
|
||||
use crate::{SubjectError, SubjectId};
|
||||
use chrono::{NaiveDate, NaiveDateTime};
|
||||
use logos::{Lexer, Logos};
|
||||
use nom::bytes::complete::{tag, take, take_while};
|
||||
use nom::character::complete::{digit1, one_of};
|
||||
use nom::combinator::opt;
|
||||
use nom::{Finish, IResult, Needed};
|
||||
use nom::{IResult, Needed};
|
||||
use std::convert::{TryFrom, TryInto};
|
||||
use std::num::ParseIntError;
|
||||
|
||||
|
|
@ -20,30 +18,24 @@ pub struct RawEvent<'a> {
|
|||
}
|
||||
|
||||
impl<'a> RawEvent<'a> {
|
||||
pub fn parse(line: &'a str) -> Result<Self, nom::error::Error<&'a str>> {
|
||||
let (_, event) = event_parser(line).finish()?;
|
||||
Ok(event)
|
||||
pub fn parse(line: &'a str) -> Result<Self> {
|
||||
event_parser(line)
|
||||
}
|
||||
}
|
||||
|
||||
fn event_parser(input: &str) -> IResult<&str, RawEvent> {
|
||||
let (input, date) = date_parser(input)?;
|
||||
fn event_parser(input: &str) -> Result<RawEvent> {
|
||||
let date = RawDate(&input[0..21]);
|
||||
|
||||
let (input, _) = tag(": ")(input)?;
|
||||
let (input, subject) = subject_parser(input)?;
|
||||
let (input, subject) = subject_parser(&input[23..])?;
|
||||
|
||||
let (input, _) = opt(tag(" "))(input)?;
|
||||
let (input, ty) = event_type_parser(input)?;
|
||||
let (input, ty) = event_type_parser(input.trim_start())?;
|
||||
|
||||
Ok((
|
||||
input,
|
||||
RawEvent {
|
||||
Ok(RawEvent {
|
||||
date,
|
||||
subject,
|
||||
ty,
|
||||
params: input.trim(),
|
||||
},
|
||||
))
|
||||
})
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Clone, Copy)]
|
||||
|
|
@ -64,11 +56,6 @@ impl<'a> TryFrom<RawDate<'a>> for NaiveDateTime {
|
|||
}
|
||||
}
|
||||
|
||||
fn date_parser(input: &str) -> IResult<&str, RawDate> {
|
||||
let (input, raw) = take(21usize)(input)?;
|
||||
Ok((input, RawDate(raw)))
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_parse_date() {
|
||||
let raw = RawDate("08/06/2018 - 21:13:57");
|
||||
|
|
@ -93,29 +80,40 @@ impl<'a> RawSubject<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn split_player_subject(input: &str) -> IResult<&str, (&str, &str, &str, &str)> {
|
||||
let (input, name) = take_while(|c| c != '<')(input)?;
|
||||
pub fn split_player_subject(input: &str) -> Result<(&str, &str, &str, &str)> {
|
||||
let mut parts = input.splitn(4, '<');
|
||||
let (name, user_id, steam_id, team) =
|
||||
if let (Some(name), Some(user_id), Some(steam_id), Some(team)) =
|
||||
(parts.next(), parts.next(), parts.next(), parts.next())
|
||||
{
|
||||
(
|
||||
name,
|
||||
&user_id[0..user_id.len() - 1],
|
||||
&steam_id[0..steam_id.len() - 1],
|
||||
&team[0..team.len() - 1],
|
||||
)
|
||||
} else {
|
||||
return Err(Error::Incomplete);
|
||||
};
|
||||
|
||||
let (input, _) = one_of("<")(input)?;
|
||||
let (input, user_id) = digit1(input)?;
|
||||
let (input, _) = one_of(">")(input)?;
|
||||
|
||||
let (input, _) = one_of("<")(input)?;
|
||||
let (input, steam_id) = take_while(|c| c != '>')(input)?;
|
||||
let (input, _) = one_of(">")(input)?;
|
||||
|
||||
let (input, _) = one_of("<")(input)?;
|
||||
let (input, team) = take_while(|c| c != '>')(input)?;
|
||||
let (input, _) = one_of(">")(input)?;
|
||||
|
||||
Ok((input, (name, user_id, steam_id, team)))
|
||||
Ok((name, user_id, steam_id, team))
|
||||
}
|
||||
|
||||
pub fn subject_parser(input: &str) -> IResult<&str, RawSubject> {
|
||||
if input.starts_with('"') {
|
||||
let (player, input) = input[1..]
|
||||
.split_once('"')
|
||||
.ok_or_else(|| nom::Err::Incomplete(Needed::Unknown))?;
|
||||
#[test]
|
||||
fn test_split_player_subject() {
|
||||
assert_eq!(
|
||||
("Fin", "4", "[U:1:129852188]", "Blue"),
|
||||
split_player_subject("Fin<4><[U:1:129852188]><Blue>").unwrap()
|
||||
)
|
||||
}
|
||||
|
||||
pub fn against_subject_parser(input: &str) -> IResult<&str, RawSubject> {
|
||||
subject_parser(input).map_err(|_| nom::Err::Incomplete(Needed::Unknown))
|
||||
}
|
||||
|
||||
pub fn subject_parser(input: &str) -> Result<(&str, RawSubject)> {
|
||||
if let Some(input) = input.strip_prefix('"') {
|
||||
let (player, input) = input.split_once('"').ok_or(Error::Incomplete)?;
|
||||
if player.ends_with("e>") {
|
||||
Ok((input, RawSubject::Console))
|
||||
} else {
|
||||
|
|
@ -128,15 +126,11 @@ pub fn subject_parser(input: &str) -> IResult<&str, RawSubject> {
|
|||
} else if &input[6..7] == "b" {
|
||||
Ok((&input[11..], RawSubject::Team(Team::Blue)))
|
||||
} else {
|
||||
let (_, input) = input[7..]
|
||||
.split_once('"')
|
||||
.ok_or_else(|| nom::Err::Incomplete(Needed::Unknown))?;
|
||||
let (_, input) = input[7..].split_once('"').ok_or(Error::Incomplete)?;
|
||||
Ok((input, RawSubject::Team(Team::Spectator)))
|
||||
}
|
||||
} else {
|
||||
let (system, input) = input
|
||||
.split_once(' ')
|
||||
.ok_or_else(|| nom::Err::Incomplete(Needed::Unknown))?;
|
||||
let (system, input) = input.split_once(' ').ok_or(Error::Incomplete)?;
|
||||
Ok((input, RawSubject::System(system)))
|
||||
}
|
||||
}
|
||||
|
|
@ -263,7 +257,7 @@ pub enum RawEventType {
|
|||
Unknown,
|
||||
}
|
||||
|
||||
fn event_type_parser(input: &str) -> IResult<&str, RawEventType> {
|
||||
fn event_type_parser(input: &str) -> Result<(&str, RawEventType)> {
|
||||
let mut lexer = Lexer::new(input);
|
||||
let ty = lexer.next().unwrap_or(RawEventType::Unknown);
|
||||
Ok((lexer.remainder(), ty))
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue