microsandbox_utils/
term.rs1use indicatif::{MultiProgress, MultiProgressAlignment, ProgressBar, ProgressStyle};
4use std::sync::{Arc, LazyLock};
5
6pub static MULTI_PROGRESS: LazyLock<Arc<MultiProgress>> = LazyLock::new(|| {
12 let mp = MultiProgress::new();
13 mp.set_alignment(MultiProgressAlignment::Top);
14 Arc::new(mp)
15});
16
17pub static CHECKMARK: LazyLock<String> =
19 LazyLock::new(|| format!("{}", console::style("✓").green()));
20
21pub static ERROR_MARK: LazyLock<String> =
23 LazyLock::new(|| format!("{}", console::style("✗").red()));
24
25pub static TICK_STRINGS: LazyLock<[&str; 11]> =
27 LazyLock::new(|| ["⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏", &CHECKMARK]);
28
29pub static ERROR_TICK_STRINGS: LazyLock<[&str; 2]> = LazyLock::new(|| ["⠏", &ERROR_MARK]);
31
32pub fn is_interactive_terminal() -> bool {
38 let stdin_is_tty = unsafe { libc::isatty(libc::STDIN_FILENO) == 1 };
40 let stdout_is_tty = unsafe { libc::isatty(libc::STDOUT_FILENO) == 1 };
41
42 let is_tty = stdin_is_tty && stdout_is_tty;
44
45 let has_term = std::env::var("TERM").is_ok();
47
48 if is_tty && !has_term {
50 tracing::debug!("detected TTY without TERM environment variable");
51 }
52
53 is_tty
55}
56
57pub fn is_ansi_interactive_terminal() -> bool {
59 is_interactive_terminal() && !std::env::var("TERM").unwrap_or_default().contains("dumb")
60}
61
62pub fn create_spinner(
76 message: String,
77 insert_at_position: Option<usize>,
78 len: Option<u64>,
79) -> ProgressBar {
80 let pb = if let Some(len) = len {
81 ProgressBar::new(len)
82 } else {
83 ProgressBar::new_spinner()
84 };
85
86 let pb = if let Some(pos) = insert_at_position {
87 MULTI_PROGRESS.insert(pos, pb)
88 } else {
89 MULTI_PROGRESS.add(pb)
90 };
91
92 let style = if let Some(_) = len {
93 ProgressStyle::with_template("{spinner} {msg} {pos:.bold} / {len:.dim}")
94 .unwrap()
95 .tick_strings(&*TICK_STRINGS)
96 } else {
97 ProgressStyle::with_template("{spinner} {msg}")
98 .unwrap()
99 .tick_strings(&*TICK_STRINGS)
100 };
101
102 pb.set_style(style);
103 pb.set_message(message);
104 pb.enable_steady_tick(std::time::Duration::from_millis(80));
105 pb
106}
107
108pub fn finish_with_error(pb: &ProgressBar) {
115 let style = ProgressStyle::with_template("{spinner} {msg}")
116 .unwrap()
117 .tick_strings(&*ERROR_TICK_STRINGS);
118
119 pb.set_style(style);
120 pb.finish();
121}