// rusty-telemetry — Sim racing telemetry analysis platform
// Copyright (C) 2026 Jan-Peter v. Hunnius
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see .
use std::collections::VecDeque;
use std::sync::{Arc, Mutex};
use std::time::Instant;
const MAX_WARNINGS: usize = 50;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum WarningLevel {
Info,
Warn,
Error,
}
#[derive(Debug, Clone)]
pub struct WarningEntry {
pub message: String,
pub timestamp: Instant,
pub level: WarningLevel,
}
#[derive(Clone)]
pub struct Warnings {
entries: Arc>>,
}
impl Warnings {
pub fn new() -> Self {
Self {
entries: Arc::new(Mutex::new(VecDeque::with_capacity(MAX_WARNINGS))),
}
}
fn push(&self, level: WarningLevel, msg: &str) {
match level {
WarningLevel::Error => eprintln!("[ERROR] {}", msg),
WarningLevel::Warn => eprintln!("[WARN] {}", msg),
WarningLevel::Info => {}
}
let mut guard = match self.entries.lock() {
Ok(g) => g,
Err(e) => e.into_inner(),
};
if guard.len() >= MAX_WARNINGS {
guard.pop_front();
}
guard.push_back(WarningEntry {
message: msg.to_string(),
timestamp: Instant::now(),
level,
});
}
pub fn warn(&self, msg: &str) {
self.push(WarningLevel::Warn, msg);
}
pub fn info(&self, msg: &str) {
self.push(WarningLevel::Info, msg);
}
pub fn error(&self, msg: &str) {
self.push(WarningLevel::Error, msg);
}
/// Returns warnings from the last `max_age_secs` seconds.
/// Currently unused but kept for potential future TUI scrolling.
#[allow(dead_code)]
pub fn recent(&self, max_age_secs: u64) -> Vec {
let guard = match self.entries.lock() {
Ok(g) => g,
Err(e) => e.into_inner(),
};
let cutoff = Instant::now() - std::time::Duration::from_secs(max_age_secs);
guard
.iter()
.filter(|e| e.timestamp >= cutoff)
.cloned()
.collect()
}
pub fn latest(&self) -> Option {
let guard = match self.entries.lock() {
Ok(g) => g,
Err(e) => e.into_inner(),
};
guard.back().cloned()
}
}