cargo_mobile2/util/
prompt.rs

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
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
use colored::{Color, Colorize as _};
use std::{
    fmt::Display,
    io::{self, Write},
};

pub fn minimal(msg: impl Display) -> io::Result<String> {
    let mut input = String::new();
    print!("{}: ", msg);
    io::stdout().flush()?;
    io::stdin().read_line(&mut input)?;

    Ok(input.trim().to_owned())
}

pub fn default(
    msg: impl Display,
    default: Option<&str>,
    default_color: Option<Color>,
) -> io::Result<String> {
    if let Some(default) = default {
        let msg = if let Some(default_color) = default_color {
            format!("{} ({})", msg, default.color(default_color).bold())
        } else {
            format!("{} ({})", msg, default)
        };
        minimal(msg)
    } else {
        minimal(msg)
    }
    .map(|response| {
        #[allow(clippy::unnecessary_unwrap)]
        if response.is_empty() && default.is_some() {
            default.unwrap().to_owned()
        } else {
            response
        }
    })
}

pub fn yes_no(msg: impl Display, default: Option<bool>) -> io::Result<Option<bool>> {
    let y_n = match default {
        Some(true) => "[Y/n]",
        Some(false) => "[y/N]",
        None => "[y/n]",
    };
    minimal(format!("{} {}", msg, y_n)).map(|response| {
        if response.eq_ignore_ascii_case("y") {
            Some(true)
        } else if response.eq_ignore_ascii_case("n") {
            Some(false)
        } else if response.is_empty() {
            default
        } else {
            println!("That was neither a Y nor an N! You're pretty silly.");
            None
        }
    })
}

pub fn list_display_only(choices: impl Iterator<Item = impl Display>, choice_count: usize) {
    if choice_count > 0 {
        for (index, choice) in choices.enumerate() {
            println!("  [{}] {}", index.to_string().green(), choice);
        }
    } else {
        println!("  -- none --");
    }
}

pub fn list(
    header: impl Display,
    choices: impl ExactSizeIterator<Item = impl Display>,
    noun: impl Display,
    alternative: Option<&str>,
    msg: impl Display,
) -> io::Result<usize> {
    println!("{}:", header);
    let choice_count = choices.len();
    list_display_only(choices, choice_count);
    if let Some(alternative) = alternative {
        println!(
            "  Enter an {} for a {} above, or enter a {} manually.",
            "index".green(),
            noun,
            alternative.cyan(),
        );
    } else {
        println!("  Enter an {} for a {} above.", "index".green(), noun);
    }
    loop {
        let response = default(
            &msg,
            if choice_count == 1 { Some("0") } else { None },
            Some(Color::Green),
        )?;
        if !response.is_empty() {
            if let Ok(index) = response.parse::<usize>() {
                if index < choice_count {
                    return Ok(index);
                } else {
                    println!("There's no device with an index that high.");
                }
            } else {
                println!("Hey, that wasn't a number! You're silly.");
            }
        } else {
            println!("Not to be pushy, but you need to pick a device.");
        }
    }
}