This repository has no description
0

Configure Feed

Select the types of activity you want to include in your feed.

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}