use crate::models::traits::{Sorter, Telemetry};
use crate::models::metrics::MetricsSnapshot;
use std::time::SystemTime;
#[derive(Debug, Clone)]
pub struct RaceSnapshot {
pub timestamp: SystemTime,
pub algorithm_snapshots: Vec<AlgorithmSnapshot>,
pub step: usize,
pub race_complete: bool,
}
#[derive(Debug, Clone)]
pub struct AlgorithmSnapshot {
pub name: String,
pub array_state: Vec<i32>,
pub telemetry: Telemetry,
pub is_complete: bool,
pub metrics: MetricsSnapshot,
}
#[derive(Debug, Default)]
pub struct SnapshotService {
snapshots: Vec<RaceSnapshot>,
max_snapshots: usize,
}
impl SnapshotService {
pub fn new(max_snapshots: usize) -> Self {
Self {
snapshots: Vec::new(),
max_snapshots: max_snapshots.max(1),
}
}
pub fn take_snapshot(&mut self, algorithms: &[Box<dyn Sorter>], step: usize) -> &RaceSnapshot {
let race_complete = algorithms.iter().all(|alg| alg.is_complete());
let algorithm_snapshots = algorithms
.iter()
.map(|algorithm| {
let telemetry = algorithm.get_telemetry();
let metrics = crate::models::metrics::Metrics {
comparisons: telemetry.total_comparisons,
moves: telemetry.total_moves,
execution_time_us: 0, peak_memory_bytes: telemetry.memory_peak,
current_memory_bytes: telemetry.memory_current,
steps: step,
array_accesses: telemetry.total_comparisons + telemetry.total_moves,
recursive_calls: 0, };
AlgorithmSnapshot {
name: algorithm.name().to_string(),
array_state: algorithm.get_array().to_vec(),
telemetry,
is_complete: algorithm.is_complete(),
metrics: MetricsSnapshot::new(
metrics,
SystemTime::now()
.duration_since(SystemTime::UNIX_EPOCH)
.unwrap()
.as_micros() as u64,
algorithm.name().to_string(),
),
}
})
.collect();
let snapshot = RaceSnapshot {
timestamp: SystemTime::now(),
algorithm_snapshots,
step,
race_complete,
};
self.snapshots.push(snapshot);
if self.snapshots.len() > self.max_snapshots {
self.snapshots.remove(0);
}
self.snapshots.last().unwrap()
}
pub fn get_snapshots(&self) -> &[RaceSnapshot] {
&self.snapshots
}
pub fn get_latest_snapshot(&self) -> Option<&RaceSnapshot> {
self.snapshots.last()
}
pub fn get_snapshot(&self, index: usize) -> Option<&RaceSnapshot> {
self.snapshots.get(index)
}
pub fn clear(&mut self) {
self.snapshots.clear();
}
pub fn len(&self) -> usize {
self.snapshots.len()
}
pub fn is_empty(&self) -> bool {
self.snapshots.is_empty()
}
pub fn set_max_snapshots(&mut self, max: usize) {
self.max_snapshots = max.max(1);
while self.snapshots.len() > self.max_snapshots {
self.snapshots.remove(0);
}
}
pub fn get_max_snapshots(&self) -> usize {
self.max_snapshots
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::services::sorters::bubble::BubbleSort;
#[test]
fn test_snapshot_service_creation() {
let service = SnapshotService::new(10);
assert_eq!(service.len(), 0);
assert!(service.is_empty());
assert_eq!(service.get_max_snapshots(), 10);
}
#[test]
fn test_take_snapshot() {
let mut service = SnapshotService::new(5);
let mut bubble = BubbleSort::new();
bubble.reset(vec![3, 1, 2]);
let algorithms: Vec<Box<dyn Sorter>> = vec![Box::new(bubble)];
let snapshot = service.take_snapshot(&algorithms, 1);
assert_eq!(snapshot.step, 1);
assert_eq!(snapshot.algorithm_snapshots.len(), 1);
assert_eq!(service.len(), 1);
}
#[test]
fn test_max_snapshots_limit() {
let mut service = SnapshotService::new(3);
let bubble = BubbleSort::new();
let algorithms: Vec<Box<dyn Sorter>> = vec![Box::new(bubble)];
for i in 0..5 {
service.take_snapshot(&algorithms, i);
}
assert_eq!(service.len(), 3);
let snapshots = service.get_snapshots();
assert_eq!(snapshots[0].step, 2);
assert_eq!(snapshots[1].step, 3);
assert_eq!(snapshots[2].step, 4);
}
}