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