quote in name handling somewhat

This commit is contained in:
Robin Appelman 2023-03-16 20:45:07 +01:00
commit de2e940974
5 changed files with 81 additions and 9 deletions

View file

@ -21,6 +21,8 @@ memchr = "2.5.0"
ahash = "0.8.3"
tf-log-parser-derive = { version = "0.1", path = "./derive" }
miette = "5.5.0"
walkdir = "2.3.2"
once_cell = "1.17.1"
[dev-dependencies]
criterion = "0.4"

28
examples/dir.rs Normal file
View file

@ -0,0 +1,28 @@
use main_error::MainError;
use std::env::args;
use std::ffi::OsStr;
use std::fs;
use tf_log_parser::parse;
use walkdir::WalkDir;
fn main() -> Result<(), MainError> {
let path = args().nth(1).expect("No path provided");
for entry in WalkDir::new(path) {
let entry = entry?;
let path = entry.path();
if path.extension() == Some(OsStr::new("log")) {
print!("{} - ", path.display());
let input = match fs::read_to_string(path) {
Ok(input) => input,
Err(e) => {
println!("failed to read file: {}", e);
continue;
}
};
let (output, _) = parse(&input)?;
println!("{} messages", output.chat.len());
}
}
Ok(())
}

View file

@ -3,7 +3,7 @@ mod medic;
mod player;
use crate::event::game::{RoundLengthEvent, RoundWinEvent};
use crate::parsing::{skip, skip_matches, split_once};
use crate::parsing::{skip, skip_matches, split_once, split_subject_end};
use crate::raw_event::{against_subject_parser, RawSubject};
use crate::{Error, Events, IResult, RawEvent, RawEventType, Result, SubjectId};
pub use game::*;
@ -15,7 +15,7 @@ use std::str::FromStr;
#[derive(thiserror::Error, Debug)]
pub enum GameEventError {
#[error("malformed game event({ty:?}): {err}")]
#[error("malformed game event({ty:?}): {err} in \"{params}\"")]
Error {
err: Box<Error>,
ty: RawEventType,
@ -167,7 +167,12 @@ pub fn param_parse_with<'a, T, P: Fn(&'a str) -> Result<T>>(
let input = skip(input, key.len() + 2)?; // skip space + key + quote
let (value, input) = split_once(input, b'"', 1)?;
// hack to handle quotes in names
let (value, input) = if key == "against" || key == "objectowner" {
split_subject_end(input, 1)?
} else {
split_once(input, b'"', 1)?
};
let value = parser(value)?;

View file

@ -1,5 +1,7 @@
use crate::{Error, Result};
use memchr::memmem::Finder;
use memchr::{memchr, memrchr};
use once_cell::unsync::Lazy;
pub fn split_once(input: &str, delim: u8, offset: usize) -> Result<(&str, &str)> {
debug_assert!(delim < 128); // only basic ascii
@ -13,6 +15,25 @@ pub fn split_once(input: &str, delim: u8, offset: usize) -> Result<(&str, &str)>
})
}
thread_local! {
static SUBJECT_END_FINDER: Lazy<Finder<'static>> = Lazy::new(|| Finder::new(br#">""#));
}
pub fn split_subject_end<'a>(input: &'a str, offset: usize) -> Result<(&'a str, &'a str)> {
let start_offset = 1;
let end_offset = start_offset + offset;
let end = SUBJECT_END_FINDER
.with(|finder| finder.find(input.as_bytes()))
.ok_or(Error::Incomplete)?;
// safety, memchr returns indices that are inside the input length and we only split on ascii
Ok(unsafe {
(
input.get_unchecked(..end + start_offset),
input.get_unchecked(end + end_offset..),
)
})
}
pub fn take_until(input: &str, delim: u8) -> (&str, &str) {
debug_assert!(delim < 128); // only basic ascii
if let Some(end) = memchr(delim, input.as_bytes()) {

View file

@ -1,5 +1,5 @@
use crate::common::Team;
use crate::parsing::split_once;
use crate::parsing::{split_once, split_subject_end};
use crate::{Error, Result};
use crate::{SubjectError, SubjectId};
use chrono::{NaiveDate, NaiveDateTime};
@ -120,7 +120,7 @@ fn test_split_player_subject() {
pub fn against_subject_parser(input: &str) -> Result<RawSubject> {
// "against" fields are always players, and unquoted
if input.ends_with("e>") {
if input.ends_with("le>") {
Ok(RawSubject::Console)
} else {
Ok(RawSubject::Player(input))
@ -128,9 +128,12 @@ pub fn against_subject_parser(input: &str) -> Result<RawSubject> {
}
pub fn subject_parser(input: &str) -> Result<(&str, RawSubject)> {
let full = input;
if let Some(input) = input.strip_prefix('"') {
let (player, input) = split_once(input, b'"', 1)?;
if player.ends_with("e>") {
let Ok((player, input)) = split_subject_end(input, 1) else {
return Ok((full, RawSubject::Console))
};
if player.ends_with("le>") {
Ok((input, RawSubject::Console))
} else {
Ok((input, RawSubject::Player(player)))
@ -151,6 +154,17 @@ pub fn subject_parser(input: &str) -> Result<(&str, RawSubject)> {
}
}
#[test]
fn test_subject_parser() {
assert_eq!(
(
"connected",
RawSubject::Player(r#"Buddie :")<25><[U:1:123]><>"#)
),
subject_parser(r#""Buddie :")<25><[U:1:123]><>" connected"#).unwrap()
);
}
#[derive(Copy, Clone, Debug, PartialEq, Logos)]
pub enum RawEventType {
#[token(r#"joined"#)]
@ -194,9 +208,9 @@ pub enum RawEventType {
#[token(r#"triggered "killedobject""#)]
KilledObject,
#[token(r#"triggered "object_detonated""#)]
Extinguished,
#[token(r#"triggered "player_extinguished""#)]
ObjectDetonated,
#[token(r#"triggered "player_extinguished""#)]
Extinguished,
#[token(r#"picked up"#)]
PickedUp,
#[token(r#"triggered "medic_death""#)]
@ -269,6 +283,8 @@ pub enum RawEventType {
TournamentModeStarted,
#[token(r#"triggered "flagevent""#)]
FlagEvent,
#[token(r#"cvars"#)]
CVars,
#[error]
Unknown,
}