Fix all clippy lints and pass CI checks

- Box large enum variants in EventData to reduce memory footprint
- Add Default trait implementations for types with new() methods
- Replace or_insert_with(Vec::new) with or_default()
- Convert vec init+push patterns to vec! macro
- Fix field reassignment with default initialization
- Convert match to if for simple equality checks
- Remove unused Backend type parameters from TUI draw functions
- Apply rustfmt formatting

All tests passing (24 total). Zero clippy warnings. Ready for CI/CD.
This commit is contained in:
pandaadir05
2025-11-20 15:13:16 +02:00
parent 27dffbd108
commit 655585d9ef
7 changed files with 187 additions and 166 deletions

View File

@@ -75,11 +75,11 @@ pub enum EventSeverity {
#[derive(Debug, Clone, Serialize, Deserialize)] #[derive(Debug, Clone, Serialize, Deserialize)]
pub enum EventData { pub enum EventData {
Detection(DetectionEventData), Detection(Box<DetectionEventData>),
ThreatIntel(ThreatIntelEventData), ThreatIntel(Box<ThreatIntelEventData>),
Evasion(EvasionEventData), Evasion(EvasionEventData),
System(SystemEventData), System(SystemEventData),
Alert(AlertEventData), Alert(Box<AlertEventData>),
Performance(PerformanceEventData), Performance(PerformanceEventData),
} }
@@ -559,11 +559,11 @@ impl EventStreamingSystem {
ThreatLevel::Suspicious => EventSeverity::Medium, ThreatLevel::Suspicious => EventSeverity::Medium,
ThreatLevel::Malicious => EventSeverity::High, ThreatLevel::Malicious => EventSeverity::High,
}, },
data: EventData::Detection(DetectionEventData { data: EventData::Detection(Box::new(DetectionEventData {
detection_result: detection, detection_result: detection,
analysis_duration: Duration::from_millis(100), // Would be actual duration analysis_duration: Duration::from_millis(100), // Would be actual duration
confidence_threshold: 0.7, confidence_threshold: 0.7,
}), })),
correlation_id: None, correlation_id: None,
tags: vec!["process-injection".to_string(), "detection".to_string()], tags: vec!["process-injection".to_string(), "detection".to_string()],
}; };

View File

