1use std::sync::{Arc, Mutex, Weak};
4
5use console::style;
6use nom::lib::std::fmt::Display;
7
8use crate::progress::{ProgressBar, ProgressTracker};
9use chrono::Local;
10
11#[derive(Debug, Clone, Copy)]
13pub enum ProgressBarLength {
14 Items(u64),
15 Bytes(u64),
16 Unknown,
17}
18
19#[derive(Debug, Clone, Copy)]
20pub enum LogLevel {
21 Info,
22 Warn,
23 Error,
24}
25
26pub trait Log: Sync + Send {
28 fn progress_bar(&self, msg: &str, len: ProgressBarLength) -> Arc<dyn ProgressTracker>;
30
31 fn log(&self, level: LogLevel, msg: String);
33}
34
35pub trait LogExt {
37 fn info(&self, msg: impl Display);
39 fn warn(&self, msg: impl Display);
41 fn err(&self, msg: impl Display);
43}
44
45impl<L: Log + ?Sized> LogExt for L {
47 fn info(&self, msg: impl Display) {
49 self.log(LogLevel::Info, msg.to_string())
50 }
51
52 fn warn(&self, msg: impl Display) {
54 self.log(LogLevel::Warn, msg.to_string())
55 }
56
57 fn err(&self, msg: impl Display) {
59 self.log(LogLevel::Error, msg.to_string())
60 }
61}
62
63pub struct StdLog {
65 program_name: String,
66 progress_bar: Mutex<Weak<ProgressBar>>,
67 pub log_stderr_to_stdout: bool,
68 pub no_progress: bool,
69}
70
71impl StdLog {
72 pub fn new() -> StdLog {
73 StdLog {
74 progress_bar: Mutex::new(Weak::default()),
75 program_name: std::env::current_exe()
76 .unwrap()
77 .file_name()
78 .unwrap()
79 .to_string_lossy()
80 .to_string(),
81 log_stderr_to_stdout: false,
82 no_progress: false,
83 }
84 }
85
86 pub fn spinner(&self, msg: &str) -> Arc<ProgressBar> {
88 if self.no_progress {
89 return Arc::new(ProgressBar::new_hidden());
90 }
91 self.progress_bar
92 .lock()
93 .unwrap()
94 .upgrade()
95 .iter()
96 .for_each(|pb| pb.finish_and_clear());
97 let result = Arc::new(ProgressBar::new_spinner(msg));
98 *self.progress_bar.lock().unwrap() = Arc::downgrade(&result);
99 result
100 }
101
102 pub fn progress_bar(&self, msg: &str, len: u64) -> Arc<ProgressBar> {
104 if self.no_progress {
105 return Arc::new(ProgressBar::new_hidden());
106 }
107 let result = Arc::new(ProgressBar::new_progress_bar(msg, len));
108 *self.progress_bar.lock().unwrap() = Arc::downgrade(&result);
109 result
110 }
111
112 pub fn hidden(&self) -> Arc<ProgressBar> {
114 Arc::new(ProgressBar::new_hidden())
115 }
116
117 pub fn bytes_progress_bar(&self, msg: &str, len: u64) -> Arc<ProgressBar> {
119 if self.no_progress {
120 return Arc::new(ProgressBar::new_hidden());
121 }
122 self.progress_bar
123 .lock()
124 .unwrap()
125 .upgrade()
126 .iter()
127 .for_each(|pb| pb.finish_and_clear());
128 let result = Arc::new(ProgressBar::new_bytes_progress_bar(msg, len));
129 *self.progress_bar.lock().unwrap() = Arc::downgrade(&result);
130 result
131 }
132
133 fn eprintln<I: Display>(&self, msg: I) {
136 match self.progress_bar.lock().unwrap().upgrade() {
137 Some(pb) if pb.is_visible() => pb.eprintln(format!("{msg}")),
138 _ if self.log_stderr_to_stdout => println!("{msg}"),
139 _ => eprintln!("{msg}"),
140 }
141 }
142
143 const TIMESTAMP_FMT: &'static str = "[%Y-%m-%d %H:%M:%S.%3f]";
144}
145
146impl Log for StdLog {
147 fn progress_bar(&self, msg: &str, len: ProgressBarLength) -> Arc<dyn ProgressTracker> {
148 match len {
149 ProgressBarLength::Items(count) => self.progress_bar(msg, count),
150 ProgressBarLength::Bytes(count) => self.bytes_progress_bar(msg, count),
151 ProgressBarLength::Unknown => self.spinner(msg),
152 }
153 }
154
155 fn log(&self, level: LogLevel, msg: String) {
156 let timestamp = Local::now();
157 let level = match level {
158 LogLevel::Info => style(" info:").for_stderr().green(),
159 LogLevel::Warn => style("warn:").for_stderr().yellow(),
160 LogLevel::Error => style("error:").for_stderr().red(),
161 };
162 let msg = format!(
163 "{} {}: {} {}",
164 style(timestamp.format(Self::TIMESTAMP_FMT))
165 .for_stderr()
166 .dim()
167 .white(),
168 style(&self.program_name).for_stderr().yellow(),
169 level,
170 msg
171 );
172 self.eprintln(msg);
173 }
174}
175
176impl Default for StdLog {
177 fn default() -> Self {
178 StdLog::new()
179 }
180}