subtr-actor 0.5.0

Rocket League replay transformer
Documentation
use std::path::Path;

use anyhow::Context;
use serde_json::Value;

use super::comparison::{
    build_actual_comparable_stats, build_expected_comparable_stats, compute_comparable_stats,
    MatchConfig, StatMatcher,
};
use super::report::BallchasingComparisonReport;
use crate::*;

pub fn parse_replay_bytes(data: &[u8]) -> anyhow::Result<boxcars::Replay> {
    boxcars::ParserBuilder::new(data)
        .always_check_crc()
        .must_parse_network_data()
        .parse()
        .context("Failed to parse replay")
}

pub fn parse_replay_file(path: impl AsRef<Path>) -> anyhow::Result<boxcars::Replay> {
    let path = path.as_ref();
    let data = std::fs::read(path)
        .with_context(|| format!("Failed to read replay file: {}", path.display()))?;
    parse_replay_bytes(&data).with_context(|| format!("Failed to parse replay: {}", path.display()))
}

pub fn compare_replay_against_ballchasing(
    replay: &boxcars::Replay,
    ballchasing: &Value,
    config: &MatchConfig,
) -> SubtrActorResult<BallchasingComparisonReport> {
    let computed = compute_comparable_stats(replay)?;
    let actual = build_actual_comparable_stats(&computed);
    let expected = build_expected_comparable_stats(ballchasing);

    let mut matcher = StatMatcher::default();
    expected.compare(&actual, &mut matcher, config);
    Ok(BallchasingComparisonReport {
        mismatches: matcher.into_mismatches(),
    })
}

pub fn compare_replay_against_ballchasing_json(
    replay_path: impl AsRef<Path>,
    json_path: impl AsRef<Path>,
    config: &MatchConfig,
) -> anyhow::Result<BallchasingComparisonReport> {
    let replay_path = replay_path.as_ref();
    let json_path = json_path.as_ref();
    let replay = parse_replay_file(replay_path)?;
    let json_file = std::fs::File::open(json_path)
        .with_context(|| format!("Failed to open ballchasing json: {}", json_path.display()))?;
    let ballchasing: Value = serde_json::from_reader(json_file)
        .with_context(|| format!("Failed to parse ballchasing json: {}", json_path.display()))?;

    compare_replay_against_ballchasing(&replay, &ballchasing, config)
        .map_err(|error| anyhow::Error::new(error.variant))
}

pub fn compare_fixture_directory(
    path: &Path,
    config: &MatchConfig,
) -> anyhow::Result<BallchasingComparisonReport> {
    let (replay_path, json_path) = if path.is_dir() {
        (path.join("replay.replay"), path.join("ballchasing.json"))
    } else {
        (
            path.with_extension("replay"),
            path.with_extension("ballchasing.json"),
        )
    };
    compare_replay_against_ballchasing_json(&replay_path, &json_path, config)
}