diff --git a/src/main.rs b/src/main.rs index f73c1bd8..b32baf50 100644 --- a/src/main.rs +++ b/src/main.rs @@ -209,7 +209,19 @@ fn run() -> Result<()> { } for step in step::default_steps() { - step.run(&mut runner, &ctx)? + match step.run(&mut runner, &ctx) { + Ok(()) => (), + Err(error) + if error + .downcast_ref::() + .is_some_and(|e| e.kind() == io::ErrorKind::Interrupted) => + { + println!(); + debug!("Interrupted (possibly with 'q' during retry prompt). Printing summary."); + break; + } + Err(error) => return Err(error), + } } let mut failed = false; diff --git a/src/runner.rs b/src/runner.rs index 210212e9..5cdc2385 100644 --- a/src/runner.rs +++ b/src/runner.rs @@ -1,14 +1,15 @@ -use color_eyre::eyre::Result; +use color_eyre::eyre::{Result, WrapErr}; use rust_i18n::t; use std::borrow::Cow; use std::fmt::Debug; +use std::io; use tracing::debug; use crate::ctrlc; use crate::error::{DryRun, MissingSudo, SkipStep}; use crate::execution_context::ExecutionContext; use crate::step::Step; -use crate::terminal::{print_error, print_warning, should_retry}; +use crate::terminal::{print_error, print_warning, should_retry, ShouldRetry}; pub enum StepResult { Success, @@ -98,21 +99,28 @@ impl<'a> Runner<'a> { let should_ask = interrupted || !(self.ctx.config().no_retry() || ignore_failure); let should_retry = if should_ask { print_error(&key, format!("{e:?}")); - should_retry(interrupted, key.as_ref())? + should_retry(key.as_ref())? } else { - false + ShouldRetry::No }; - if !should_retry { - self.push_result( - key, - if ignore_failure { - StepResult::Ignored - } else { - StepResult::Failure - }, - ); - break; + match should_retry { + ShouldRetry::No | ShouldRetry::Quit => { + self.push_result( + key, + if ignore_failure { + StepResult::Ignored + } else { + StepResult::Failure + }, + ); + if let ShouldRetry::Quit = should_retry { + return Err(io::Error::from(io::ErrorKind::Interrupted)) + .context("Quit from user input"); + } + break; + } + ShouldRetry::Yes => (), } } } diff --git a/src/terminal.rs b/src/terminal.rs index 44d59bb8..c6fba67c 100644 --- a/src/terminal.rs +++ b/src/terminal.rs @@ -201,10 +201,11 @@ impl Terminal { } } } + #[allow(unused_variables)] - fn should_retry(&mut self, interrupted: bool, step_name: &str) -> eyre::Result { + fn should_retry(&mut self, step_name: &str) -> eyre::Result { if self.width.is_none() { - return Ok(false); + return Ok(ShouldRetry::No); } if self.set_title { @@ -223,7 +224,7 @@ impl Terminal { let answer = loop { match self.term.read_key() { - Ok(Key::Char('y' | 'Y')) => break Ok(true), + Ok(Key::Char('y' | 'Y')) => break Ok(ShouldRetry::Yes), Ok(Key::Char('s' | 'S')) => { println!( "\n\n{}\n", @@ -232,16 +233,16 @@ impl Terminal { if let Err(err) = run_shell().context("Failed to run shell") { self.term.write_fmt(format_args!("{err:?}\n{prompt_inner}")).ok(); } else { - break Ok(true); + break Ok(ShouldRetry::Yes); } } - Ok(Key::Char('n' | 'N') | Key::Enter) => break Ok(false), + Ok(Key::Char('n' | 'N') | Key::Enter) => break Ok(ShouldRetry::No), Err(e) => { error!("Error reading from terminal: {}", e); - break Ok(false); + break Ok(ShouldRetry::No); } Ok(Key::Char('q' | 'Q')) => { - return Err(io::Error::from(io::ErrorKind::Interrupted)).context("Quit from user input") + break Ok(ShouldRetry::Quit); } _ => (), } @@ -257,14 +258,21 @@ impl Terminal { } } +#[derive(Clone, Copy)] +pub enum ShouldRetry { + Yes, + No, + Quit, +} + impl Default for Terminal { fn default() -> Self { Self::new() } } -pub fn should_retry(interrupted: bool, step_name: &str) -> eyre::Result { - TERMINAL.lock().unwrap().should_retry(interrupted, step_name) +pub fn should_retry(step_name: &str) -> eyre::Result { + TERMINAL.lock().unwrap().should_retry(step_name) } pub fn print_separator>(message: P) {