This repository has no description
1use chrono::DateTime;
2use console::Style;
3use indicatif::{ProgressBar, ProgressStyle};
4use itertools::Itertools;
5use std::borrow::Cow;
6use std::collections::HashMap;
7use std::ops::Range;
8use std::sync::{Arc, Mutex};
9use std::thread::{self, JoinHandle};
10use std::time::{self, Duration};
11
12pub const PROGRESS_BARS_STYLE: &str = "\x1b]9;4;1;{percent}\x1b\\{prefix:>12.bold.cyan} {percent:03}% [{bar:25}] {msg} ({elapsed} ago)";
13
14pub struct Spinner {
15 pub spinner: ProgressBar,
16 pub finished: Arc<Mutex<bool>>,
17 pub thread: JoinHandle<()>,
18}
19
20impl Spinner {
21 pub fn start(verb: &'static str, message: &str) -> Self {
22 let spinner = ProgressBar::new(0).with_style(
23 ProgressStyle::with_template(&format_log_msg_cyan(
24 verb,
25 &(message.to_owned() + " {spinner:.cyan}"),
26 ))
27 .unwrap(),
28 );
29 spinner.tick();
30
31 let thread_spinner = spinner.clone();
32 let finished = Arc::new(Mutex::new(false));
33 let thread_finished = Arc::clone(&finished);
34 let spinner_thread = thread::spawn(move || {
35 while !*thread_finished.lock().unwrap() {
36 thread_spinner.tick();
37 thread::sleep(time::Duration::from_millis(100));
38 }
39 thread_spinner.finish_and_clear();
40 });
41
42 Self {
43 spinner: spinner.clone(),
44 finished,
45 thread: spinner_thread,
46 }
47 }
48
49 pub fn end(self, message: &str) {
50 self.spinner.finish_and_clear();
51 *self.finished.lock().unwrap() = true;
52 self.thread.join().unwrap();
53 println!("{}", message);
54 }
55}
56
57pub fn setup_progress_bar(total: u64, verb: &'static str) -> ProgressBar {
58 indicatif::ProgressBar::new(total)
59 .with_prefix(verb)
60 .with_style(
61 indicatif::ProgressStyle::with_template(PROGRESS_BARS_STYLE)
62 .unwrap()
63 .progress_chars("=> "),
64 )
65 .with_finish(indicatif::ProgressFinish::WithMessage(
66 "\x1b]9;4;0\x1b\\".into(),
67 ))
68}
69
70pub trait Log {
71 fn log(&self, verb: &'static str, message: &str);
72}
73
74pub fn format_log_msg(verb: &'static str, message: &str) -> String {
75 let style = Style::new().bold().green();
76 format!("{} {}", style.apply_to(format!("{verb:>12}")), message)
77}
78
79pub fn format_log_msg_cyan(verb: &'static str, message: &str) -> String {
80 let style = Style::new().bold().cyan();
81 format!("{} {}", style.apply_to(format!("{verb:>12}")), message)
82}
83
84impl Log for ProgressBar {
85 fn log(&self, verb: &'static str, message: &str) {
86 self.println(format_log_msg(verb, message));
87 }
88}
89
90impl Log for Option<&ProgressBar> {
91 fn log(&self, verb: &'static str, message: &str) {
92 if let Some(pb) = self {
93 pb.println(format_log_msg(verb, message));
94 }
95 }
96}
97
98pub trait MaybeProgressBar<'a> {
99 fn set_message(&'a self, message: impl Into<Cow<'static, str>>);
100 fn set_length(&'a self, length: u64);
101 fn inc(&'a self, n: u64);
102 fn println(&'a self, message: impl AsRef<str>);
103}
104
105impl<'a> MaybeProgressBar<'a> for Option<&'a ProgressBar> {
106 fn set_message(&'a self, message: impl Into<Cow<'static, str>>) {
107 if let Some(pb) = self {
108 pb.set_message(message);
109 }
110 }
111
112 fn set_length(&'a self, length: u64) {
113 if let Some(pb) = self {
114 pb.set_length(length);
115 }
116 }
117
118 fn inc(&'a self, n: u64) {
119 if let Some(pb) = self {
120 pb.inc(n);
121 }
122 }
123
124 fn println(&'a self, message: impl AsRef<str>) {
125 if let Some(pb) = self {
126 pb.println(message);
127 }
128 }
129}
130
131pub fn display_counts(counts: HashMap<impl std::fmt::Display, usize>) -> String {
132 counts
133 .iter()
134 .filter_map(|(name, &count)| {
135 if count > 0 {
136 Some(format!("{count} {name}"))
137 } else {
138 None
139 }
140 })
141 .join(", ")
142}
143
144pub(crate) fn format_duration(duration: Duration) -> String {
145 let (hours, rest) = duration.as_millis().div_rem(&3_600_000);
146 let (minutes, rest) = rest.div_rem(&60_000);
147 let (seconds, milliseconds) = rest.div_rem(&1_000);
148
149 if hours > 0 {
150 format!("{} h {:02} m {:02} s", hours, minutes, seconds)
151 } else if minutes > 0 {
152 format!("{} m {:02} s", minutes, seconds)
153 } else if seconds > 0 {
154 format!("{}.{:03} s", seconds, milliseconds)
155 } else {
156 format!("{} ms", milliseconds)
157 }
158}
159
160trait DivRem<T> {
161 fn div_rem(&self, rhs: &T) -> (T, T);
162}
163
164impl DivRem<u128> for u128 {
165 fn div_rem(&self, rhs: &u128) -> (u128, u128) {
166 (self / rhs, self % rhs)
167 }
168}
169
170pub(crate) fn format_timestamp(ms: usize) -> String {
171 format!(
172 "{}",
173 DateTime::from_timestamp_millis(ms as i64)
174 .unwrap()
175 .format("%H:%M:%S%.3f")
176 )
177}
178
179pub(crate) fn format_timestamp_range(ms_range: &Range<usize>) -> String {
180 format!(
181 "from {} to {}",
182 format_timestamp(ms_range.start),
183 format_timestamp(ms_range.end)
184 )
185}
186
187pub(crate) fn format_filepath(path: &std::path::Path) -> String {
188 format!(
189 "{}{}",
190 if path.is_relative() { "./" } else { "" },
191 path.to_string_lossy()
192 )
193}