1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
use dialoguer::theme::ColorfulTheme;
use dialoguer::Confirm;
use std::io::Result;

#[derive(Clone, Copy)]
pub enum Decision {
    Yes,
    No,
    Quit,
}

#[derive(Clone, Copy, Default)]
pub struct DecisionContext {
    pub is_dry_run: bool,
    pub yes_to_all: bool,
}

pub trait Decide {
    fn obtain_decision(
        &mut self,
        ctx: &DecisionContext,
        question: impl AsRef<str>,
    ) -> Result<Decision>;
}

#[derive(Default)]
pub struct NiceInteractiveDecider {
    decision_memory: Option<Decision>,
}

impl Decide for NiceInteractiveDecider {
    fn obtain_decision(
        &mut self,
        ctx: &DecisionContext,
        question: impl AsRef<str>,
    ) -> Result<Decision> {
        let suffix = if ctx.is_dry_run { " [dry-run]" } else { "" };
        Ok(self.decision_memory.as_ref().copied().unwrap_or_else(|| {
            if ctx.yes_to_all {
                println!("  {}{} [yes by -y arg]", question.as_ref(), suffix);
                Decision::Yes
            } else {
                Confirm::with_theme(&ColorfulTheme::default())
                    .with_prompt(format!("{}{}", question.as_ref(), suffix,))
                    .default(false)
                    .show_default(true)
                    .wait_for_newline(false)
                    .interact_opt()
                    .unwrap()
                    .map(|x| if x { Decision::Yes } else { Decision::No })
                    .unwrap_or(Decision::Quit)
            }
        }))
    }
}