From e2bb4841fa139025c8ee842193f32b71bac53402 Mon Sep 17 00:00:00 2001 From: monosans Date: Wed, 23 Jul 2025 15:57:25 +0300 Subject: [PATCH] refactor: simplify signal handling and update TUI controls --- src/main.rs | 89 ++++++++++++----------------------------------------- src/tui.rs | 73 ++++++++++++++++++++++--------------------- 2 files changed, 58 insertions(+), 104 deletions(-) diff --git a/src/main.rs b/src/main.rs index 22cd546..c989fc5 100644 --- a/src/main.rs +++ b/src/main.rs @@ -153,73 +153,6 @@ async fn download_output_dependencies( Ok(()) } -#[cfg(unix)] -async fn watch_signals(token: tokio_util::sync::CancellationToken) { - let token_clone = token.clone(); - tokio::select! { - biased; - () = token_clone.cancelled() => {}, - () = async move { - match ( - tokio::signal::unix::signal( - tokio::signal::unix::SignalKind::interrupt(), - ), - tokio::signal::unix::signal( - tokio::signal::unix::SignalKind::terminate(), - ) - ) { - (Ok(mut a), Ok(mut b)) => { - tokio::select! { - _ = a.recv() => { - tracing::info!("Received SIGINT, exiting..."); - token.cancel(); - }, - _ = b.recv() => { - tracing::info!("Received SIGTERM, exiting..."); - token.cancel(); - } - }; - } - (Err(e), Ok(mut s)) => { - tracing::warn!("Failed to create SIGINT handler: {}", e); - s.recv().await; - tracing::info!("Received SIGTERM, exiting..."); - token.cancel(); - } - (Ok(mut s), Err(e)) => { - tracing::warn!("Failed to create SIGTERM handler: {}", e); - s.recv().await; - tracing::info!("Received SIGINT, exiting..."); - token.cancel(); - } - (Err(e), Err(e2)) => { - tracing::warn!("Failed to create signal handlers: {}, {}", e, e2); - } - } - } => {} - }; -} - -#[cfg(not(unix))] -async fn watch_signals(token: tokio_util::sync::CancellationToken) { - let token_clone = token.clone(); - tokio::select! { - biased; - () = token_clone.cancelled() => {}, - ctrl_c = tokio::signal::ctrl_c() => { - match ctrl_c { - Ok(()) => { - tracing::info!("Received Ctrl+C, exiting..."); - token.cancel(); - } - Err(e) => { - tracing::warn!("Failed to create Ctrl+C handler: {}", e); - } - } - } - }; -} - async fn main_task( config: Arc, token: tokio_util::sync::CancellationToken, @@ -285,8 +218,6 @@ async fn run_with_tui( let terminal_guard = tui::RatatuiRestoreGuard; let token = tokio_util::sync::CancellationToken::new(); - tokio::spawn(watch_signals(token.clone())); - let (tx, rx) = tokio::sync::mpsc::unbounded_channel(); tokio::try_join!( @@ -301,6 +232,26 @@ async fn run_with_tui( Ok(()) } +#[cfg(not(feature = "tui"))] +async fn watch_signals(token: tokio_util::sync::CancellationToken) { + let token_clone = token.clone(); + tokio::select! { + biased; + () = token_clone.cancelled() => {}, + ctrl_c = tokio::signal::ctrl_c() => { + match ctrl_c { + Ok(()) => { + tracing::info!("Received Ctrl+C, exiting..."); + token.cancel(); + } + Err(e) => { + tracing::warn!("Failed to create Ctrl+C handler: {}", e); + } + } + } + }; +} + #[cfg(not(feature = "tui"))] async fn run_without_tui( config: Arc, diff --git a/src/tui.rs b/src/tui.rs index 1dd5ae2..5410f05 100644 --- a/src/tui.rs +++ b/src/tui.rs @@ -24,7 +24,6 @@ use crate::{ event::{AppEvent, Event}, ipdb, proxy::ProxyType, - utils::is_docker, }; const FPS: f64 = 30.0; @@ -49,8 +48,7 @@ pub async fn run( let logger_state = TuiWidgetState::default(); while !matches!(app_state.mode, AppMode::Quit) { if let Some(event) = rx.recv().await { - if handle_event(event, &mut app_state, &token, &logger_state).await - { + if handle_event(event, &mut app_state, &token, &logger_state) { terminal .draw(|frame| draw(frame, &app_state, &logger_state)) .wrap_err("failed to draw tui")?; @@ -70,6 +68,15 @@ pub enum AppMode { Quit, } +impl AppMode { + pub const fn next(&self) -> Self { + match self { + Self::Running => Self::Done, + Self::Done | Self::Quit => Self::Quit, + } + } +} + #[derive(Default)] pub struct AppState { pub mode: AppMode, @@ -148,7 +155,7 @@ fn draw(f: &mut Frame, state: &AppState, logger_state: &TuiWidgetState) { // Scraping and checking Constraint::Length(1 + (3 * 3) + 1), // Hotkeys - Constraint::Length(3), + Constraint::Length(4), ]) .split(outer_block.inner(f.area())); @@ -270,25 +277,32 @@ fn draw(f: &mut Frame, state: &AppState, logger_state: &TuiWidgetState) { working_proxies_block.inner(layout[2]), ); } - let lines = vec![ - Line::from("\u{2b06}\u{fe0f} Up/PageUp/k - scroll logs up"), - Line::from("\u{2b07}\u{fe0f} Down/PageDown/j - scroll logs down"), - if matches!(state.mode, AppMode::Running) { - Line::from("\u{1f6d1} ESC/q/Ctrl-C - stop") - .style(Style::default().fg(Color::Yellow)) + + let running = matches!(state.mode, AppMode::Running); + let mut lines = Vec::with_capacity(if running { 4 } else { 3 }); + lines.push(Line::from("\u{2b06}\u{fe0f} Up / PageUp / k - scroll logs up")); + lines.push(Line::from( + "\u{2b07}\u{fe0f} Down / PageDown / j - scroll logs down", + )); + if running { + lines.push( + Line::from("\u{1f6d1} ESC / q - stop") + .style(Style::default().fg(Color::Yellow)), + ); + } + lines.push( + Line::from(if running { + "\u{1f6aa} Ctrl-C - quit" } else { - Line::from("\u{1f6aa} ESC/q/Ctrl-C - quit") - .style(Style::default().fg(Color::Red)) - }, - ]; + "\u{1f6aa} ESC / q / Ctrl-C - quit" + }) + .style(Style::default().fg(Color::Red)), + ); + f.render_widget(Text::from(lines).centered(), outer_layout[3]); } -async fn is_interactive() -> bool { - !is_docker().await -} - -async fn handle_event( +fn handle_event( event: Event, state: &mut AppState, token: &tokio_util::sync::CancellationToken, @@ -300,21 +314,13 @@ async fn handle_event( match crossterm_event { CrosstermEvent::Key(key_event) => match key_event.code { KeyCode::Esc | KeyCode::Char('q' | 'Q') => { - state.mode = if matches!(state.mode, AppMode::Running) { - AppMode::Done - } else { - AppMode::Quit - }; + state.mode = state.mode.next(); token.cancel(); } KeyCode::Char('c' | 'C') if key_event.modifiers == KeyModifiers::CONTROL => { - state.mode = if matches!(state.mode, AppMode::Running) { - AppMode::Done - } else { - AppMode::Quit - }; + state.mode = AppMode::Quit; token.cancel(); } KeyCode::Up | KeyCode::PageUp | KeyCode::Char('k') => { @@ -382,12 +388,9 @@ async fn handle_event( .or_insert(1); } AppEvent::Done => { - state.mode = - if !token.is_cancelled() && is_interactive().await { - AppMode::Done - } else { - AppMode::Quit - }; + if matches!(state.mode, AppMode::Running) { + state.mode = AppMode::Done; + } } } false