Bug Fixes:
- Fixed KSUDP binary parser offsets from packed (323B) to natural C
alignment (328B) — 3 bytes padding after char identifier, 2 bytes
after 6 bools
- Fixed setup_terminal() leaving terminal in raw mode on early exit
New Features:
- Thread-safe warning buffer (warnings.rs) with TUI bar + stderr fallback
- Rewrote shift point analysis per AC forum methodology: per-gear
acceleration curves, crossover detection, same-gear merging
- UseCase::guidance() — step-by-step recording instructions
- Recording metadata enrichment in analysis responses
- recording_id field on sessions
Code Quality:
- Extracted finalize_run() helper (session.rs)
- Extracted execute_and_store_analysis() helper (api.rs)
- Extracted lerp_search<T>() generic helper (analysis.rs)
Breaking API Changes:
- GET /api/use-cases: string[] -> [{id, name, guidance}[]]
- Shift point analysis: gear_ranges/shift_events -> gear_curves/crossover_points
Updated README.md with new API response examples and TUI warning bar.
89 lines
2.2 KiB
Rust
89 lines
2.2 KiB
Rust
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()
|
|
}
|
|
}
|