termal_core/progress/
dots.rs1use std::{
2 fmt::{Display, Write as _},
3 io::{Write as _, stdout},
4 time::Duration,
5};
6
7use crate::{
8 codes,
9 progress::{ProgressFormatter, duration_to_string},
10};
11
12pub struct Dots {
14 pub log: bool,
15 buf: String,
16}
17
18pub fn dots<T>(task: &str, f: impl FnOnce() -> T) -> T {
20 let mut d = Dots::default();
21 d.start(task, "");
22 let res = f();
23 d.finish(task, "", Duration::default());
24 res
25}
26
27pub fn dots_try<T, E: Display>(
29 task: &str,
30 f: impl FnOnce() -> Result<T, E>,
31) -> Result<T, E> {
32 let mut d = Dots::default();
33 d.start(task, "");
34 match f() {
35 res @ Ok(_) => {
36 d.finish(task, "", Duration::default());
37 res
38 }
39 Err(e) => {
40 d.fail(None, task, "", Duration::default(), &format!("{e}"));
41 Err(e)
42 }
43 }
44}
45
46impl Dots {
47 fn show_progress(
48 &mut self,
49 done: Option<f32>,
50 task: &str,
51 info: &str,
52 eta: Option<Duration>,
53 ) {
54 self.buf.clear();
55 self.buf += codes::ERASE_TO_END;
56 self.buf += codes::CUR_SAVE;
57 self.format_progress(done, task, info, eta);
58 self.buf += codes::CUR_LOAD;
59 print!("{}", self.buf);
60 _ = stdout().flush();
61 if self.log {
62 print!("{}", codes::ERASE_TO_END);
63 }
64 }
65
66 fn format_progress(
67 &mut self,
68 done: Option<f32>,
69 task: &str,
70 info: &str,
71 eta: Option<Duration>,
72 ) {
73 self.buf += task;
74 self.buf += "...";
75
76 if let Some(done) = done {
77 _ = write!(self.buf, " \x1b[96m{:.2} %\x1b[0m", done * 100.);
78 }
79
80 if let Some(time) = eta {
81 self.buf += " [\x1b[95m";
82 duration_to_string(time, true, &mut self.buf);
83 self.buf += "\x1b[0m]"
84 }
85
86 if !info.is_empty() {
87 self.buf.push(' ');
88 self.buf += info;
89 }
90 }
91}
92
93impl ProgressFormatter for Dots {
94 fn start(&mut self, task: &str, info: &str) {
95 self.show_progress(None, task, info, None);
96 }
97
98 fn update(
99 &mut self,
100 done: Option<f32>,
101 task: &str,
102 info: &str,
103 time: Duration,
104 ) {
105 self.show_progress(done, task, info, Some(time));
106 }
107
108 fn finish(&mut self, task: &str, _: &str, _: Duration) {
109 self.buf.clear();
110 self.buf += codes::ERASE_TO_END;
111 self.format_progress(None, task, "\x1b[92mDone!\x1b[0m", None);
112 println!("{}", self.buf);
113 }
114
115 fn fail(
116 &mut self,
117 _: Option<f32>,
118 task: &str,
119 _: &str,
120 _: Duration,
121 err: &str,
122 ) {
123 self.buf.clear();
124 self.buf += codes::ERASE_TO_END;
125 self.format_progress(None, task, "\x1b[91mFailed!\x1b[0m", None);
126 println!("{}\n\x1b[91merror:\x1b[0m {err}", self.buf);
127 }
128}
129
130impl Default for Dots {
131 fn default() -> Self {
132 Self {
133 log: true,
134 buf: String::new(),
135 }
136 }
137}