cli_ui/prompt/
progress_bar.rs1#![allow(missing_docs)]
2use super::spinner::{spinner, Spinner};
9use crate::styles::{paint, DIM};
10
11#[derive(Clone, Copy)]
12pub enum Style {
13 Light,
14 Heavy,
15 Block,
16}
17
18impl Style {
19 fn ch(self) -> &'static str {
20 match self {
21 Style::Light => "─",
22 Style::Heavy => "━",
23 Style::Block => "█",
24 }
25 }
26}
27
28pub struct Progress {
29 spin: Spinner,
30 style: Style,
31 size: usize,
32 max: u64,
33 value: std::sync::Mutex<u64>,
34 msg: std::sync::Mutex<String>,
35}
36
37pub fn progress() -> Progress {
38 Progress {
39 spin: spinner(),
40 style: Style::Heavy,
41 size: 40,
42 max: 100,
43 value: std::sync::Mutex::new(0),
44 msg: std::sync::Mutex::new(String::new()),
45 }
46}
47
48impl Progress {
49 pub fn style(mut self, s: Style) -> Self {
50 self.style = s;
51 self
52 }
53 pub fn size(mut self, n: usize) -> Self {
54 self.size = n.max(1);
55 self
56 }
57 pub fn max(mut self, n: u64) -> Self {
58 self.max = n.max(1);
59 self
60 }
61
62 pub fn start(&self, msg: impl Into<String>) {
63 let m = msg.into();
64 *self.msg.lock().unwrap() = m.clone();
65 self.spin.start(self.render(&m));
66 }
67
68 pub fn advance(&self, step: u64, msg: Option<String>) {
69 let mut v = self.value.lock().unwrap();
70 *v = (*v + step).min(self.max);
71 let value = *v;
72 drop(v);
73 if let Some(m) = msg {
74 *self.msg.lock().unwrap() = m;
75 }
76 let m = self.msg.lock().unwrap().clone();
77 self.spin.message(self.render_with(&m, value));
78 }
79
80 pub fn message(&self, msg: impl Into<String>) {
81 self.advance(0, Some(msg.into()));
82 }
83
84 pub fn stop(&self, msg: impl Into<String>) {
85 self.spin.stop(msg);
86 }
87 pub fn cancel(&self, msg: impl Into<String>) {
88 self.spin.cancel(msg);
89 }
90 pub fn error(&self, msg: impl Into<String>) {
91 self.spin.error(msg);
92 }
93
94 fn render(&self, msg: &str) -> String {
95 self.render_with(msg, *self.value.lock().unwrap())
96 }
97 fn render_with(&self, msg: &str, value: u64) -> String {
98 let filled = ((value as usize) * self.size) / (self.max as usize);
99 let ch = self.style.ch();
100 let on = ch.repeat(filled);
101 let off = ch.repeat(self.size - filled);
102 format!(
103 "{}{} {}",
104 paint(super::settings::colors().accent, &on),
105 paint(DIM, &off),
106 msg
107 )
108 }
109}