<commit_before>//! This example of boxcars extracts all the player names found in the "PlayerStats" property of the
//! header. This property may be absent in some replays or lack players that drop or join mid-game.
//! A more foolproof approach is to scour the network data for a specific property:
//! "Engine.PlayerReplicationInfo:PlayerName". This example shows both methods. The error handling
//! demonstrated is minimal, relying on stringly typed errors. In practice, prefer a richer error
//! type.
use boxcars::{HeaderProp, ParserBuilder};
use std::error;
use std::io::{self, Read};

/// Given an array of objects (represented as a slice of key-value pairs), find all the instances
/// of the "Name" key and extract the string value
fn names_in_header(
    stats: &[Vec<(String, HeaderProp)>],
) -> impl Iterator<Item = &String> {
    stats
        .iter()
        .flat_map(|v| v.iter())
        .filter(|(prop_name, _)| *prop_name == "Name")
        .filter_map(|(_, prop_val)| {
            if let HeaderProp::Str(name) = prop_val {
                Some(name)
            } else {
                None
            }
        })
}

/// Given network frames and the object id to "Engine.PlayerReplicationInfo:PlayerName", comb
/// through all the attributes looking for attributes that have our object id.
fn names_in_network(
    frames: &[boxcars::Frame],
    name_attribute_id: boxcars::ObjectId,
) -> Vec<&String> {
    let mut names = frames
        .iter()
        .flat_map(|x| x.updated_actors.iter())
        .filter(|attr| attr.object_id == name_attribute_id)
        .filter_map(|attr| {
            // PlayerName will be a string attribute
            if let boxcars::Attribute::String(s) = &attr.attribute {
                Some(s)
            } else {
                None
            }
        })
        .collect::<Vec<_>>();

    // This list will contain many duplicates so we dedup it before returning.
    names.sort();
    names.dedup();
    names
}

/// This function looks up the object id for "Engine.PlayerReplicationInfo:PlayerName". The object
/// id is the same as the index of that value in the `replay.objects` array.
fn player_name_object_id(
    replay: &boxcars::Replay,
) -> Result<boxcars::ObjectId, Box<dyn error::Error>> {
    let id = replay
        .objects
        .iter()
        .position(|val| val == "Engine.PlayerReplicationInfo:PlayerName")<commit_msg>Prefer &str to &String in example<commit_after>//! This example of boxcars extracts all the player names found in the "PlayerStats" property of the
//! header. This property may be absent in some replays or lack players that drop or join mid-game.
//! A more foolproof approach is to scour the network data for a specific property:
//! "Engine.PlayerReplicationInfo:PlayerName". This example shows both methods. The error handling
//! demonstrated is minimal, relying on stringly typed errors. In practice, prefer a richer error
//! type.
use boxcars::{HeaderProp, ParserBuilder};
use std::error;
use std::io::{self, Read};

/// Given an array of objects (represented as a slice of key-value pairs), find all the instances
/// of the "Name" key and extract the string value
fn names_in_header(
    stats: &[Vec<(String, HeaderProp)>],
) -> impl Iterator<Item = &str> {
    stats
        .iter()
        .flat_map(|v| v.iter())
        .filter(|(prop_name, _)| *prop_name == "Name")
        .filter_map(|(_, prop_val)| prop_val.as_string())
}

/// Given network frames and the object id to "Engine.PlayerReplicationInfo:PlayerName", comb
/// through all the attributes looking for attributes that have our object id.
fn names_in_network(
    frames: &[boxcars::Frame],
    name_attribute_id: boxcars::ObjectId,
) -> Vec<&str> {
    let mut names = frames
        .iter()
        .flat_map(|x| x.updated_actors.iter())
        .filter(|attr| attr.object_id == name_attribute_id)
        .filter_map(|attr| {
            // PlayerName will be a string attribute
            if let boxcars::Attribute::String(ref s) = attr.attribute {
                Some(s.as_str())
            } else {
                None
            }
        })
        .collect::<Vec<_>>();

    // This list will contain many duplicates so we dedup it before returning.
    names.sort();
    names.dedup();
    names
}

/// This function looks up the object id for "Engine.PlayerReplicationInfo:PlayerName". The object
/// id is the same as the index of that value in the `replay.objects` array.
fn player_name_object_id(
    replay: &boxcars::Replay,
) -> Result<boxcars::ObjectId, Box<dyn error::Error>> {
    let id = replay
        .objects
        .iter()
        .position(|val| val == "Engine.PlayerReplicationInfo:PlayerName")
{'fraction_non_alphanumeric': 0.11714770797962648, 'fraction_numerical': 0.0, 'mean_word_length': 2.7825040128410916, 'pattern_counts': {'":': 0, '<': 17, '<?xml version=': 0, '>': 23, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '2833822', 'n_tokens_mistral': 1366, 'n_tokens_neox': 1317, 'n_words': 475}