modcli/input/
input_builder.rs

1use crate::output::hook;
2use rpassword::read_password;
3use std::io::{stdin, stdout, Write};
4
5/// Prompt for plain text input with optional default fallback
6pub fn prompt_text(prompt: &str, default: Option<&str>) -> String {
7    print!(
8        "{prompt}{} ",
9        default.map_or(String::new(), |d| format!(" [{d}]"))
10    );
11    if let Err(e) = stdout().flush() {
12        hook::warn(&format!("flush failed: {e}"));
13    }
14
15    let mut input = String::new();
16    if let Err(e) = stdin().read_line(&mut input) {
17        hook::error(&format!("failed to read input: {e}"));
18        return default.unwrap_or("").to_string();
19    }
20    let trimmed = input.trim();
21
22    if trimmed.is_empty() {
23        default.unwrap_or("").to_string()
24    } else {
25        trimmed.to_string()
26    }
27}
28
29/// Prompt for a yes/no confirmation
30pub fn confirm(prompt: &str, default_yes: bool) -> bool {
31    let yes_hint = if default_yes { "[Y/n]" } else { "[y/N]" };
32    print!("{prompt} {yes_hint} ");
33    if let Err(e) = stdout().flush() {
34        hook::warn(&format!("flush failed: {e}"));
35    }
36
37    let mut input = String::new();
38    if let Err(e) = stdin().read_line(&mut input) {
39        hook::error(&format!("failed to read input: {e}"));
40        return default_yes;
41    }
42    let trimmed = input.trim().to_lowercase();
43
44    match trimmed.as_str() {
45        "y" | "yes" => true,
46        "n" | "no" => false,
47        "" => default_yes,
48        _ => default_yes, // Fallback to default if unrecognized
49    }
50}
51
52/// Prompt for a hidden password
53pub fn prompt_password(prompt: &str) -> String {
54    print!("{prompt} ");
55    if let Err(e) = stdout().flush() {
56        hook::warn(&format!("flush failed: {e}"));
57    }
58    read_password().unwrap_or_default()
59}