Skip to main content

subtr_actor/ballchasing/
compare.rs

1use std::path::Path;
2
3use anyhow::Context;
4use serde_json::Value;
5
6use crate::*;
7
8use super::config::{MatchConfig, StatMatcher};
9use super::conversion::{
10    build_actual_comparable_stats, build_expected_comparable_stats,
11    compute_ballchasing_comparable_stats,
12};
13use super::report::BallchasingComparisonReport;
14
15pub fn parse_replay_bytes(data: &[u8]) -> anyhow::Result<boxcars::Replay> {
16    boxcars::ParserBuilder::new(data)
17        .always_check_crc()
18        .must_parse_network_data()
19        .parse()
20        .context("Failed to parse replay")
21}
22
23pub fn parse_replay_file(path: impl AsRef<Path>) -> anyhow::Result<boxcars::Replay> {
24    let path = path.as_ref();
25    let data = std::fs::read(path)
26        .with_context(|| format!("Failed to read replay file: {}", path.display()))?;
27    parse_replay_bytes(&data).with_context(|| format!("Failed to parse replay: {}", path.display()))
28}
29
30pub fn compare_replay_against_ballchasing(
31    replay: &boxcars::Replay,
32    ballchasing: &Value,
33    config: &MatchConfig,
34) -> SubtrActorResult<BallchasingComparisonReport> {
35    let computed = compute_ballchasing_comparable_stats(replay)?;
36    let actual = build_actual_comparable_stats(&computed);
37    let expected = build_expected_comparable_stats(ballchasing);
38
39    let mut matcher = StatMatcher::default();
40    expected.compare(&actual, &mut matcher, config);
41    Ok(BallchasingComparisonReport {
42        mismatches: matcher.mismatches,
43    })
44}
45
46pub fn compare_replay_against_ballchasing_json(
47    replay_path: impl AsRef<Path>,
48    json_path: impl AsRef<Path>,
49    config: &MatchConfig,
50) -> anyhow::Result<BallchasingComparisonReport> {
51    let replay_path = replay_path.as_ref();
52    let json_path = json_path.as_ref();
53    let replay = parse_replay_file(replay_path)?;
54    let json_file = std::fs::File::open(json_path)
55        .with_context(|| format!("Failed to open ballchasing json: {}", json_path.display()))?;
56    let ballchasing: Value = serde_json::from_reader(json_file)
57        .with_context(|| format!("Failed to parse ballchasing json: {}", json_path.display()))?;
58
59    compare_replay_against_ballchasing(&replay, &ballchasing, config)
60        .map_err(|error| anyhow::Error::new(error.variant))
61}
62
63pub fn compare_fixture_directory(
64    path: &Path,
65    config: &MatchConfig,
66) -> anyhow::Result<BallchasingComparisonReport> {
67    let replay_path = path.join("replay.replay");
68    let json_path = path.join("ballchasing.json");
69    compare_replay_against_ballchasing_json(&replay_path, &json_path, config)
70}