From 14148c74109f409369b4807c3b8ce255b0eb4a45 Mon Sep 17 00:00:00 2001 From: Pavel Perestoronin Date: Mon, 24 Apr 2023 22:38:23 +0200 Subject: [PATCH] Break: add initial `meta.json` support --- Cargo.toml | 1 + src/error.rs | 14 +++++++------- src/lib.rs | 3 +++ src/models.rs | 2 ++ src/models/battle_results_dat.rs | 8 ++------ src/models/meta.rs | 28 ++++++++++++++++++++++++++++ src/prelude.rs | 2 -- src/replay.rs | 14 +++++++++++--- tests/battle_results.rs | 2 +- tests/meta.rs | 16 ++++++++++++++++ 10 files changed, 71 insertions(+), 19 deletions(-) create mode 100644 src/models/meta.rs create mode 100644 tests/meta.rs diff --git a/Cargo.toml b/Cargo.toml index ab3030f..40114c0 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -18,6 +18,7 @@ prost = "0.11.3" serde = { version = "1.0.148", features = ["derive"] } serde_bytes = "0.11.7" serde-pickle = "1.1.1" +serde_json = "1.0.96" thiserror = "1.0.37" zip = { version = "0.6.3", default-features = false, features = ["deflate"] } diff --git a/src/error.rs b/src/error.rs index f919695..21ff47d 100644 --- a/src/error.rs +++ b/src/error.rs @@ -2,18 +2,18 @@ use thiserror::Error; #[derive(Error, Debug)] pub enum Error { - #[error("failed to open the ZIP-archive")] + #[error("failed to open the replay as a ZIP-archive")] OpenArchiveFailed(#[source] zip::result::ZipError), - #[error("failed to open the battle results entry in the replay archive")] - OpenBattleResultsFailed(#[source] zip::result::ZipError), + #[error("failed to open the entry in the replay archive")] + OpenEntryFailed(#[source] zip::result::ZipError), - #[error("failed to read the archive entry")] - ReadEntryFailed(#[source] std::io::Error), - - #[error("failed to decode")] + #[error("failed to decode the Protocol Buffers encoded data")] DecodeFailed(#[source] prost::DecodeError), #[error("failed to un-pickle")] UnpickleFailed(#[source] serde_pickle::Error), + + #[error("failed to decode JSON")] + JsonDecodeFailed(#[source] serde_json::Error), } diff --git a/src/lib.rs b/src/lib.rs index 117d812..01ba95f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -17,6 +17,9 @@ pub mod prelude; mod replay; mod result; +pub use self::error::Error; +pub use self::result::Result; + #[cfg(doctest)] mod test_readme { macro_rules! external_doc_test { diff --git a/src/models.rs b/src/models.rs index 9e6a457..75eb77e 100644 --- a/src/models.rs +++ b/src/models.rs @@ -1,5 +1,7 @@ mod battle_results; mod battle_results_dat; +mod meta; pub use self::battle_results::*; pub use self::battle_results_dat::*; +pub use self::meta::*; diff --git a/src/models/battle_results_dat.rs b/src/models/battle_results_dat.rs index 7d8579c..05ba0ea 100644 --- a/src/models/battle_results_dat.rs +++ b/src/models/battle_results_dat.rs @@ -35,17 +35,13 @@ impl BattleResultsDat { pub fn from_reader(reader: impl Read) -> Result { serde_pickle::from_reader(reader, Default::default()).map_err(Error::UnpickleFailed) } - - /// Decodes the battle results from the internal buffer. - pub fn decode_battle_results(&self) -> Result { - BattleResults::decode(self.buffer.as_ref()).map_err(Error::DecodeFailed) - } } impl TryInto for BattleResultsDat { type Error = Error; + /// Decodes the battle results from the internal buffer. fn try_into(self) -> Result { - self.decode_battle_results() + BattleResults::decode(self.buffer.as_ref()).map_err(Error::DecodeFailed) } } diff --git a/src/models/meta.rs b/src/models/meta.rs new file mode 100644 index 0000000..84178d0 --- /dev/null +++ b/src/models/meta.rs @@ -0,0 +1,28 @@ +use std::io::Read; + +use serde::Deserialize; + +use crate::error::Error; +use crate::result::Result; + +/// The model for `meta.json`. +#[derive(Deserialize)] +pub struct Meta { + #[serde(rename = "playerName")] + pub player_name: String, + + #[serde(rename = "arenaUniqueId")] + pub arena_unique_id: String, + + #[serde(rename = "battleDuration")] + pub battle_duration_secs: f64, + + #[serde(rename = "vehicleCompDescriptor")] + pub tank_id: u16, +} + +impl Meta { + pub fn from_reader(reader: impl Read) -> Result { + serde_json::from_reader(reader).map_err(Error::JsonDecodeFailed) + } +} diff --git a/src/prelude.rs b/src/prelude.rs index d6df9f8..e489608 100644 --- a/src/prelude.rs +++ b/src/prelude.rs @@ -1,4 +1,2 @@ -pub use crate::error::Error; pub use crate::models::*; pub use crate::replay::Replay; -pub use crate::result::Result; diff --git a/src/replay.rs b/src/replay.rs index 6976bba..ce0416c 100644 --- a/src/replay.rs +++ b/src/replay.rs @@ -3,7 +3,7 @@ use std::io::{Read, Seek}; use zip::ZipArchive; use crate::error::Error; -use crate::models::{BattleResults, BattleResultsDat}; +use crate::models::{BattleResults, BattleResultsDat, Meta}; use crate::result::Result; /// Opened (but not yet parsed) replay. @@ -19,7 +19,7 @@ impl Replay { /// Reads and parses the battle results from the replay. pub fn read_battle_results(&mut self) -> Result { - self.read_battle_results_dat()?.decode_battle_results() + self.read_battle_results_dat()?.try_into() } /// Reads and parses the included `battle_results.dat`. @@ -27,7 +27,15 @@ impl Replay { let pickled_battle_results = self .0 .by_name("battle_results.dat") - .map_err(Error::OpenBattleResultsFailed)?; + .map_err(Error::OpenEntryFailed)?; BattleResultsDat::from_reader(pickled_battle_results) } + + pub fn read_meta(&mut self) -> Result { + Meta::from_reader( + self.0 + .by_name("meta.json") + .map_err(Error::OpenEntryFailed)?, + ) + } } diff --git a/tests/battle_results.rs b/tests/battle_results.rs index 08b485a..671af31 100644 --- a/tests/battle_results.rs +++ b/tests/battle_results.rs @@ -8,7 +8,7 @@ fn player_results_ok() -> Result<()> { let battle_results_dat = Replay::open(File::open("tests/replays/player_results.wotbreplay")?)? .read_battle_results_dat()?; assert_eq!(battle_results_dat.arena_unique_id, 1661909200500084); - let battle_results = battle_results_dat.decode_battle_results()?; + let battle_results: BattleResults = battle_results_dat.try_into()?; let snak_the_ripper_info = &battle_results.player_results[3].info; assert_eq!(snak_the_ripper_info.base_xp, 929); diff --git a/tests/meta.rs b/tests/meta.rs new file mode 100644 index 0000000..37570ef --- /dev/null +++ b/tests/meta.rs @@ -0,0 +1,16 @@ +use std::fs::File; + +use anyhow::Result; +use wotbreplay_parser::prelude::*; + +#[test] +fn meta_ok() -> Result<()> { + let meta = Replay::open(File::open("tests/replays/player_results.wotbreplay")?)?.read_meta()?; + + assert_eq!(meta.player_name, "zeekrab"); + assert_eq!(meta.arena_unique_id, "1661909200500084"); + assert_eq!(meta.battle_duration_secs, 164.08485); + assert_eq!(meta.tank_id, 26657); + + Ok(()) +}