1#![forbid(unsafe_code)]
34#![warn(missing_debug_implementations)]
35#![warn(missing_docs)]
36
37use std::sync::Mutex;
38
39#[macro_use]
40extern crate lazy_static;
41
42lazy_static! {
43 static ref TEXT: Mutex<String> = Mutex::new(String::new());
44}
45
46#[derive(Debug)]
48pub struct Tqdm;
49
50impl Tqdm {
51 #[allow(clippy::new_ret_no_self)]
53 pub fn new<I: Iterator>(iter: I) -> TqdmAuto<I> {
54 let total = iter.size_hint().0;
55 TqdmAuto {
56 iter,
57 current: 0,
58 total,
59 }
60 }
61
62 pub fn manual(total: usize) -> TqdmManual {
64 TqdmManual { current: 0, total }
65 }
66}
67
68pub fn write(text: &str) {
71 let mut msg = TEXT.lock().unwrap();
72 *msg = String::from(text);
73}
74
75fn clear_previous_line() {
76 print!("\x1b[1A");
77 print!("\r");
78 let size = terminal_size::terminal_size()
79 .unwrap_or((terminal_size::Width(120), terminal_size::Height(120)));
80 let width = size.0 .0 as usize;
81 print!("{}", " ".repeat(width));
82 print!("\r");
83}
84
85trait WriteCon {
86 fn get_current_amount(&self) -> usize {
87 0
88 }
89
90 fn get_total_amount(&self) -> usize {
91 0
92 }
93
94 fn get_percentage(&self) -> usize {
95 let fraction = self.get_current_amount() as f32 / self.get_total_amount() as f32;
96 (fraction * 100.0).round() as usize
97 }
98
99 fn create_bar(&self) -> String {
100 let percents = self.get_percentage();
101 let current = self.get_current_amount();
102 let total = self.get_total_amount();
103 let length = total.to_string().len();
104 let mut bar = String::new();
105
106 bar += "[";
107 for x in 1..=20 {
108 if x * 5 <= percents {
109 bar += "#"
110 } else {
111 bar += " "
112 }
113 }
114 bar += "]";
115
116 format!(
117 "{:>3}% {} {:>length$}/{}",
118 percents,
119 bar,
120 current,
121 total,
122 length = length
123 )
124 }
125
126 fn display(&self) {
127 let bar = self.create_bar();
128 let mut text = TEXT.lock().unwrap();
129
130 if self.get_current_amount() == 0 {
131 return;
132 }
133
134 let lines = text.matches('\n').count();
135 if self.get_current_amount() == 1 && lines > 0 {
136 for _ in 0..=lines + 1 {
137 println!();
138 }
139 }
140 for _ in 0..lines {
141 clear_previous_line();
142 }
143 if lines > 0 {
144 clear_previous_line();
145 clear_previous_line();
146 }
147
148 if !text.is_empty() {
149 println!("{}", text);
150 *text = String::new();
151 }
152
153 println!("{}", bar);
154 }
155}
156
157#[derive(Debug)]
159pub struct TqdmAuto<I: Iterator> {
160 iter: I,
161 current: usize,
162 total: usize,
163}
164
165impl<I: Iterator> Iterator for TqdmAuto<I> {
166 type Item = I::Item;
167 fn next(&mut self) -> Option<Self::Item> {
168 let next = self.iter.next();
169
170 #[allow(clippy::branches_sharing_code)]
171 if next.is_some() {
172 self.display();
173 self.current += 1;
174 next
175 } else {
176 self.display();
177 None
178 }
179 }
180}
181
182impl<I: Iterator> WriteCon for TqdmAuto<I> {
183 fn get_current_amount(&self) -> usize {
184 self.current
185 }
186 fn get_total_amount(&self) -> usize {
187 self.total
188 }
189}
190
191#[derive(Debug, Copy, Clone)]
193pub struct TqdmManual {
194 current: usize,
195 total: usize,
196}
197
198impl TqdmManual {
199 pub fn update(mut self, num: usize) {
201 self.current = std::cmp::min(self.total, self.current + num);
202 self.display();
203 }
204}
205
206impl WriteCon for TqdmManual {
207 fn get_current_amount(&self) -> usize {
208 self.current
209 }
210
211 fn get_total_amount(&self) -> usize {
212 self.total
213 }
214}