1use std::io::IsTerminal;
8
9use dialoguer::{Confirm, Select};
10
11#[derive(Debug, Clone, Copy, PartialEq, Eq)]
13pub enum StepAction {
14 Run,
16 Skip,
18 Abort,
20 RunAll,
22}
23
24#[derive(Debug, Clone, Copy, PartialEq, Eq)]
26pub enum ErrorAction {
27 Continue,
29 Abort,
31 Retry,
33}
34
35#[must_use]
43pub fn prompt_step(command: &str, index: usize, total: usize, is_destructive: bool) -> StepAction {
44 let prefix = if is_destructive {
45 "\x1b[31;1m\u{26a0}\x1b[0m "
46 } else {
47 ""
48 };
49
50 let prompt = format!(
51 "{}[{}/{}] \x1b[1m$ {}\x1b[0m",
52 prefix,
53 index + 1,
54 total,
55 command
56 );
57 eprintln!("{prompt}");
58
59 let items = &["Run", "Skip", "Abort", "Run all remaining"];
60 let selection = Select::new()
61 .with_prompt("Action")
62 .items(items)
63 .default(0)
64 .interact();
65
66 match selection {
67 Ok(0) => StepAction::Run,
68 Ok(1) => StepAction::Skip,
69 Ok(3) => StepAction::RunAll,
70 _ => StepAction::Abort,
71 }
72}
73
74#[must_use]
81pub fn prompt_destructive(command: &str, reason: &str) -> bool {
82 eprintln!();
83 eprintln!(" \x1b[31;1m\u{26a0} Destructive command detected\x1b[0m");
84 eprintln!(" Command: \x1b[1m{command}\x1b[0m");
85 eprintln!(" Reason: {reason}");
86 eprintln!();
87
88 Confirm::new()
89 .with_prompt("Execute this command?")
90 .default(false)
91 .interact()
92 .unwrap_or(false)
93}
94
95#[must_use]
102pub fn prompt_error(command: &str, exit_code: Option<i32>) -> ErrorAction {
103 let code_str = exit_code.map_or_else(|| "unknown".to_string(), |c| c.to_string());
104
105 eprintln!();
106 eprintln!(" \x1b[31m\u{2717} Command failed\x1b[0m (exit code: {code_str})");
107 eprintln!(" $ {command}");
108 eprintln!();
109
110 let items = &["Continue", "Abort", "Retry"];
111 let selection = Select::new()
112 .with_prompt("What would you like to do?")
113 .items(items)
114 .default(0)
115 .interact();
116
117 match selection {
118 Ok(0) => ErrorAction::Continue,
119 Ok(2) => ErrorAction::Retry,
120 _ => ErrorAction::Abort,
121 }
122}
123
124#[must_use]
130pub fn is_interactive() -> bool {
131 std::io::stdin().is_terminal()
132}