Skip to main content

subtr_actor/collector/
mod.rs

1//! The output layer: turn a processed replay into structured data, numeric
2//! features, or stat timelines.
3//!
4//! Everything here is built on the [`Collector`] trait — the core extension
5//! point. A collector observes the replay frame by frame (driven by a
6//! [`ReplayProcessor`]) and decides the sampling pace
7//! via the [`TimeAdvance`] it returns. Run one with
8//! [`Collector::process_replay`], or share a single pass across several with
9//! [`ReplayProcessor::process_all`](crate::ReplayProcessor::process_all).
10//!
11//! # Built-in collectors
12//!
13//! - [`ReplayDataCollector`] — a serde-friendly payload of frame data, metadata,
14//!   and derived event streams for JSON export and playback UIs.
15//! - [`NDArrayCollector`] — a dense numeric feature matrix for ML; see the
16//!   [`ndarray`] submodule for the feature-adder system.
17//! - Stats collectors in [`stats`] — graph-backed accumulated stats; see also
18//!   [`crate::stats::timeline`] for cumulative-over-time stat timelines.
19//!
20//! # Wrappers
21//!
22//! - [`FrameRateDecorator`] downsamples any collector to a target FPS.
23//! - [`CallbackCollector`] attaches side-effecting hooks (progress, debugging)
24//!   to a collector.
25
26pub mod callback;
27pub mod decorator;
28pub(crate) mod frame_resolution;
29pub mod ndarray;
30pub mod replay_data;
31pub mod stats;
32
33pub use self::ndarray::*;
34pub use callback::*;
35pub use decorator::*;
36pub use frame_resolution::StatsFrameResolution;
37pub use replay_data::*;
38pub use stats::*;
39
40use crate::*;
41use boxcars;
42
43/// Enum used to control the progress of time during replay processing.
44pub enum TimeAdvance {
45    /// Move forward in time by a specified amount.
46    Time(f32),
47    /// Advance to the next frame.
48    NextFrame,
49}
50
51/// Trait for types that collect data from a replay.
52///
53/// A `Collector` processes frames from a replay, potentially using a
54/// [`ReplayProcessor`] for access to additional replay data and context. It
55/// determines the pace of replay progression via the [`TimeAdvance`] return
56/// value.
57pub trait Collector {
58    /// Process a single frame from a replay.
59    ///
60    /// # Arguments
61    ///
62    /// * `processor` - The [`ReplayProcessor`] providing context for the replay.
63    /// * `frame` - The [`boxcars::Frame`] to process.
64    /// * `frame_number` - The number of the current frame.
65    /// * `current_time` - The current target time in the replay.
66    ///
67    /// # Returns
68    ///
69    /// Returns a [`TimeAdvance`] enum which determines the next step in replay
70    /// progression.
71    fn process_frame(
72        &mut self,
73        processor: &dyn ProcessorView,
74        frame: &boxcars::Frame,
75        frame_number: usize,
76        current_time: f32,
77    ) -> SubtrActorResult<TimeAdvance>;
78
79    /// Process an entire replay.
80    ///
81    /// # Arguments
82    ///
83    /// * `replay` - The [`boxcars::Replay`] to process.
84    ///
85    /// # Returns
86    ///
87    /// Returns the [`Collector`] itself, potentially modified by the processing
88    /// of the replay.
89    fn process_replay(mut self, replay: &boxcars::Replay) -> SubtrActorResult<Self>
90    where
91        Self: Sized,
92    {
93        ReplayProcessor::new(replay)?.process(&mut self)?;
94        Ok(self)
95    }
96
97    /// Finalize replay-derived state after the last frame has been processed.
98    ///
99    /// Collectors that aggregate state across frame boundaries can override
100    /// this to flush any in-progress segment once replay traversal is complete.
101    fn finish_replay(&mut self, _processor: &dyn ProcessorView) -> SubtrActorResult<()> {
102        Ok(())
103    }
104}
105
106impl<G> Collector for G
107where
108    G: FnMut(&dyn ProcessorView, &boxcars::Frame, usize, f32) -> SubtrActorResult<TimeAdvance>,
109{
110    fn process_frame(
111        &mut self,
112        processor: &dyn ProcessorView,
113        frame: &boxcars::Frame,
114        frame_number: usize,
115        current_time: f32,
116    ) -> SubtrActorResult<TimeAdvance> {
117        self(processor, frame, frame_number, current_time)
118    }
119}