Fix TUI borrow checker and generic type issues

This commit is contained in:
pandaadir05
2025-11-20 14:26:37 +02:00
parent 9d684cab19
commit 2f7eed4047
3 changed files with 70 additions and 57 deletions

View File

@@ -180,7 +180,8 @@ impl App {
let mut suspicious_count = 0; let mut suspicious_count = 0;
let mut malicious_count = 0; let mut malicious_count = 0;
for proc in &self.processes { let processes = self.processes.clone();
for proc in &processes {
if Self::should_skip_process(proc) { if Self::should_skip_process(proc) {
continue; continue;
} }

View File

@@ -20,14 +20,14 @@ use ratatui::{
use std::{ use std::{
collections::VecDeque, collections::VecDeque,
io, io,
sync::{Arc, Mutex}, sync::Arc,
time::{Duration, Instant}, time::{Duration, Instant},
}; };
use tokio::time; use tokio::{sync::Mutex, time};
mod app; mod app;
mod ui;
mod events; mod events;
mod ui;
use app::{App, TabIndex}; use app::{App, TabIndex};
@@ -78,15 +78,12 @@ async fn main() -> Result<()> {
Ok(()) Ok(())
} }
async fn run_app<B: Backend>( async fn run_app<B: Backend>(terminal: &mut Terminal<B>, app: Arc<Mutex<App>>) -> Result<()> {
terminal: &mut Terminal<B>,
app: Arc<Mutex<App>>,
) -> Result<()> {
loop { loop {
// Draw the UI // Draw the UI
terminal.draw(|f| { terminal.draw(|f| {
if let Ok(app) = app.try_lock() { if let Ok(app) = app.try_lock() {
ui::draw(f, &app); ui::draw::<CrosstermBackend<std::io::Stdout>>(f, &app);
} }
})?; })?;

View File

@@ -44,19 +44,20 @@ pub fn draw<B: Backend>(f: &mut Frame, app: &App) {
.split(size); .split(size);
// Draw header // Draw header
draw_header(f, chunks[0], app); draw_header::<B>(f, chunks[0], app);
// Draw main content based on selected tab // Draw main content based on selected tab
match app.current_tab { match app.current_tab {
TabIndex::Overview => draw_overview(f, chunks[1], app), TabIndex::Overview => draw_overview::<B>(f, chunks[1], app),
TabIndex::Processes => draw_processes(f, chunks[1], app), TabIndex::Processes => draw_processes::<B>(f, chunks[1], app),
TabIndex::Detections => draw_detections(f, chunks[1], app), TabIndex::Detections => draw_detections::<B>(f, chunks[1], app),
TabIndex::Memory => draw_memory(f, chunks[1], app), TabIndex::Memory => draw_memory::<B>(f, chunks[1], app),
TabIndex::Logs => draw_logs(f, chunks[1], app), TabIndex::Logs => draw_logs::<B>(f, chunks[1], app),
TabIndex::ThreatIntel => {} // TODO: Implement threat intel view
} }
// Draw footer // Draw footer
draw_footer(f, chunks[2], app); draw_footer::<B>(f, chunks[2], app);
} }
fn draw_header<B: Backend>(f: &mut Frame, area: Rect, app: &App) { fn draw_header<B: Backend>(f: &mut Frame, area: Rect, app: &App) {
@@ -83,11 +84,18 @@ fn draw_header<B: Backend>(f: &mut Frame, area: Rect, app: &App) {
fn draw_footer<B: Backend>(f: &mut Frame, area: Rect, app: &App) { fn draw_footer<B: Backend>(f: &mut Frame, area: Rect, app: &App) {
let help_text = match app.current_tab { let help_text = match app.current_tab {
TabIndex::Overview => "Up/Down: Navigate | Tab: Switch tabs | R: Refresh | C: Clear | Q: Quit", TabIndex::Overview => {
TabIndex::Processes => "Up/Down: Select process | Enter: View details | Tab: Switch tabs | Q: Quit", "Up/Down: Navigate | Tab: Switch tabs | R: Refresh | C: Clear | Q: Quit"
TabIndex::Detections => "Up/Down: Navigate detections | C: Clear history | Tab: Switch tabs | Q: Quit", }
TabIndex::Processes => {
"Up/Down: Select process | Enter: View details | Tab: Switch tabs | Q: Quit"
}
TabIndex::Detections => {
"Up/Down: Navigate detections | C: Clear history | Tab: Switch tabs | Q: Quit"
}
TabIndex::Memory => "Up/Down: Navigate | Tab: Switch tabs | R: Refresh | Q: Quit", TabIndex::Memory => "Up/Down: Navigate | Tab: Switch tabs | R: Refresh | Q: Quit",
TabIndex::Logs => "Up/Down: Navigate logs | C: Clear logs | Tab: Switch tabs | Q: Quit", TabIndex::Logs => "Up/Down: Navigate logs | C: Clear logs | Tab: Switch tabs | Q: Quit",
TabIndex::ThreatIntel => "Up/Down: Navigate threats | Tab: Switch tabs | Q: Quit",
}; };
let footer = Paragraph::new(help_text) let footer = Paragraph::new(help_text)
@@ -106,20 +114,20 @@ fn draw_overview<B: Backend>(f: &mut Frame, area: Rect, app: &App) {
let chunks = Layout::default() let chunks = Layout::default()
.direction(Direction::Vertical) .direction(Direction::Vertical)
.constraints([ .constraints([
Constraint::Length(8), // Stats Constraint::Length(8), // Stats
Constraint::Length(8), // Threat level gauge Constraint::Length(8), // Threat level gauge
Constraint::Min(0), // Recent detections Constraint::Min(0), // Recent detections
]) ])
.split(area); .split(area);
// Statistics panel // Statistics panel
draw_stats_panel(f, chunks[0], app); draw_stats_panel::<B>(f, chunks[0], app);
// Threat level gauge // Threat level gauge
draw_threat_gauge(f, chunks[1], app); draw_threat_gauge::<B>(f, chunks[1], app);
// Recent detections // Recent detections
draw_recent_detections(f, chunks[2], app); draw_recent_detections::<B>(f, chunks[2], app);
} }
fn draw_stats_panel<B: Backend>(f: &mut Frame, area: Rect, app: &App) { fn draw_stats_panel<B: Backend>(f: &mut Frame, area: Rect, app: &App) {
@@ -280,24 +288,28 @@ fn draw_processes<B: Backend>(f: &mut Frame, area: Rect, app: &App) {
let header = Row::new(header_cells).height(1).bottom_margin(1); let header = Row::new(header_cells).height(1).bottom_margin(1);
let rows: Vec<Row> = app.processes.iter().map(|proc| { let rows: Vec<Row> = app
let status = match app.detections.iter().find(|d| d.process.pid == proc.pid) { .processes
Some(detection) => match detection.threat_level { .iter()
ThreatLevel::Malicious => "MALICIOUS", .map(|proc| {
ThreatLevel::Suspicious => "SUSPICIOUS", let status = match app.detections.iter().find(|d| d.process.pid == proc.pid) {
ThreatLevel::Clean => "CLEAN", Some(detection) => match detection.threat_level {
}, ThreatLevel::Malicious => "MALICIOUS",
None => "CLEAN", ThreatLevel::Suspicious => "SUSPICIOUS",
}; ThreatLevel::Clean => "CLEAN",
},
None => "CLEAN",
};
Row::new(vec![ Row::new(vec![
Cell::from(proc.pid.to_string()), Cell::from(proc.pid.to_string()),
Cell::from(proc.ppid.to_string()), Cell::from(proc.ppid.to_string()),
Cell::from(proc.name.as_str()), Cell::from(proc.name.as_str()),
Cell::from(proc.thread_count.to_string()), Cell::from(proc.thread_count.to_string()),
Cell::from(status), Cell::from(status),
]) ])
}).collect(); })
.collect();
let table = Table::new(rows) let table = Table::new(rows)
.header(header) .header(header)
@@ -320,7 +332,7 @@ fn draw_processes<B: Backend>(f: &mut Frame, area: Rect, app: &App) {
f.render_stateful_widget(table, chunks[0], &mut state); f.render_stateful_widget(table, chunks[0], &mut state);
// Process details panel // Process details panel
draw_process_details(f, chunks[1], app); draw_process_details::<B>(f, chunks[1], app);
} }
fn draw_process_details<B: Backend>(f: &mut Frame, area: Rect, app: &App) { fn draw_process_details<B: Backend>(f: &mut Frame, area: Rect, app: &App) {
@@ -394,7 +406,10 @@ fn draw_detections<B: Backend>(f: &mut Frame, area: Rect, app: &App) {
.block( .block(
Block::default() Block::default()
.borders(Borders::ALL) .borders(Borders::ALL)
.title(format!("Detection History ({} total)", app.detections.len())) .title(format!(
"Detection History ({} total)",
app.detections.len()
))
.border_style(Style::default().fg(DANGER)), .border_style(Style::default().fg(DANGER)),
) )
.style(Style::default().fg(TEXT)); .style(Style::default().fg(TEXT));