Skip to main content

iron/
ui.rs

1use console::style;
2use indicatif::{ProgressBar, ProgressStyle};
3use std::time::Duration;
4
5pub fn spinner(msg: &str) -> ProgressBar {
6    let pb = ProgressBar::new_spinner();
7    #[allow(clippy::unwrap_used)]
8    let style = ProgressStyle::default_spinner()
9        .tick_strings(&["⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"])
10        .template("{spinner:.cyan} {msg}")
11        .unwrap();
12    pb.set_style(style);
13    pb.set_message(msg.to_string());
14    pb.enable_steady_tick(Duration::from_millis(80));
15    pb
16}
17
18pub fn success(msg: &str) {
19    println!("{} {}", style("✓").green().bold(), msg);
20}
21
22pub fn error(msg: &str) {
23    eprintln!("{} {}", style("✗").red().bold(), msg);
24}
25
26pub fn header(msg: &str) {
27    println!("\n{}", style(msg).bold().underlined());
28}
29
30pub fn confirm(msg: &str) -> bool {
31    eprint!("{} {} ", style("?").yellow().bold(), msg);
32    let mut input = String::new();
33    if std::io::stdin().read_line(&mut input).is_err() {
34        return false;
35    }
36    matches!(input.trim().to_lowercase().as_str(), "y" | "yes")
37}
38
39pub fn prompt(label: &str) -> Option<String> {
40    eprint!("{} {} ", style("?").yellow().bold(), label);
41    let mut input = String::new();
42    if std::io::stdin().read_line(&mut input).is_err() {
43        return None;
44    }
45    let trimmed = input.trim();
46    if trimmed.is_empty() {
47        None
48    } else {
49        Some(trimmed.to_string())
50    }
51}
52
53pub fn prompt_secret(label: &str) -> Option<String> {
54    eprint!("{} {} ", style("?").yellow().bold(), label);
55    let term = console::Term::stderr();
56    match term.read_secure_line() {
57        Ok(input) => {
58            let trimmed = input.trim().to_string();
59            if trimmed.is_empty() {
60                None
61            } else {
62                Some(trimmed)
63            }
64        }
65        Err(_) => None,
66    }
67}