- LICENSE: GNU AGPL-3.0 (full text) - Copyright headers added to all 15 source files in src/ - COMMERCIAL_LICENSE.md: dual-license explanation (AGPL + commercial)
104 lines
3 KiB
Rust
104 lines
3 KiB
Rust
// 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 <https://www.gnu.org/licenses/>.
|
|
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<Mutex<VecDeque<WarningEntry>>>,
|
|
}
|
|
|
|
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<WarningEntry> {
|
|
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<WarningEntry> {
|
|
let guard = match self.entries.lock() {
|
|
Ok(g) => g,
|
|
Err(e) => e.into_inner(),
|
|
};
|
|
guard.back().cloned()
|
|
}
|
|
}
|