Skip to main content

subtr_actor/ballchasing/
compare.rs

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