subject postprocessing

This commit is contained in:
Robin Appelman 2021-08-07 19:28:58 +02:00
commit 747ee7d1a9
10 changed files with 2970 additions and 22 deletions

View file

@ -1,12 +1,31 @@
use crate::raw_event::RawSubject;
use std::convert::TryFrom;
use std::fmt::{Display, Formatter};
use std::str::FromStr;
use steamid_ng::SteamID;
use thiserror::Error;
#[derive(Copy, Clone, Eq, PartialEq, Debug)]
#[derive(Copy, Clone, Eq, PartialEq, Debug, Ord, PartialOrd)]
pub enum Team {
Red,
Blue,
}
impl Team {
pub fn as_str(self) -> &'static str {
match self {
Team::Red => "Red",
Team::Blue => "Blue",
}
}
}
impl Display for Team {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
self.as_str().fmt(f)
}
}
impl FromStr for Team {
type Err = ();
@ -20,7 +39,7 @@ impl FromStr for Team {
}
/// Optimized subject id
#[derive(Copy, Clone, Eq, PartialEq, Debug)]
#[derive(Copy, Clone, Eq, PartialEq, Debug, Ord, PartialOrd)]
pub enum SubjectId {
Player(u8),
Team(Team),
@ -40,3 +59,52 @@ impl From<&RawSubject<'_>> for SubjectId {
}
}
}
#[derive(Debug, PartialEq)]
pub enum SubjectData {
Player {
name: String,
user_id: u8,
steam_id: SteamID,
team: Team,
},
Team(Team),
System(String),
Console,
World,
}
#[derive(Debug, Error)]
pub enum SubjectError {
#[error("Invalid user id")]
InvalidUserId,
#[error("Invalid steam id")]
InvalidSteamId,
#[error("Invalid team name")]
InvalidTeam,
}
impl TryFrom<&RawSubject<'_>> for SubjectData {
type Error = SubjectError;
fn try_from(raw: &RawSubject<'_>) -> Result<Self, Self::Error> {
Ok(match raw {
RawSubject::Player {
name,
user_id,
steam_id,
team,
} => SubjectData::Player {
name: name.to_string(),
user_id: user_id.parse().map_err(|_| SubjectError::InvalidUserId)?,
steam_id: SteamID::from_steam3(steam_id)
.map_err(|_| SubjectError::InvalidSteamId)?,
team: team.parse().map_err(|_| SubjectError::InvalidTeam)?,
},
RawSubject::Team(team) => SubjectData::Team(team.parse().unwrap()),
RawSubject::System(name) => SubjectData::System(name.to_string()),
RawSubject::Console => SubjectData::Console,
RawSubject::World => SubjectData::World,
})
}
}

View file

@ -1,8 +1,11 @@
use crate::common::{SubjectData, SubjectError, SubjectId};
use crate::module::EventHandler;
use crate::raw_event::RawEvent;
use crate::raw_event::{RawEvent, RawSubject};
use chrono::{DateTime, Utc};
use std::collections::BTreeMap;
use std::convert::TryInto;
use std::fmt::{Debug, Formatter};
use std::ops::Index;
use thiserror::Error;
mod common;
@ -26,12 +29,41 @@ impl<Handler: EventHandler> Debug for Error<Handler> {
}
}
impl<Handler: EventHandler> From<SubjectError> for Error<Handler> {
fn from(e: SubjectError) -> Self {
Error::Malformed(e.to_string())
}
}
impl<Handler: EventHandler> From<nom::error::Error<&'_ str>> for Error<Handler> {
fn from(e: nom::error::Error<&str>) -> Self {
Error::Malformed(e.to_string())
}
}
#[derive(Default)]
pub struct SubjectMap(BTreeMap<SubjectId, SubjectData>);
impl Index<SubjectId> for SubjectMap {
type Output = SubjectData;
fn index(&self, index: SubjectId) -> &Self::Output {
self.0
.get(&index)
.expect("subject id created without matching subject data")
}
}
impl SubjectMap {
pub fn insert(&mut self, raw: &RawSubject) -> Result<SubjectId, SubjectError> {
let id = raw.into();
if !self.0.contains_key(&id) {
self.0.insert(id, raw.try_into()?);
}
Ok(id)
}
}
pub fn parse_with_handler<Handler: EventHandler>(
log: &str,
) -> Result<Handler::Output, Error<Handler>> {
@ -43,6 +75,7 @@ pub fn parse_with_handler<Handler: EventHandler>(
let mut handler = Handler::default();
let mut start_time: Option<DateTime<Utc>> = None;
let mut subjects = SubjectMap::default();
for event_res in events {
let event = event_res?;
@ -56,10 +89,10 @@ pub fn parse_with_handler<Handler: EventHandler>(
}
};
handler
.handle(match_time, (&event.subject).into(), &event)
.handle(match_time, subjects.insert(&event.subject)?, &event)
.map_err(Error::HandlerError)?;
}
}
Ok(handler.finish())
Ok(handler.finish(&subjects))
}

