💥 Improve the docs and error handling

This commit is contained in:
eigenein committed 2023-05-08 23:39:52 +02:00
1 parent 838a49ee68
commit c986abca5d
10 files changed
+58 -19

No files matched your search

+1 -1
View File
@@ -20,7 +20,7 @@ use wotbreplay_parser::replay::Replay;
fn main() -> Result<()> { fn main() -> Result<()> {
let battle_results = Replay::open(File::open("replays/20221203_player_results.wotbreplay")?)?.read_battle_results()?; let battle_results = Replay::open(File::open("replays/20221203_player_results.wotbreplay")?)?.read_battle_results()?;
assert_eq!(battle_results.timestamp, 1670083956); assert_eq!(battle_results.timestamp_secs, 1670083956);
assert_eq!(battle_results.players.len(), 14); assert_eq!(battle_results.players.len(), 14);
assert_eq!(battle_results.players[0].account_id, 595693744); assert_eq!(battle_results.players[0].account_id, 595693744);
+8 -4
View File
@@ -21,9 +21,13 @@ pub enum Error {
#[error("JSON error")] #[error("JSON error")]
JsonError(#[from] serde_json::Error), JsonError(#[from] serde_json::Error),
#[error("invalid magic: {0:#X}, expected: {1:#X}")] #[error("invalid magic: {actual:#X}, expected: {expected:#X}")]
InvalidMagic(u32, u32), InvalidMagic { expected: u32, actual: u32 },
#[error("failed to read a packet length, got {0} bytes")] #[error("failed to parse type {type_} packet's payload at replay time {clock_secs}s")]
PacketLengthError(usize), PacketPayloadParsingError {
source: Box<Error>,
type_: u32,
clock_secs: f32,
},
} }
+9 -6
View File
@@ -9,7 +9,7 @@ impl BattleResults {
/// ///
/// # Note /// # Note
/// ///
/// This does **not** parse `battle_results.dat` itself, but the un-pickled tuple element. /// This can **not** parse `battle_results.dat` itself, but the un-pickled tuple element.
/// To parse `battle_results.dat`, use [`crate::models::battle_results_dat::BattleResultsDat`]. /// To parse `battle_results.dat`, use [`crate::models::battle_results_dat::BattleResultsDat`].
pub fn from_buffer(buffer: impl Buf) -> Result<Self> { pub fn from_buffer(buffer: impl Buf) -> Result<Self> {
Ok(Self::decode(buffer)?) Ok(Self::decode(buffer)?)
@@ -23,7 +23,7 @@ impl BattleResults {
pub struct BattleResults { pub struct BattleResults {
/// Battle timestamp. /// Battle timestamp.
#[prost(int64, tag = "2")] #[prost(int64, tag = "2")]
pub timestamp: i64, pub timestamp_secs: i64,
#[prost(enumeration = "TeamNumber", optional, tag = "3")] #[prost(enumeration = "TeamNumber", optional, tag = "3")]
pub winner_team_number: Option<i32>, pub winner_team_number: Option<i32>,
@@ -40,7 +40,7 @@ pub struct BattleResults {
#[prost(message, repeated, tag = "201")] #[prost(message, repeated, tag = "201")]
pub players: Vec<Player>, pub players: Vec<Player>,
/// Player's results. /// Individual player's results.
#[prost(message, repeated, tag = "301")] #[prost(message, repeated, tag = "301")]
pub player_results: Vec<PlayerResults>, pub player_results: Vec<PlayerResults>,
} }
@@ -65,8 +65,9 @@ pub struct PlayerInfo {
pub nickname: String, pub nickname: String,
/// Some sort of platoon ID: /// Some sort of platoon ID:
/// - contains same ID for a platoon members, or ///
/// - `None` for non-platoon players /// - contains same ID for the platoon members, or
/// - [`None`] for non-platoon players
#[prost(uint32, optional, tag = "2")] #[prost(uint32, optional, tag = "2")]
pub platoon_id: Option<u32>, pub platoon_id: Option<u32>,
@@ -165,6 +166,8 @@ pub struct PlayerResultsInfo {
/// ///
/// Note, that this is **not** the game client's displayed rating. /// Note, that this is **not** the game client's displayed rating.
/// This field matches the `mm_rating` as returned by the Wargaming.net API. /// This field matches the `mm_rating` as returned by the Wargaming.net API.
///
/// The display rating is calculated as: `3000.0 + mm_rating * 10.0`.
#[prost(float, optional, tag = "107")] #[prost(float, optional, tag = "107")]
pub mm_rating: Option<f32>, pub mm_rating: Option<f32>,
@@ -173,7 +176,7 @@ pub struct PlayerResultsInfo {
} }
impl PlayerResultsInfo { impl PlayerResultsInfo {
/// Returns displayed rating as per the game client. /// Calculate displayed rating according to the game client.
pub fn display_rating(&self) -> Option<u32> { pub fn display_rating(&self) -> Option<u32> {
self.mm_rating self.mm_rating
.map(|mm_rating| (mm_rating * 10.0 + 3000.0) as u32) .map(|mm_rating| (mm_rating * 10.0 + 3000.0) as u32)
+9 -2
View File
@@ -53,7 +53,10 @@ fn assert_magic<T: Into<u32> + PartialEq>(actual: T, expected: T) -> Result {
if actual == expected { if actual == expected {
Ok(()) Ok(())
} else { } else {
Err(Error::InvalidMagic(actual.into(), expected.into())) Err(Error::InvalidMagic {
actual: actual.into(),
expected: expected.into(),
})
} }
} }
@@ -82,7 +85,11 @@ fn read_pickled<T: DeserializeOwned>(reader: &mut impl Read, length: usize) -> R
/// Read 2-byte length surrounded by the magical `0xFF` and `0x00`, /// Read 2-byte length surrounded by the magical `0xFF` and `0x00`,
/// unless it's a simple 1-byte length. /// unless it's a simple 1-byte length.
/// ///
/// P.S. Okay, Wargaming, I've no idea what you smoke, but I want some! /// # Context
///
/// It seems that for payloads larger than 254 bytes, Wargaming writes `00 <XX XX> FF`,
/// where the word in the middle is the actual length of a payload, and the `00` and `FF`
/// are magic values.
#[inline] #[inline]
fn read_quirky_length(reader: &mut impl Read) -> Result<usize> { fn read_quirky_length(reader: &mut impl Read) -> Result<usize> {
match reader.read_u8()? { match reader.read_u8()? {
+13 -1
View File
@@ -22,8 +22,16 @@ pub struct BasePlayerCreate {
#[serde(rename(deserialize = "camouflageSlot"))] #[serde(rename(deserialize = "camouflageSlot"))]
pub camouflage_slot: u8, pub camouflage_slot: u8,
/// Average team ratings for rating battles.
///
/// # Contains
///
/// - [`Some`] 2-vector, if the attribute is present in the replay. Each element represents one team:
/// - [`Some`] rating, if known
/// - [`None`], if the average rating is unknown (all the players are calibrating)
/// - [`None`], if the attribute is missing in the replay
#[serde(default, rename(deserialize = "avgMmr"))] #[serde(default, rename(deserialize = "avgMmr"))]
pub average_mmr: Option<Vec<Option<f64>>>, pub average_mm_ratings: Option<Vec<Option<f64>>>,
#[serde(default, rename(deserialize = "playerWaitTimes"))] #[serde(default, rename(deserialize = "playerWaitTimes"))]
pub player_wait_times: Option<HashMap<u32, f64>>, pub player_wait_times: Option<HashMap<u32, f64>>,
@@ -35,6 +43,10 @@ pub struct BasePlayerCreate {
pub turbo_battles_statistics: Option<TurboBattlesStatistics>, pub turbo_battles_statistics: Option<TurboBattlesStatistics>,
} }
/// I don't know what that is:
///
/// - On EU, Asia, and NA the values are just constants
/// - On Russia, the `alpha` and `beta` are different constants, and the chances actually differ for different replays
#[derive(Debug, Serialize, Deserialize)] #[derive(Debug, Serialize, Deserialize)]
pub struct TurboBattlesStatistics { pub struct TurboBattlesStatistics {
#[serde(rename(deserialize = "turboBattleChance"))] #[serde(rename(deserialize = "turboBattleChance"))]
+1 -1
View File
@@ -26,7 +26,7 @@ pub enum EntityMethod {
} }
impl EntityMethod { impl EntityMethod {
/// Parse the entity method payload. /// Parse the entity method raw payload.
pub fn new(raw_payload: &[u8]) -> Result<Self> { pub fn new(raw_payload: &[u8]) -> Result<Self> {
let mut raw_payload = raw_payload; let mut raw_payload = raw_payload;
+14 -1
View File
@@ -6,6 +6,7 @@ use serde_with::serde_as;
use crate::models::data::payload::Payload; use crate::models::data::payload::Payload;
use crate::result::Result; use crate::result::Result;
use crate::Error;
/// «Packet» is a single traffic message in a replay, /// «Packet» is a single traffic message in a replay,
/// packets tell us what is going on during the battle. /// packets tell us what is going on during the battle.
@@ -28,14 +29,26 @@ pub struct Packet {
} }
impl Packet { impl Packet {
/// Parse next packet from the reader.
///
/// # Returns
///
/// Parsed packet, or [`None`], when no packets are left.
pub fn from_reader(reader: &mut impl Read) -> Result<Option<Self>> { pub fn from_reader(reader: &mut impl Read) -> Result<Option<Self>> {
let Ok(length) = reader.read_u32::<LittleEndian>() else { return Ok(None) }; let Ok(length) = reader.read_u32::<LittleEndian>() else { return Ok(None) };
let type_ = reader.read_u32::<LittleEndian>()?; let type_ = reader.read_u32::<LittleEndian>()?;
let clock_secs = reader.read_f32::<LittleEndian>()?; let clock_secs = reader.read_f32::<LittleEndian>()?;
let raw_payload = Self::read_raw_payload(reader, length as usize)?; let raw_payload = Self::read_raw_payload(reader, length as usize)?;
let payload = Payload::new(type_, &raw_payload).map_err(|source| {
Error::PacketPayloadParsingError {
source: Box::new(source),
type_,
clock_secs,
}
})?;
let this = Self { let this = Self {
clock_secs, clock_secs,
payload: Payload::new(type_, &raw_payload)?, payload,
#[cfg(feature = "raw-payload")] #[cfg(feature = "raw-payload")]
raw_payload, raw_payload,
+1 -1
View File
@@ -29,7 +29,7 @@ pub enum Payload {
} }
impl Payload { impl Payload {
/// Parse the packet payload. /// Parse the raw packet payload.
pub fn new(packet_type: u32, raw_payload: &[u8]) -> Result<Self> { pub fn new(packet_type: u32, raw_payload: &[u8]) -> Result<Self> {
let mut raw_payload = raw_payload; let mut raw_payload = raw_payload;
+1 -1
View File
@@ -21,7 +21,7 @@ use crate::result::Result;
pub struct Replay<R>(pub(crate) ZipArchive<R>); pub struct Replay<R>(pub(crate) ZipArchive<R>);
impl<R: Read + Seek> Replay<R> { impl<R: Read + Seek> Replay<R> {
/// Opens a replay from the reader which contains a `*.wotbreplay`. /// Open a replay from the reader which contains a `*.wotbreplay`.
pub fn open(reader: R) -> Result<Self> { pub fn open(reader: R) -> Result<Self> {
Ok(Self(ZipArchive::new(reader)?)) Ok(Self(ZipArchive::new(reader)?))
} }
+1 -1
View File
@@ -50,7 +50,7 @@ fn base_player_create_ok() -> Result<()> {
assert_eq!(arguments.matchmaker_type, 2); assert_eq!(arguments.matchmaker_type, 2);
assert_eq!(arguments.camouflage_slot, 1); assert_eq!(arguments.camouflage_slot, 1);
assert_eq!( assert_eq!(
arguments.average_mmr, arguments.average_mm_ratings,
Some(vec![Some(59.29457753400008), Some(99.34182121604681)]) Some(vec![Some(59.29457753400008), Some(99.34182121604681)])
); );
assert_eq!( assert_eq!(