1use env_logger::{
2 Builder,
3 fmt::{
4 Color,
5 Style,
6 StyledValue,
7 },
8};
9use git_cliff_core::error::{
10 Error,
11 Result,
12};
13#[cfg(feature = "remote")]
14use indicatif::{
15 ProgressBar,
16 ProgressStyle,
17};
18use log::Level;
19use std::io::Write;
20use std::sync::atomic::{
21 AtomicUsize,
22 Ordering,
23};
24use std::{
25 env,
26 fmt,
27};
28
29const LOGGER_ENV: &str = "RUST_LOG";
31
32static MAX_MODULE_WIDTH: AtomicUsize = AtomicUsize::new(0);
34
35struct Padded<T> {
37 value: T,
38 width: usize,
39}
40
41impl<T: fmt::Display> fmt::Display for Padded<T> {
42 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
43 write!(f, "{: <width$}", self.value, width = self.width)
44 }
45}
46
47fn max_target_width(target: &str) -> usize {
49 let max_width = MAX_MODULE_WIDTH.load(Ordering::Relaxed);
50 if max_width < target.len() {
51 MAX_MODULE_WIDTH.store(target.len(), Ordering::Relaxed);
52 target.len()
53 } else {
54 max_width
55 }
56}
57
58fn colored_level(style: &mut Style, level: Level) -> StyledValue<'_, &'static str> {
60 match level {
61 Level::Trace => style.set_color(Color::Magenta).value("TRACE"),
62 Level::Debug => style.set_color(Color::Blue).value("DEBUG"),
63 Level::Info => style.set_color(Color::Green).value("INFO "),
64 Level::Warn => style.set_color(Color::Yellow).value("WARN "),
65 Level::Error => style.set_color(Color::Red).value("ERROR"),
66 }
67}
68
69#[cfg(feature = "remote")]
70lazy_static::lazy_static! {
71 pub static ref PROGRESS_BAR: ProgressBar = {
73 let progress_bar = ProgressBar::new_spinner();
74 progress_bar.set_style(
75 ProgressStyle::with_template("{spinner:.green} {msg}")
76 .unwrap()
77 .tick_strings(&[
78 "▹▹▹▹▹",
79 "▸▹▹▹▹",
80 "▹▸▹▹▹",
81 "▹▹▸▹▹",
82 "▹▹▹▸▹",
83 "▹▹▹▹▸",
84 "▪▪▪▪▪",
85 ]),
86 );
87 progress_bar
88 };
89}
90
91#[allow(unreachable_code, clippy::needless_return)]
96pub fn init() -> Result<()> {
97 let mut builder = Builder::new();
98 builder.format(move |f, record| {
99 let target = record.target();
100 let max_width = max_target_width(target);
101
102 let mut style = f.style();
103 let level = colored_level(&mut style, record.level());
104
105 let mut style = f.style();
106 let target = style.set_bold(true).value(Padded {
107 value: target,
108 width: max_width,
109 });
110
111 #[cfg(feature = "github")]
112 {
113 let message = record.args().to_string();
114 if message
115 .starts_with(git_cliff_core::remote::github::START_FETCHING_MSG)
116 {
117 PROGRESS_BAR
118 .enable_steady_tick(std::time::Duration::from_millis(80));
119 PROGRESS_BAR.set_message(message);
120 return Ok(());
121 } else if message
122 .starts_with(git_cliff_core::remote::github::FINISHED_FETCHING_MSG)
123 {
124 PROGRESS_BAR.finish_and_clear();
125 return Ok(());
126 }
127 }
128
129 #[cfg(feature = "gitlab")]
130 {
131 let message = record.args().to_string();
132 if message
133 .starts_with(git_cliff_core::remote::gitlab::START_FETCHING_MSG)
134 {
135 PROGRESS_BAR
136 .enable_steady_tick(std::time::Duration::from_millis(80));
137 PROGRESS_BAR.set_message(message);
138 return Ok(());
139 } else if message
140 .starts_with(git_cliff_core::remote::gitlab::FINISHED_FETCHING_MSG)
141 {
142 PROGRESS_BAR.finish_and_clear();
143 return Ok(());
144 }
145 }
146
147 #[cfg(feature = "gitea")]
148 {
149 let message = record.args().to_string();
150 if message.starts_with(git_cliff_core::remote::gitea::START_FETCHING_MSG)
151 {
152 PROGRESS_BAR
153 .enable_steady_tick(std::time::Duration::from_millis(80));
154 PROGRESS_BAR.set_message(message);
155 return Ok(());
156 } else if message
157 .starts_with(git_cliff_core::remote::gitea::FINISHED_FETCHING_MSG)
158 {
159 PROGRESS_BAR.finish_and_clear();
160 return Ok(());
161 }
162 }
163
164 #[cfg(feature = "bitbucket")]
165 {
166 let message = record.args().to_string();
167 if message
168 .starts_with(git_cliff_core::remote::bitbucket::START_FETCHING_MSG)
169 {
170 PROGRESS_BAR
171 .enable_steady_tick(std::time::Duration::from_millis(80));
172 PROGRESS_BAR.set_message(message);
173 return Ok(());
174 } else if message.starts_with(
175 git_cliff_core::remote::bitbucket::FINISHED_FETCHING_MSG,
176 ) {
177 PROGRESS_BAR.finish_and_clear();
178 return Ok(());
179 }
180 }
181
182 writeln!(f, " {} {} > {}", level, target, record.args())
183 });
184
185 if let Ok(var) = env::var(LOGGER_ENV) {
186 builder.parse_filters(&var);
187 }
188
189 builder
190 .try_init()
191 .map_err(|e| Error::LoggerError(e.to_string()))
192}