report: grow the Prompter port and move display_path out of the ui module

Prompter gains interactive(), select() and text() (with the Validator
type), and confirm() now propagates cancellation as Err so flows abort
instead of silently taking a default when the user hits Ctrl+C. The
terminal prompter implements the full port; the port also re-exports
the path display helper, which is pure presentation formatting used by
events and messages rather than terminal code.
This commit is contained in:
2026-09-18 20:34:33 +02:00
parent 052c02cdc3
commit d64e472845
11 changed files with 115 additions and 47 deletions
+1 -1
View File
@@ -232,7 +232,7 @@ impl DebUi {
if self.shared.enabled && !artifacts.is_empty() {
println!("Built in {}s:", elapsed.as_secs());
for artifact in artifacts {
println!(" {}", crate::ui::display_path(artifact));
println!(" {}", crate::report::display_path(artifact));
}
}
}
+31 -8
View File
@@ -12,7 +12,7 @@ use crossterm::{
style::{self, Color, Print, SetForegroundColor},
terminal,
};
use std::io::{self, Write};
use std::io::{self, IsTerminal, Write};
use std::time::Duration;
/// Why a prompt could not run interactively
@@ -32,9 +32,8 @@ impl std::fmt::Display for PromptError {
impl std::error::Error for PromptError {}
/// Answer validator of [`text`]: accepts the answer, or explains why it is
/// rejected
pub type Validator = dyn Fn(&str) -> Result<(), String>;
/// Answer validator of [`text`] (re-exported from [`crate::report`])
pub use crate::report::Validator;
/// What a prompt's event loop asks its drawing helper to render; keeping all
/// terminal access behind this callback makes the loops pure logic that unit
@@ -128,13 +127,37 @@ pub fn confirm(label: &str, default: bool) -> Result<bool, Box<dyn std::error::E
}
/// [`crate::report::Prompter`] answered by the interactive terminal: each
/// question runs the matching raw-mode prompt, falling back to the default
/// answer when no interactive terminal is attached (CI, piped input).
/// question runs the matching raw-mode prompt. Without an interactive
/// terminal (CI, piped input) [`Prompter::interactive`] is false — flows
/// then take their headless path instead of asking — and a cancel (Ctrl+C)
/// propagates as `Err` so flows can abort.
pub struct TerminalPrompter;
impl crate::report::Prompter for TerminalPrompter {
fn confirm(&self, question: &str, default: bool) -> bool {
confirm(question, default).unwrap_or(default)
fn interactive(&self) -> bool {
std::io::stdin().is_terminal() && std::io::stdout().is_terminal()
}
fn confirm(&self, question: &str, default: bool) -> Result<bool, Box<dyn std::error::Error>> {
confirm(question, default)
}
fn select(
&self,
label: &str,
options: &[String],
default: &str,
) -> Result<String, Box<dyn std::error::Error>> {
select(label, options, default)
}
fn text(
&self,
label: &str,
default: &str,
validate: Option<&Validator>,
) -> Result<String, Box<dyn std::error::Error>> {
text(label, default, validate)
}
}