use crate::scanner_dashboard::state::{ScannerDashboardState, ViewMode};
use crossterm::event::{self, Event, KeyCode, KeyEvent};
use std::time::Duration;
pub enum AppAction {
Continue,
Quit,
}
pub fn handle_events(
state: &mut ScannerDashboardState,
timeout: Duration,
) -> Result<AppAction, Box<dyn std::error::Error>> {
if event::poll(timeout)? {
if let Event::Key(key) = event::read()? {
return handle_key_event(state, key);
}
}
Ok(AppAction::Continue)
}
fn handle_key_event(
state: &mut ScannerDashboardState,
key: KeyEvent,
) -> Result<AppAction, Box<dyn std::error::Error>> {
use crate::scanner_dashboard::state::LibraryCategory;
if state.completion_notification_shown
&& matches!(key.code, KeyCode::Char(' ') | KeyCode::Enter)
{
state.dismiss_completion_notification();
return Ok(AppAction::Continue);
}
match key.code {
KeyCode::Char('q') | KeyCode::Esc => {
if state.completion_notification_shown {
state.dismiss_completion_notification();
Ok(AppAction::Continue)
} else if state.help_overlay_open {
state.close_help_overlay();
Ok(AppAction::Continue)
} else if state.detail_view_open {
state.close_detail_view();
Ok(AppAction::Continue)
} else {
Ok(AppAction::Quit)
}
}
KeyCode::Char(' ') => {
if state.completion_notification_shown {
state.dismiss_completion_notification();
} else {
state.toggle_pause();
}
Ok(AppAction::Continue)
}
KeyCode::Up | KeyCode::Char('k') => {
state.scroll_up();
Ok(AppAction::Continue)
}
KeyCode::Down | KeyCode::Char('j') => {
state.scroll_down();
Ok(AppAction::Continue)
}
KeyCode::Home => {
state.scroll_to_top();
Ok(AppAction::Continue)
}
KeyCode::Char('g') => {
if state.current_view == ViewMode::Migration {
state.generate_migration_recommendations();
} else {
state.scroll_to_top();
}
Ok(AppAction::Continue)
}
KeyCode::End | KeyCode::Char('G') => {
state.scroll_to_bottom();
Ok(AppAction::Continue)
}
KeyCode::Tab => {
state.cycle_view();
Ok(AppAction::Continue)
}
KeyCode::Char('1') => {
state.current_view = ViewMode::LiveScan;
Ok(AppAction::Continue)
}
KeyCode::Char('2') => {
state.current_view = ViewMode::Category;
Ok(AppAction::Continue)
}
KeyCode::Char('3') => {
state.current_view = ViewMode::Statistics;
Ok(AppAction::Continue)
}
KeyCode::Char('4') => {
state.current_view = ViewMode::Migration;
Ok(AppAction::Continue)
}
KeyCode::Enter | KeyCode::Char('d') => {
if state.current_view == ViewMode::Category && key.code == KeyCode::Enter {
state.toggle_category(LibraryCategory::SslTls);
} else {
if let Some(library) = state.get_selected_library() {
state.open_detail_view(library.clone());
}
}
Ok(AppAction::Continue)
}
KeyCode::Char('a') => {
if state.current_view == ViewMode::Category {
state.expand_all_categories();
}
Ok(AppAction::Continue)
}
KeyCode::Char('c') => {
if state.current_view == ViewMode::Category {
state.collapse_all_categories();
}
Ok(AppAction::Continue)
}
KeyCode::Char('f') => {
state.toggle_quantum_vulnerable_filter();
Ok(AppAction::Continue)
}
KeyCode::Char('x') => {
state.clear_filter();
Ok(AppAction::Continue)
}
KeyCode::Char('s') => {
state.cycle_sort();
Ok(AppAction::Continue)
}
KeyCode::Char('m') => {
if state.current_view == ViewMode::Migration {
let library_name = state
.get_all_libraries()
.iter()
.find(|l| l.quantum_vulnerable)
.map(|lib| lib.name.clone());
if let Some(name) = library_name {
state.toggle_migration_status(&name);
}
}
Ok(AppAction::Continue)
}
KeyCode::Char('e') => {
if state.current_view == ViewMode::Migration {
let markdown = state.export_migration_plan();
let filename = format!(
"migration_plan_{}.md",
chrono::Local::now().format("%Y%m%d_%H%M%S")
);
if let Err(e) = std::fs::write(&filename, markdown) {
eprintln!("Failed to export migration plan: {}", e);
}
} else {
match state.export_to_json() {
Ok(json) => {
let filename = format!(
"scan_results_{}.json",
chrono::Local::now().format("%Y%m%d_%H%M%S")
);
if let Err(e) = std::fs::write(&filename, json) {
eprintln!("Failed to export JSON: {}", e);
}
}
Err(e) => {
eprintln!("Failed to generate JSON: {}", e);
}
}
}
Ok(AppAction::Continue)
}
KeyCode::Char('v') => {
let csv = state.export_to_csv();
let filename = format!(
"library_inventory_{}.csv",
chrono::Local::now().format("%Y%m%d_%H%M%S")
);
if let Err(e) = std::fs::write(&filename, csv) {
eprintln!("Failed to export CSV: {}", e);
}
Ok(AppAction::Continue)
}
KeyCode::Char('h') | KeyCode::F(1) => {
state.toggle_help_overlay();
Ok(AppAction::Continue)
}
_ => Ok(AppAction::Continue),
}
}