View file

@ -1,22 +1,50 @@
use crate::common::SubjectId;
use crate::common::{SubjectData, SubjectId};
use crate::module::EventHandler;
use crate::raw_event::{RawEvent, RawEventType};
use crate::SubjectMap;
use std::convert::Infallible;
use steamid_ng::SteamID;
pub struct ChatMessage {
struct BareChatMessage {
pub time: u32,
pub subject: SubjectId,
pub message: String,
pub chat_type: ChatType,
}
pub struct ChatMessage {
pub time: u32,
pub name: String,
pub steam_id: SteamID,
pub message: String,
pub chat_type: ChatType,
}
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()),
_ => {
unreachable!("only player messages are added");
}
};
ChatMessage {
time: bare.time,
name,
steam_id,
message: bare.message,
chat_type: bare.chat_type,
}
}
}
pub enum ChatType {
All,
Team,
}
#[derive(Default)]
pub struct ChatHandler(Vec<ChatMessage>);
pub struct ChatHandler(Vec<BareChatMessage>);
impl EventHandler for ChatHandler {
type Output = Vec<ChatMessage>;
@ -36,13 +64,13 @@ impl EventHandler for ChatHandler {
return Ok(());
}
match event.ty {
RawEventType::SayTeam => self.0.push(ChatMessage {
RawEventType::SayTeam => self.0.push(BareChatMessage {
time,
subject,
message: event.params.trim_matches('"').into(),
chat_type: ChatType::Team,
}),
RawEventType::Say => self.0.push(ChatMessage {
RawEventType::Say => self.0.push(BareChatMessage {
time,
subject,
message: event.params.trim_matches('"').into(),
@ -53,7 +81,10 @@ impl EventHandler for ChatHandler {
Ok(())
}
fn finish(self) -> Self::Output {
fn finish(self, subjects: &SubjectMap) -> Self::Output {
self.0
.into_iter()
.map(|bare| ChatMessage::from_bare(bare, subjects))
.collect()
}
}

View file

@ -1,6 +1,7 @@
use crate::common::SubjectId;
use crate::module::EventHandler;
use crate::raw_event::{RawEvent, RawEventType};
use crate::SubjectMap;
use chrono::{DateTime, FixedOffset, NaiveDateTime, TimeZone, Utc};
use std::str::{FromStr, ParseBoolError};
use steamid_ng::SteamID;
@ -182,7 +183,7 @@ impl EventHandler for LobbySettingsHandler {
Ok(())
}
fn finish(self) -> Self::Output {
fn finish(self, _subjects: &SubjectMap) -> Self::Output {
if self.0.id > 0 {
Some(self.0)
} else {

View file

@ -1,5 +1,6 @@
use crate::common::SubjectId;
use crate::raw_event::{RawEvent, RawEventType};
use crate::SubjectMap;
pub use chat::{ChatHandler, ChatMessage, ChatType};
pub use lobbysettings::{
LobbySettingsError, LobbySettingsHandler, Location, Settings as LobbySettings,
@ -25,7 +26,7 @@ pub trait EventHandler: Default {
event: &RawEvent,
) -> Result<(), Self::Error>;
fn finish(self) -> Self::Output;
fn finish(self, subjects: &SubjectMap) -> Self::Output;
}
#[derive(Default)]
@ -57,8 +58,8 @@ impl<Head: EventHandler, Tail: EventHandler> EventHandler for HandlerStack<Head,
Ok(())
}
fn finish(self) -> Self::Output {
(self.head.finish(), self.tail.finish())
fn finish(self, subjects: &SubjectMap) -> Self::Output {
(self.head.finish(subjects), self.tail.finish(subjects))
}
}
@ -112,9 +113,9 @@ impl<Handler: EventHandler> EventHandler for OptionalHandler<Handler> {
Ok(())
}
fn finish(self) -> Self::Output {
fn finish(self, subjects: &SubjectMap) -> Self::Output {
match self {
OptionalHandler::Active(handler) => Ok(handler.finish()),
OptionalHandler::Active(handler) => Ok(handler.finish(subjects)),
OptionalHandler::Failed(e) => Err(e),
}
}