mirror of
https://codeberg.org/icewind/tf-log-parser.git
synced 2026-06-03 18:24:09 +02:00
even less nom
This commit is contained in:
parent
41d558ecb0
commit
19f9c9f973
4 changed files with 74 additions and 56 deletions
|
|
@ -337,6 +337,16 @@ pub fn split_once(input: &str, delim: u8, offset: usize) -> Result<(&str, &str)>
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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()) {
|
||||||
|
// safety, memchr returns indices that are inside the input length and we only split on ascii
|
||||||
|
unsafe { (input.get_unchecked(end..), input.get_unchecked(..end)) }
|
||||||
|
} else {
|
||||||
|
("", input)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn skip(input: &str, count: usize) -> Result<&str> {
|
pub fn skip(input: &str, count: usize) -> Result<&str> {
|
||||||
input.get(count..).ok_or(Error::Incomplete)
|
input.get(count..).ok_or(Error::Incomplete)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,8 @@
|
||||||
use crate::event::{param_parse, param_parse_with, parse_field, ParamIter};
|
use crate::event::{param_parse_with, parse_field, ParamIter};
|
||||||
use crate::raw_event::RawSubject;
|
use crate::raw_event::RawSubject;
|
||||||
use crate::{Event, IResult};
|
use crate::{Error, Event, IResult};
|
||||||
|
|
||||||
use nom::bytes::complete::{tag, take_while};
|
use crate::common::{skip, take_until};
|
||||||
use nom::combinator::opt;
|
|
||||||
|
|
||||||
#[derive(Debug, Event)]
|
#[derive(Debug, Event)]
|
||||||
pub struct RoundWinEvent<'a> {
|
pub struct RoundWinEvent<'a> {
|
||||||
|
|
@ -32,10 +31,10 @@ pub struct TournamentModeStartedEvent<'a> {
|
||||||
|
|
||||||
impl<'a> Event<'a> for TournamentModeStartedEvent<'a> {
|
impl<'a> Event<'a> for TournamentModeStartedEvent<'a> {
|
||||||
fn parse(input: &'a str) -> IResult<Self> {
|
fn parse(input: &'a str) -> IResult<Self> {
|
||||||
let (input, _) = tag("\nBlue Team: ")(input)?;
|
let input = skip(input, "\nBlue Team: ".len())?;
|
||||||
let (input, blue) = take_while(|c| c != '\n')(input)?;
|
let (input, blue) = take_until(input, b'\n');
|
||||||
let (input, _) = tag("\nRed Team: ")(input)?;
|
let input = skip(input, "\nRed Team: ".len())?;
|
||||||
let (input, red) = take_while(|c| c != '\n')(input)?;
|
let (input, red) = take_until(input, b'\n');
|
||||||
Ok((input, TournamentModeStartedEvent { blue, red }))
|
Ok((input, TournamentModeStartedEvent { blue, red }))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -58,20 +57,41 @@ pub struct PointCapturedEvent<'a> {
|
||||||
|
|
||||||
impl<'a> Event<'a> for PointCapturedEvent<'a> {
|
impl<'a> Event<'a> for PointCapturedEvent<'a> {
|
||||||
fn parse(input: &'a str) -> IResult<Self> {
|
fn parse(input: &'a str) -> IResult<Self> {
|
||||||
let (input, cp) = opt(param_parse("cp"))(input)?;
|
let mut cp = Default::default();
|
||||||
let (input, cp_name) = opt(param_parse("cpname"))(input)?;
|
let mut cp_name = Default::default();
|
||||||
let (input, num_cappers) = opt(param_parse("numcappers"))(input)?;
|
let mut num_cappers = Default::default();
|
||||||
|
|
||||||
let mut players = Vec::new();
|
let mut players = Vec::new();
|
||||||
|
|
||||||
let mut params = ParamIter::new(input);
|
let mut params = ParamIter::new(input).peekable();
|
||||||
match (params.next(), params.next()) {
|
while let Some((name, value)) = params.peek() {
|
||||||
(Some((subject_key, subject)), Some((position_key, position_str)))
|
match *name {
|
||||||
if subject_key.starts_with("player") && position_key.starts_with("position") =>
|
"cp" => {
|
||||||
{
|
cp = Some(value.parse().map_err(Error::from)?);
|
||||||
players.push((parse_field(subject)?.1, parse_field(position_str)?.1));
|
let _ = params.next();
|
||||||
|
}
|
||||||
|
"cpname" => {
|
||||||
|
cp_name = Some(*value);
|
||||||
|
let _ = params.next();
|
||||||
|
}
|
||||||
|
"numcappers" => {
|
||||||
|
num_cappers = Some(value.parse().map_err(Error::from)?);
|
||||||
|
let _ = params.next();
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
loop {
|
||||||
|
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));
|
||||||
|
}
|
||||||
|
_ => break,
|
||||||
}
|
}
|
||||||
_ => {}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok((
|
Ok((
|
||||||
|
|
|
||||||
|
|
@ -2,15 +2,12 @@ mod game;
|
||||||
mod medic;
|
mod medic;
|
||||||
mod player;
|
mod player;
|
||||||
|
|
||||||
use crate::common::{skip, skip_matches, split_once};
|
use crate::common::{skip, skip_matches, split_once, take_until};
|
||||||
use crate::event::game::{RoundLengthEvent, RoundWinEvent};
|
use crate::event::game::{RoundLengthEvent, RoundWinEvent};
|
||||||
use crate::raw_event::{against_subject_parser, RawSubject};
|
use crate::raw_event::{against_subject_parser, RawSubject};
|
||||||
use crate::{err_incomplete, IResult, RawEvent, RawEventType, SubjectId};
|
use crate::{err_incomplete, 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::character::complete::digit1;
|
|
||||||
use nom::combinator::opt;
|
|
||||||
use nom::error::{ErrorKind, ParseError};
|
use nom::error::{ErrorKind, ParseError};
|
||||||
use nom::number::complete::float;
|
use nom::number::complete::float;
|
||||||
use nom::Err;
|
use nom::Err;
|
||||||
|
|
@ -253,9 +250,9 @@ fn quoted<'a, T, P: Fn(&'a str) -> IResult<'a, T>>(
|
||||||
parser: P,
|
parser: P,
|
||||||
) -> impl Fn(&'a str) -> IResult<'a, T> {
|
) -> impl Fn(&'a str) -> IResult<'a, T> {
|
||||||
move |input| {
|
move |input| {
|
||||||
let (input, _) = tag(r#"""#)(input)?;
|
let input = skip(input, 1)?;
|
||||||
let (input, res) = parser(input)?;
|
let (inner, input) = split_once(input, b'"', 1)?;
|
||||||
let (input, _) = tag(r#"""#)(input)?;
|
let (_, res) = parser(inner)?;
|
||||||
Ok((input, res))
|
Ok((input, res))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -297,45 +294,29 @@ fn parse_from_str<'a, T: FromStr + 'a>(input: &'a str) -> IResult<T> {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn int(input: &str) -> IResult<i32> {
|
fn int(input: &str) -> IResult<i32> {
|
||||||
let (input, sign) = opt(tag("-"))(input)?;
|
let (input, sign) = skip_matches(input, b'-');
|
||||||
let (input, raw) = digit1(input)?;
|
let (input, unsigned) = u_int(input)?;
|
||||||
let val: i32 = raw
|
let signed = unsigned as i32;
|
||||||
.parse()
|
Ok((input, if sign { -signed } else { signed }))
|
||||||
.map_err(|_| Err::Error(nom::error::Error::new(raw, ErrorKind::Digit)))?;
|
|
||||||
Ok((input, if sign.is_some() { -val } else { val }))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn u_int(input: &str) -> IResult<u32> {
|
fn u_int(input: &str) -> IResult<u32> {
|
||||||
let (input, quote) = opt(tag("\""))(input)?;
|
let (input, raw) = take_until(input, b' ');
|
||||||
|
|
||||||
let (input, raw) = digit1(input)?;
|
|
||||||
let val = raw.parse().map_err(|_| err_incomplete())?;
|
let val = raw.parse().map_err(|_| err_incomplete())?;
|
||||||
|
|
||||||
let input = if quote.is_some() {
|
|
||||||
tag("\"")(input)?.0
|
|
||||||
} else {
|
|
||||||
input
|
|
||||||
};
|
|
||||||
Ok((input, val))
|
Ok((input, val))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn position(input: &str) -> IResult<(i32, i32, i32)> {
|
|
||||||
let (input, x) = int(input)?;
|
|
||||||
let (input, _) = tag(" ")(input)?;
|
|
||||||
let (input, y) = int(input)?;
|
|
||||||
let (input, _) = tag(" ")(input)?;
|
|
||||||
let (input, z) = int(input)?;
|
|
||||||
Ok((input, (x, y, z)))
|
|
||||||
}
|
|
||||||
|
|
||||||
pub trait EventField<'a>: Sized + 'a {
|
pub trait EventField<'a>: Sized + 'a {
|
||||||
fn parse_field(input: &'a str) -> IResult<Self>;
|
fn parse_field(input: &'a str) -> IResult<Self>;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> EventField<'a> for &'a str {
|
impl<'a> EventField<'a> for &'a str {
|
||||||
fn parse_field(input: &'a str) -> IResult<Self> {
|
fn parse_field(input: &'a str) -> IResult<Self> {
|
||||||
if input.starts_with('"') {
|
let (input, quoted) = skip_matches(input, b'"');
|
||||||
quoted(take_while(|c| c != '"'))(input)
|
if quoted {
|
||||||
|
let (value, input) = split_once(input, b'"', 1)?;
|
||||||
|
Ok((input, value))
|
||||||
} else {
|
} else {
|
||||||
Ok(("", input))
|
Ok(("", input))
|
||||||
}
|
}
|
||||||
|
|
@ -384,11 +365,11 @@ impl EventFieldFromStr for SocketAddr {}
|
||||||
|
|
||||||
impl<'a, T: EventField<'a>> EventField<'a> for (T, T, T) {
|
impl<'a, T: EventField<'a>> EventField<'a> for (T, T, T) {
|
||||||
fn parse_field(input: &'a str) -> IResult<Self> {
|
fn parse_field(input: &'a str) -> IResult<Self> {
|
||||||
let (input, x) = T::parse_field(input)?;
|
let (input, x) = parse_field(input)?;
|
||||||
let (input, _) = tag(" ")(input)?;
|
let input = skip(input, 1)?;
|
||||||
let (input, y) = T::parse_field(input)?;
|
let (input, y) = parse_field(input)?;
|
||||||
let (input, _) = tag(" ")(input)?;
|
let input = skip(input, 1)?;
|
||||||
let (input, z) = T::parse_field(input)?;
|
let (input, z) = parse_field(input)?;
|
||||||
Ok((input, (x, y, z)))
|
Ok((input, (x, y, z)))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -14,6 +14,7 @@ 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;
|
||||||
|
use std::num::ParseIntError;
|
||||||
pub use tf_log_parser_derive::Event;
|
pub use tf_log_parser_derive::Event;
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
|
|
||||||
|
|
@ -64,6 +65,12 @@ impl From<nom::error::Error<&'_ str>> for Error {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl From<ParseIntError> for Error {
|
||||||
|
fn from(_: ParseIntError) -> Self {
|
||||||
|
Error::Malformed
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
type Result<O, E = Error> = std::result::Result<O, E>;
|
type Result<O, E = Error> = std::result::Result<O, E>;
|
||||||
|
|
||||||
#[doc(hidden)]
|
#[doc(hidden)]
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue