// 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() } }