@@ -398,80 +398,78 @@ impl TestFramework {
/// Create detection engine unit tests /// Create detection engine unit tests
fn create_detection_engine_tests(&mut self) { fn create_detection_engine_tests(&mut self) {
let mut test_cases = Vec::new(); let test_cases = vec![
TestCase {
// Test clean process detection name: "clean_process_detection".to_string(),
test_cases.push(TestCase { description: "Verify clean processes are not flagged".to_string(),
name: "clean_process_detection".to_string(), test_function: TestFunction::DetectionTest(DetectionTestParams {
description: "Verify clean processes are not flagged".to_string(), process_data: ProcessTestData {
test_function: TestFunction::DetectionTest(DetectionTestParams { name: "notepad.exe".to_string(),
process_data: ProcessTestData { pid: 1234,
name: "notepad.exe".to_string(), path: Some("C:\\Windows\\System32\\notepad.exe".to_string()),
pid: 1234, thread_count: 1,
path: Some("C:\\Windows\\System32\\notepad.exe".to_string()), suspicious_indicators: Vec::new(),
thread_count: 1, },
suspicious_indicators: Vec::new(), memory_data: vec![MemoryTestData {
}, base_address: 0x400000,
memory_data: vec![MemoryTestData { size: 0x10000,
base_address: 0x400000, protection: MemoryProtection::ReadExecute,
size: 0x10000, contains_shellcode: false,
protection: MemoryProtection::ReadExecute, shellcode_pattern: None,
contains_shellcode: false, }],
shellcode_pattern: None, thread_data: vec![ThreadTestData {
}], tid: 5678,
thread_data: vec![ThreadTestData { entry_point: 0x401000,
tid: 5678, stack_base: 0x500000,
entry_point: 0x401000, stack_size: 0x10000,
stack_base: 0x500000, is_suspicious: false,
stack_size: 0x10000, }],
is_suspicious: false, injection_type: None,
}], }),
injection_type: None, expected_result: ExpectedResult::ThreatLevel(ThreatLevel::Clean),
}), timeout: Duration::from_secs(5),
expected_result: ExpectedResult::ThreatLevel(ThreatLevel::Clean), tags: vec!["unit".to_string(), "detection".to_string()],
timeout: Duration::from_secs(5), },
tags: vec!["unit".to_string(), "detection".to_string()], // Test malicious process detection
}); TestCase {
name: "malicious_process_detection".to_string(),
// Test malicious process detection description: "Verify malicious processes are properly detected".to_string(),
test_cases.push(TestCase { test_function: TestFunction::DetectionTest(DetectionTestParams {
name: "malicious_process_detection".to_string(), process_data: ProcessTestData {
description: "Verify malicious processes are properly detected".to_string(), name: "malware.exe".to_string(),
test_function: TestFunction::DetectionTest(DetectionTestParams { pid: 9999,
process_data: ProcessTestData { path: Some("C:\\Temp\\malware.exe".to_string()),
name: "malware.exe".to_string(), thread_count: 5,
pid: 9999, suspicious_indicators: vec![
path: Some("C:\\Temp\\malware.exe".to_string()), "High RWX memory usage".to_string(),
thread_count: 5, "Suspicious API calls".to_string(),
suspicious_indicators: vec![ ],
"High RWX memory usage".to_string(), },
"Suspicious API calls".to_string(), memory_data: vec![MemoryTestData {
], base_address: 0x200000,
}, size: 0x1000,
memory_data: vec![MemoryTestData { protection: MemoryProtection::ReadWriteExecute,
base_address: 0x200000, contains_shellcode: true,
size: 0x1000, shellcode_pattern: Some(vec![0x90, 0x90, 0xEB, 0xFE]), // NOP NOP JMP -2
protection: MemoryProtection::ReadWriteExecute, }],
contains_shellcode: true, thread_data: vec![ThreadTestData {
shellcode_pattern: Some(vec![0x90, 0x90, 0xEB, 0xFE]), // NOP NOP JMP -2 tid: 1111,
}], entry_point: 0x200000,
thread_data: vec![ThreadTestData { stack_base: 0x600000,
tid: 1111, stack_size: 0x10000,
entry_point: 0x200000, is_suspicious: true,
stack_base: 0x600000, }],
stack_size: 0x10000, injection_type: Some(InjectionTestType::ShellcodeInjection),
is_suspicious: true, }),
}], expected_result: ExpectedResult::ThreatLevel(ThreatLevel::Malicious),
injection_type: Some(InjectionTestType::ShellcodeInjection), timeout: Duration::from_secs(10),
}), tags: vec![
expected_result: ExpectedResult::ThreatLevel(ThreatLevel::Malicious), "unit".to_string(),
timeout: Duration::from_secs(10), "detection".to_string(),
tags: vec![ "malware".to_string(),
"unit".to_string(), ],
"detection".to_string(), },
"malware".to_string(), ];
],
});
let test_suite = TestSuite { let test_suite = TestSuite {
name: "detection_engine_tests".to_string(), name: "detection_engine_tests".to_string(),
@@ -487,10 +485,7 @@ impl TestFramework {
/// Create shellcode detection tests /// Create shellcode detection tests
fn create_shellcode_detection_tests(&mut self) { fn create_shellcode_detection_tests(&mut self) {
let mut test_cases = Vec::new(); let test_cases = vec![TestCase {
// Test common shellcode patterns
test_cases.push(TestCase {
name: "common_shellcode_patterns".to_string(), name: "common_shellcode_patterns".to_string(),
description: "Detect common shellcode patterns".to_string(), description: "Detect common shellcode patterns".to_string(),
test_function: TestFunction::DetectionTest(DetectionTestParams { test_function: TestFunction::DetectionTest(DetectionTestParams {
@@ -519,7 +514,7 @@ impl TestFramework {
expected_result: ExpectedResult::IndicatorPresent("Shellcode detected".to_string()), expected_result: ExpectedResult::IndicatorPresent("Shellcode detected".to_string()),
timeout: Duration::from_secs(5), timeout: Duration::from_secs(5),
tags: vec!["unit".to_string(), "shellcode".to_string()], tags: vec!["unit".to_string(), "shellcode".to_string()],
}); }];
let test_suite = TestSuite { let test_suite = TestSuite {
name: "shellcode_detection_tests".to_string(), name: "shellcode_detection_tests".to_string(),
@@ -535,9 +530,7 @@ impl TestFramework {
/// Create process hollowing detection tests /// Create process hollowing detection tests
fn create_process_hollowing_tests(&mut self) { fn create_process_hollowing_tests(&mut self) {
let mut test_cases = Vec::new(); let test_cases = vec![TestCase {
test_cases.push(TestCase {
name: "process_hollowing_detection".to_string(), name: "process_hollowing_detection".to_string(),
description: "Detect process hollowing techniques".to_string(), description: "Detect process hollowing techniques".to_string(),
test_function: TestFunction::DetectionTest(DetectionTestParams { test_function: TestFunction::DetectionTest(DetectionTestParams {
@@ -564,7 +557,7 @@ impl TestFramework {
expected_result: ExpectedResult::IndicatorPresent("Process hollowing".to_string()), expected_result: ExpectedResult::IndicatorPresent("Process hollowing".to_string()),
timeout: Duration::from_secs(10), timeout: Duration::from_secs(10),
tags: vec!["unit".to_string(), "hollowing".to_string()], tags: vec!["unit".to_string(), "hollowing".to_string()],
}); }];
let test_suite = TestSuite { let test_suite = TestSuite {
name: "process_hollowing_tests".to_string(), name: "process_hollowing_tests".to_string(),
@@ -580,9 +573,7 @@ impl TestFramework {
/// Create evasion detection tests /// Create evasion detection tests
fn create_evasion_detection_tests(&mut self) { fn create_evasion_detection_tests(&mut self) {
let mut test_cases = Vec::new(); let test_cases = vec![TestCase {
test_cases.push(TestCase {
name: "anti_debug_detection".to_string(), name: "anti_debug_detection".to_string(),
description: "Detect anti-debugging techniques".to_string(), description: "Detect anti-debugging techniques".to_string(),
test_function: TestFunction::DetectionTest(DetectionTestParams { test_function: TestFunction::DetectionTest(DetectionTestParams {
@@ -603,7 +594,7 @@ impl TestFramework {
expected_result: ExpectedResult::IndicatorPresent("Evasion technique".to_string()), expected_result: ExpectedResult::IndicatorPresent("Evasion technique".to_string()),
timeout: Duration::from_secs(15), timeout: Duration::from_secs(15),
tags: vec!["unit".to_string(), "evasion".to_string()], tags: vec!["unit".to_string(), "evasion".to_string()],
}); }];
let test_suite = TestSuite { let test_suite = TestSuite {
name: "evasion_detection_tests".to_string(), name: "evasion_detection_tests".to_string(),
@@ -625,39 +616,38 @@ impl TestFramework {
/// Create performance benchmark tests /// Create performance benchmark tests
fn create_performance_tests(&mut self) { fn create_performance_tests(&mut self) {
let mut benchmarks = Vec::new(); let benchmarks = vec![
Benchmark {
benchmarks.push(Benchmark { name: "single_process_analysis".to_string(),
name: "single_process_analysis".to_string(), description: "Benchmark single process analysis performance".to_string(),
description: "Benchmark single process analysis performance".to_string(), benchmark_function: BenchmarkFunction::ProcessAnalysis(ProcessAnalysisBenchmark {
benchmark_function: BenchmarkFunction::ProcessAnalysis(ProcessAnalysisBenchmark { process_count: 1,
process_count: 1, complexity_level: ComplexityLevel::Moderate,
complexity_level: ComplexityLevel::Moderate, }),
}), warm_up_iterations: 10,
warm_up_iterations: 10, measurement_iterations: 100,
measurement_iterations: 100, target_metrics: vec![
target_metrics: vec![ PerformanceMetric::ExecutionTime,
PerformanceMetric::ExecutionTime, PerformanceMetric::MemoryUsage,
PerformanceMetric::MemoryUsage, PerformanceMetric::CPUUtilization,
PerformanceMetric::CPUUtilization, ],
], },
}); Benchmark {
name: "bulk_process_analysis".to_string(),
benchmarks.push(Benchmark { description: "Benchmark bulk process analysis performance".to_string(),
name: "bulk_process_analysis".to_string(), benchmark_function: BenchmarkFunction::ProcessAnalysis(ProcessAnalysisBenchmark {
description: "Benchmark bulk process analysis performance".to_string(), process_count: 100,
benchmark_function: BenchmarkFunction::ProcessAnalysis(ProcessAnalysisBenchmark { complexity_level: ComplexityLevel::Simple,
process_count: 100, }),
complexity_level: ComplexityLevel::Simple, warm_up_iterations: 5,
}), measurement_iterations: 20,
warm_up_iterations: 5, target_metrics: vec![
measurement_iterations: 20, PerformanceMetric::ThroughputRate,
target_metrics: vec![ PerformanceMetric::LatencyP95,
PerformanceMetric::ThroughputRate, PerformanceMetric::MemoryUsage,
PerformanceMetric::LatencyP95, ],
PerformanceMetric::MemoryUsage, },
], ];
});
let benchmark_suite = BenchmarkSuite { let benchmark_suite = BenchmarkSuite {
name: "performance_benchmarks".to_string(), name: "performance_benchmarks".to_string(),

View File

@@ -296,6 +296,12 @@ pub struct CertificateInfo {
pub valid_to: SystemTime, pub valid_to: SystemTime,
} }
impl Default for ThreatIntelligence {
fn default() -> Self {
Self::new()
}
}
impl ThreatIntelligence { impl ThreatIntelligence {
pub fn new() -> Self { pub fn new() -> Self {
Self { Self {
@@ -551,6 +557,12 @@ impl ThreatIntelligence {
} }
} }
impl Default for IocDatabase {
fn default() -> Self {
Self::new()
}
}
impl IocDatabase { impl IocDatabase {
pub fn new() -> Self { pub fn new() -> Self {
Self { Self {
@@ -570,13 +582,13 @@ impl IocDatabase {
IocType::FileHash => { IocType::FileHash => {
self.hash_index self.hash_index
.entry(ioc.value.clone()) .entry(ioc.value.clone())
.or_insert_with(Vec::new) .or_default()
.push(ioc.id.clone()); .push(ioc.id.clone());
} }
IocType::MemorySignature | IocType::BehaviorPattern => { IocType::MemorySignature | IocType::BehaviorPattern => {
self.pattern_index self.pattern_index
.entry(ioc.value.clone()) .entry(ioc.value.clone())
.or_insert_with(Vec::new) .or_default()
.push(ioc.id.clone()); .push(ioc.id.clone());
} }
_ => {} _ => {}
@@ -637,6 +649,12 @@ impl IocDatabase {
} }
} }
impl Default for AttributionEngine {
fn default() -> Self {
Self::new()
}
}
impl AttributionEngine { impl AttributionEngine {
pub fn new() -> Self { pub fn new() -> Self {
Self { Self {
@@ -725,6 +743,12 @@ impl AttributionEngine {
} }
} }
impl Default for SimilarityCalculator {
fn default() -> Self {
Self::new()
}
}
impl SimilarityCalculator { impl SimilarityCalculator {
pub fn new() -> Self { pub fn new() -> Self {
Self { Self {
@@ -735,6 +759,12 @@ impl SimilarityCalculator {
} }
} }
impl Default for ReputationCache {
fn default() -> Self {
Self::new()
}
}
impl ReputationCache { impl ReputationCache {
pub fn new() -> Self { pub fn new() -> Self {
Self { Self {

View File

@@ -139,8 +139,10 @@ mod tests {
let config = DetectionConfig::default(); let config = DetectionConfig::default();
assert!(config.validate().is_ok()); assert!(config.validate().is_ok());
let mut invalid_config = DetectionConfig::default(); let mut invalid_config = DetectionConfig {
invalid_config.confidence_threshold = 1.5; // Invalid confidence_threshold: 1.5, // Invalid
..Default::default()
};
assert!(invalid_config.validate().is_err()); assert!(invalid_config.validate().is_err());
invalid_config.confidence_threshold = -0.1; // Invalid invalid_config.confidence_threshold = -0.1; // Invalid
@@ -165,8 +167,10 @@ mod tests {
#[test] #[test]
fn test_engine_with_custom_config() { fn test_engine_with_custom_config() {
let mut config = DetectionConfig::default(); let config = DetectionConfig {
config.hook_detection = false; hook_detection: false,
..Default::default()
};
let mut engine = let mut engine =
DetectionEngine::with_config(Some(config)).expect("Failed to create engine"); DetectionEngine::with_config(Some(config)).expect("Failed to create engine");

View File

@@ -379,19 +379,16 @@ impl App {
} }
pub fn select_item(&mut self) { pub fn select_item(&mut self) {
match self.current_tab { if self.current_tab == TabIndex::Processes {
TabIndex::Processes => { if let Some(i) = self.processes_state.selected() {
if let Some(i) = self.processes_state.selected() { if let Some(process) = self.processes.get(i) {
if let Some(process) = self.processes.get(i) { self.selected_process = Some(process.clone());
self.selected_process = Some(process.clone()); self.add_log_message(format!(
self.add_log_message(format!( "Selected process: {} (PID: {})",
"Selected process: {} (PID: {})", process.name, process.pid
process.name, process.pid ));
));
}
} }
} }
_ => {}
} }
} }

View File

@@ -69,7 +69,7 @@ async fn run_app<B: Backend>(terminal: &mut Terminal<B>, app: Arc<Mutex<App>>) -
// 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::<CrosstermBackend<std::io::Stdout>>(f, &app); ui::draw(f, &app);
} }
})?; })?;

View File

@@ -32,7 +32,7 @@ mod colors {
use colors::*; use colors::*;
pub fn draw<B: Backend>(f: &mut Frame, app: &App) { pub fn draw(f: &mut Frame, app: &App) {
let size = f.size(); let size = f.size();
// Create main layout // Create main layout
@@ -46,23 +46,23 @@ pub fn draw<B: Backend>(f: &mut Frame, app: &App) {
.split(size); .split(size);
// Draw header // Draw header
draw_header::<B>(f, chunks[0], app); draw_header(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::<B>(f, chunks[1], app), TabIndex::Overview => draw_overview(f, chunks[1], app),
TabIndex::Processes => draw_processes::<B>(f, chunks[1], app), TabIndex::Processes => draw_processes(f, chunks[1], app),
TabIndex::Detections => draw_detections::<B>(f, chunks[1], app), TabIndex::Detections => draw_detections(f, chunks[1], app),
TabIndex::Memory => draw_memory::<B>(f, chunks[1], app), TabIndex::Memory => draw_memory(f, chunks[1], app),
TabIndex::Logs => draw_logs::<B>(f, chunks[1], app), TabIndex::Logs => draw_logs(f, chunks[1], app),
TabIndex::ThreatIntel => {} // TODO: Implement threat intel view TabIndex::ThreatIntel => {} // TODO: Implement threat intel view
} }
// Draw footer // Draw footer
draw_footer::<B>(f, chunks[2], app); draw_footer(f, chunks[2], app);
} }
fn draw_header<B: Backend>(f: &mut Frame, area: Rect, app: &App) { fn draw_header(f: &mut Frame, area: Rect, app: &App) {
let titles = app.get_tab_titles(); let titles = app.get_tab_titles();
let tabs = Tabs::new(titles) let tabs = Tabs::new(titles)
.block( .block(
@@ -84,7 +84,7 @@ fn draw_header<B: Backend>(f: &mut Frame, area: Rect, app: &App) {
f.render_widget(tabs, area); f.render_widget(tabs, area);
} }
fn draw_footer<B: Backend>(f: &mut Frame, area: Rect, app: &App) { fn draw_footer(f: &mut Frame, area: Rect, app: &App) {
let help_text = match app.current_tab { let help_text = match app.current_tab {
TabIndex::Overview => { TabIndex::Overview => {
"Up/Down: Navigate | Tab: Switch tabs | R: Refresh | C: Clear | Q: Quit" "Up/Down: Navigate | Tab: Switch tabs | R: Refresh | C: Clear | Q: Quit"
@@ -112,7 +112,7 @@ fn draw_footer<B: Backend>(f: &mut Frame, area: Rect, app: &App) {
f.render_widget(footer, area); f.render_widget(footer, area);
} }
fn draw_overview<B: Backend>(f: &mut Frame, area: Rect, app: &App) { fn draw_overview(f: &mut Frame, area: Rect, app: &App) {
let chunks = Layout::default() let chunks = Layout::default()
.direction(Direction::Vertical) .direction(Direction::Vertical)
.constraints([ .constraints([
@@ -123,16 +123,16 @@ fn draw_overview<B: Backend>(f: &mut Frame, area: Rect, app: &App) {
.split(area); .split(area);
// Statistics panel // Statistics panel
draw_stats_panel::<B>(f, chunks[0], app); draw_stats_panel(f, chunks[0], app);
// Threat level gauge // Threat level gauge
draw_threat_gauge::<B>(f, chunks[1], app); draw_threat_gauge(f, chunks[1], app);
// Recent detections // Recent detections
draw_recent_detections::<B>(f, chunks[2], app); draw_recent_detections(f, chunks[2], app);
} }
fn draw_stats_panel<B: Backend>(f: &mut Frame, area: Rect, app: &App) { fn draw_stats_panel(f: &mut Frame, area: Rect, app: &App) {
let stats_chunks = Layout::default() let stats_chunks = Layout::default()
.direction(Direction::Horizontal) .direction(Direction::Horizontal)
.constraints([ .constraints([
@@ -204,7 +204,7 @@ fn draw_stats_panel<B: Backend>(f: &mut Frame, area: Rect, app: &App) {
f.render_widget(perf_gauge, stats_chunks[3]); f.render_widget(perf_gauge, stats_chunks[3]);
} }
fn draw_threat_gauge<B: Backend>(f: &mut Frame, area: Rect, app: &App) { fn draw_threat_gauge(f: &mut Frame, area: Rect, app: &App) {
let threat_level = if app.stats.malicious_processes > 0 { let threat_level = if app.stats.malicious_processes > 0 {
100 100
} else if app.stats.suspicious_processes > 0 { } else if app.stats.suspicious_processes > 0 {
@@ -239,7 +239,7 @@ fn draw_threat_gauge<B: Backend>(f: &mut Frame, area: Rect, app: &App) {
f.render_widget(threat_gauge, area); f.render_widget(threat_gauge, area);
} }
fn draw_recent_detections<B: Backend>(f: &mut Frame, area: Rect, app: &App) { fn draw_recent_detections(f: &mut Frame, area: Rect, app: &App) {
let items: Vec<ListItem> = app let items: Vec<ListItem> = app
.detections .detections
.iter() .iter()
@@ -277,7 +277,7 @@ fn draw_recent_detections<B: Backend>(f: &mut Frame, area: Rect, app: &App) {
f.render_widget(list, area); f.render_widget(list, area);
} }
fn draw_processes<B: Backend>(f: &mut Frame, area: Rect, app: &App) { fn draw_processes(f: &mut Frame, area: Rect, app: &App) {
let chunks = Layout::default() let chunks = Layout::default()
.direction(Direction::Horizontal) .direction(Direction::Horizontal)
.constraints([Constraint::Percentage(70), Constraint::Percentage(30)]) .constraints([Constraint::Percentage(70), Constraint::Percentage(30)])
@@ -334,10 +334,10 @@ 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::<B>(f, chunks[1], app); draw_process_details(f, chunks[1], app);
} }
fn draw_process_details<B: Backend>(f: &mut Frame, area: Rect, app: &App) { fn draw_process_details(f: &mut Frame, area: Rect, app: &App) {
let details = if let Some(ref process) = app.selected_process { let details = if let Some(ref process) = app.selected_process {
format!( format!(
"PID: {}\nPPID: {}\nName: {}\nPath: {}\nThreads: {}", "PID: {}\nPPID: {}\nName: {}\nPath: {}\nThreads: {}",
@@ -364,7 +364,7 @@ fn draw_process_details<B: Backend>(f: &mut Frame, area: Rect, app: &App) {
f.render_widget(paragraph, area); f.render_widget(paragraph, area);
} }
fn draw_detections<B: Backend>(f: &mut Frame, area: Rect, app: &App) { fn draw_detections(f: &mut Frame, area: Rect, app: &App) {
let items: Vec<ListItem> = app let items: Vec<ListItem> = app
.detections .detections
.iter() .iter()
@@ -420,7 +420,7 @@ fn draw_detections<B: Backend>(f: &mut Frame, area: Rect, app: &App) {
f.render_stateful_widget(list, area, &mut state); f.render_stateful_widget(list, area, &mut state);
} }
fn draw_memory<B: Backend>(f: &mut Frame, area: Rect, app: &App) { fn draw_memory(f: &mut Frame, area: Rect, app: &App) {
let chunks = Layout::default() let chunks = Layout::default()
.direction(Direction::Vertical) .direction(Direction::Vertical)
.constraints([Constraint::Length(8), Constraint::Min(0)]) .constraints([Constraint::Length(8), Constraint::Min(0)])
@@ -459,7 +459,7 @@ fn draw_memory<B: Backend>(f: &mut Frame, area: Rect, app: &App) {
f.render_widget(memory_info, chunks[1]); f.render_widget(memory_info, chunks[1]);
} }
fn draw_logs<B: Backend>(f: &mut Frame, area: Rect, app: &App) { fn draw_logs(f: &mut Frame, area: Rect, app: &App) {
let items: Vec<ListItem> = app let items: Vec<ListItem> = app
.logs .logs
.iter() .iter()