1mod config;
55
56pub use config::Config;
57use indicatif::{
58 MultiProgress, ProgressBar, ProgressBarIter, ProgressDrawTarget, ProgressState, ProgressStyle,
59};
60use std::{borrow::Cow, fmt::Write, sync::OnceLock};
61
62#[cfg(feature = "rayon")]
63mod parallel;
64#[cfg(feature = "rayon")]
65pub use parallel::{par_tqdm, ParTqdm};
66
67pub trait Tqdm<I>: Sized {
69 fn tqdm_config(self, config: Config) -> ProgressBarIter<I>;
72 fn tqdm(self) -> ProgressBarIter<I> {
74 self.tqdm_config(Config::default())
75 }
76}
77
78static BARS: OnceLock<MultiProgress> = OnceLock::new();
79
80impl<I: Iterator> Tqdm<I> for I {
81 fn tqdm_config(self, config: Config) -> ProgressBarIter<I> {
82 let bars = BARS.get_or_init(MultiProgress::new);
83 bars.add(progress_bar(config, self.size_hint().1)).wrap_iter(self)
84 }
85}
86
87pub fn tqdm<I: ExactSizeIterator>(iter: I) -> ProgressBarIter<I> {
89 iter.tqdm()
90}
91
92fn progress_bar(config: Config, iter_len: Option<usize>) -> ProgressBar {
93 let len = match config.total {
94 Some(total) => Some((total * config.unit_scale) as u64),
95 None => iter_len.map(|int| int as u64),
96 };
97
98 let draw_target =
99 if config.disable { ProgressDrawTarget::hidden() } else { ProgressDrawTarget::stderr() };
100
101 ProgressBar::with_draw_target(len, draw_target)
102 .with_finish(config.progress_finish())
103 .with_prefix(config.prefix)
104 .with_style(style(
105 config.unit,
106 config.unit_scale,
107 config.postfix,
108 &config.progress_chars,
109 &config.colour,
110 ))
111}
112
113#[allow(clippy::float_cmp)]
114fn style(
115 unit: Cow<'static, str>,
116 unit_scale: f64,
117 postfix: Cow<'static, str>,
118 progress_chars: &str,
119 color: &str,
120) -> ProgressStyle {
121 ProgressStyle::with_template(
122 &format!("{{prefix}}{{percent}}|{{wide_bar:.{color}}}| {{pos}}/{{len}} [{{elapsed}}<{{eta}}, {{per_sec}}{{postfix}}]",
123 ))
124 .unwrap()
125 .with_key(
126 "per_sec",
127 move |state: &ProgressState, w: &mut dyn Write| {
128 let _ = write!(w, "{:.2}{}/s", unit_scale * state.per_sec(), unit);
129 },
130 )
131 .with_key("percent", |state: &ProgressState, w: &mut dyn Write| {
132 let _ = write!(w, "{: >3}%", (state.fraction() * 100.0) as i32);
133 })
134 .with_key("elapsed", |state: &ProgressState, w: &mut dyn Write| {
135 let duration = state.elapsed();
136 let minutes = duration.as_secs() / 60;
137 let seconds = duration.as_secs() % 60;
138 let _ = write!(w, "{minutes:0>2}:{seconds:0>2}");
139 })
140 .with_key("eta", |state: &ProgressState, w: &mut dyn Write| {
141 let duration = state.eta();
142 let minutes = duration.as_secs() / 60;
143 let seconds = duration.as_secs() % 60;
144 let _ = write!(w, "{minutes:0>2}:{seconds:0>2}");
145 })
146 .with_key("pos", move |state: &ProgressState, w: &mut dyn Write| {
147 if unit_scale.round() == unit_scale {
148 let _ = write!(w, "{:?}", unit_scale as i64 * state.pos() as i64);
149 } else {
150 let _ = write!(w, "{:?}", unit_scale * state.pos() as f64);
151 }
152 })
153 .with_key("len", move |state: &ProgressState, w: &mut dyn Write| {
154 if unit_scale.round() == unit_scale {
155 let state_len = state.len().unwrap_or(state.pos()) as i64;
156 let _ = write!(w, "{:?}", unit_scale as i64 * state_len);
157 } else {
158 let state_len = state.len().unwrap_or(state.pos()) as f64;
159 let _ = write!(w, "{:?}", unit_scale * state_len);
160 }
161 })
162 .with_key("postfix", move |_: &ProgressState, w: &mut dyn Write| {
163 let _ = write!(w, "{postfix}");
164 })
165 .progress_chars(progress_chars)
166}