Skip to main content

mvm_cli/
ui.rs

1use colored::Colorize;
2use indicatif::{ProgressBar, ProgressStyle};
3
4// ---------------------------------------------------------------------------
5// Colored message helpers
6// ---------------------------------------------------------------------------
7
8fn prefix() -> String {
9    "[mvm]".bold().cyan().to_string()
10}
11
12/// Print an informational message: [mvm] message
13pub fn info(msg: &str) {
14    println!("{} {}", prefix(), msg);
15}
16
17/// Print a success message: [mvm] message (in green)
18pub fn success(msg: &str) {
19    println!("{} {}", prefix(), msg.green());
20}
21
22/// Print an error message: [mvm] ERROR: message (in red)
23pub fn error(msg: &str) {
24    eprintln!("{} {}", "[mvm]".bold().red(), msg.red());
25}
26
27/// Print a warning message: [mvm] message (in yellow)
28pub fn warn(msg: &str) {
29    println!("{} {}", prefix(), msg.yellow());
30}
31
32/// Print a numbered step: [mvm] Step n/total: message
33pub fn step(n: u32, total: u32, msg: &str) {
34    println!(
35        "\n{} {} {}",
36        prefix(),
37        format!("Step {}/{}:", n, total).bold().yellow(),
38        msg,
39    );
40}
41
42// ---------------------------------------------------------------------------
43// Banner
44// ---------------------------------------------------------------------------
45
46/// Print a green bold banner box.
47pub fn banner(lines: &[&str]) {
48    let width = lines.iter().map(|l| l.len()).max().unwrap_or(0) + 4;
49    let rule = "=".repeat(width);
50
51    println!();
52    println!("{}", rule.bold().green());
53    for line in lines {
54        let pad = width - line.len() - 4;
55        println!(
56            "{}",
57            format!("  {}{}  ", line, " ".repeat(pad)).bold().green()
58        );
59    }
60    println!("{}", rule.bold().green());
61    println!();
62}
63
64// ---------------------------------------------------------------------------
65// Status table
66// ---------------------------------------------------------------------------
67
68/// Print the status header.
69pub fn status_header() {
70    println!("{}", "mvm status".bold());
71    println!("{}", "----------".dimmed());
72}
73
74/// Print a status line with a bold label and a colored value.
75/// Recognized values: "Running", "Stopped", "Not running", etc.
76pub fn status_line(label: &str, value: &str) {
77    let colored_value = if value.starts_with("Running") {
78        value.green().to_string()
79    } else if value == "Stopped" {
80        value.yellow().to_string()
81    } else if value.starts_with("Not ") || value == "-" {
82        value.dimmed().to_string()
83    } else if value.starts_with("Starting") {
84        value.yellow().to_string()
85    } else {
86        value.to_string()
87    };
88
89    println!("{} {}", format!("{:<14}", label).bold(), colored_value);
90}
91
92// ---------------------------------------------------------------------------
93// Interactive prompts
94// ---------------------------------------------------------------------------
95
96/// Show an interactive confirmation prompt. Returns true if confirmed.
97pub fn confirm(msg: &str) -> bool {
98    inquire::Confirm::new(msg)
99        .with_default(false)
100        .prompt()
101        .unwrap_or(false)
102}
103
104// ---------------------------------------------------------------------------
105// Spinners
106// ---------------------------------------------------------------------------
107
108/// Create and start a spinner with the given message.
109/// Call `.finish_with_message()` or `.finish_and_clear()` when done.
110pub fn spinner(msg: &str) -> ProgressBar {
111    let pb = ProgressBar::new_spinner();
112    pb.set_style(
113        ProgressStyle::default_spinner()
114            .tick_strings(&["⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"])
115            .template("{spinner:.cyan} {msg}")
116            .expect("invalid spinner template"),
117    );
118    pb.set_message(msg.to_string());
119    pb.enable_steady_tick(std::time::Duration::from_millis(80));
120    pb
121}