1use dialoguer::theme::ColorfulTheme;
2use dialoguer::Confirm;
3use std::io::Result;
4use std::path::PathBuf;
5
6#[derive(Clone, Copy)]
7pub enum Decision {
8 Yes,
9 No,
10 Quit,
11}
12
13#[derive(Clone, Default)]
14pub struct DecisionContext {
15 pub is_dry_run: bool,
16 pub yes_to_all: bool,
17 pub working_dir: PathBuf,
18}
19
20impl DecisionContext {
21 pub fn println(&self, msg: impl AsRef<str>) {
22 println!("{}", msg.as_ref());
23 }
24}
25
26pub trait Decide {
27 fn obtain_decision(
28 &mut self,
29 ctx: &DecisionContext,
30 question: impl AsRef<str>,
31 ) -> Result<Decision>;
32}
33
34#[derive(Default)]
35pub struct NiceInteractiveDecider {
36 decision_memory: Option<Decision>,
37}
38
39impl Decide for NiceInteractiveDecider {
40 fn obtain_decision(
41 &mut self,
42 ctx: &DecisionContext,
43 question: impl AsRef<str>,
44 ) -> Result<Decision> {
45 let suffix = if ctx.is_dry_run { " [dry-run]" } else { "" };
46 Ok(self.decision_memory.as_ref().copied().unwrap_or_else(|| {
47 if ctx.yes_to_all {
48 ctx.println(format!(" {}{suffix} [yes by -y arg]", question.as_ref()));
49 Decision::Yes
50 } else {
51 Confirm::with_theme(&ColorfulTheme::default())
52 .with_prompt(format!("{}{suffix}", question.as_ref()))
53 .default(false)
54 .show_default(true)
55 .wait_for_newline(false)
56 .interact_opt()
57 .unwrap()
58 .map(|x| if x { Decision::Yes } else { Decision::No })
59 .unwrap_or(Decision::Quit)
60 }
61 }))
62 }
63}