@@ -9,6 +9,10 @@ use std::{
9
9
io:: { self , BufRead , StdoutLock , Write } ,
10
10
} ;
11
11
12
+ pub const PROGRESS_FAILED_COLOR : Color = Color :: Red ;
13
+ pub const PROGRESS_SUCCESS_COLOR : Color = Color :: Green ;
14
+ pub const PROGRESS_PENDING_COLOR : Color = Color :: Blue ;
15
+
12
16
pub struct MaxLenWriter < ' a , ' b > {
13
17
pub stdout : & ' a mut StdoutLock < ' b > ,
14
18
len : usize ,
@@ -85,15 +89,26 @@ impl<'a> CountedWrite<'a> for StdoutLock<'a> {
85
89
}
86
90
}
87
91
88
- /// Terminal progress bar to be used when not using Ratataui.
92
+ /// Simple terminal progress bar
89
93
pub fn progress_bar < ' a > (
90
94
writer : & mut impl CountedWrite < ' a > ,
91
95
progress : u16 ,
92
96
total : u16 ,
93
97
line_width : u16 ,
98
+ ) -> io:: Result < ( ) > {
99
+ progress_bar_with_success ( writer, 0 , 0 , progress, total, line_width)
100
+ }
101
+ /// Terminal progress bar with three states (pending + failed + success)
102
+ pub fn progress_bar_with_success < ' a > (
103
+ writer : & mut impl CountedWrite < ' a > ,
104
+ pending : u16 ,
105
+ failed : u16 ,
106
+ success : u16 ,
107
+ total : u16 ,
108
+ line_width : u16 ,
94
109
) -> io:: Result < ( ) > {
95
110
debug_assert ! ( total < 1000 ) ;
96
- debug_assert ! ( progress <= total) ;
111
+ debug_assert ! ( ( pending + failed + success ) <= total) ;
97
112
98
113
const PREFIX : & [ u8 ] = b"Progress: [" ;
99
114
const PREFIX_WIDTH : u16 = PREFIX . len ( ) as u16 ;
@@ -104,25 +119,67 @@ pub fn progress_bar<'a>(
104
119
if line_width < MIN_LINE_WIDTH {
105
120
writer. write_ascii ( b"Progress: " ) ?;
106
121
// Integers are in ASCII.
107
- return writer. write_ascii ( format ! ( "{progress }/{total}" ) . as_bytes ( ) ) ;
122
+ return writer. write_ascii ( format ! ( "{}/{total}" , failed + success ) . as_bytes ( ) ) ;
108
123
}
109
124
110
125
let stdout = writer. stdout ( ) ;
111
126
stdout. write_all ( PREFIX ) ?;
112
127
113
128
let width = line_width - WRAPPER_WIDTH ;
114
- let filled = ( width * progress) / total;
129
+ let mut failed_end = ( width * failed) / total;
130
+ let mut success_end = ( width * ( failed + success) ) / total;
131
+ let mut pending_end = ( width * ( failed + success + pending) ) / total;
132
+
133
+ // In case the range boundaries overlap, "pending" has priority over both
134
+ // "failed" and "success" (don't show the bar as "complete" when we are
135
+ // still checking some things).
136
+ // "Failed" has priority over "success" (don't show 100% success if we
137
+ // have some failures, at the risk of showing 100% failures even with
138
+ // a few successes).
139
+ //
140
+ // "Failed" already has priority over "success" because it's displayed
141
+ // first. But "pending" is last so we need to fix "success"/"failed".
142
+ if pending > 0 {
143
+ pending_end = pending_end. max ( 1 ) ;
144
+ if pending_end == success_end {
145
+ success_end -= 1 ;
146
+ }
147
+ if pending_end == failed_end {
148
+ failed_end -= 1 ;
149
+ }
115
150
116
- stdout. queue ( SetForegroundColor ( Color :: Green ) ) ?;
117
- for _ in 0 ..filled {
151
+ // This will replace the last character of the "pending" range with
152
+ // the arrow char ('>'). This ensures that even if the progress bar
153
+ // is filled (everything either done or pending), we'll still see
154
+ // the '>' as long as we are not fully done.
155
+ pending_end -= 1 ;
156
+ }
157
+
158
+ if failed > 0 {
159
+ stdout. queue ( SetForegroundColor ( PROGRESS_FAILED_COLOR ) ) ?;
160
+ for _ in 0 ..failed_end {
161
+ stdout. write_all ( b"#" ) ?;
162
+ }
163
+ }
164
+
165
+ stdout. queue ( SetForegroundColor ( PROGRESS_SUCCESS_COLOR ) ) ?;
166
+ for _ in failed_end..success_end {
118
167
stdout. write_all ( b"#" ) ?;
119
168
}
120
169
121
- if filled < width {
170
+ if pending > 0 {
171
+ stdout. queue ( SetForegroundColor ( PROGRESS_PENDING_COLOR ) ) ?;
172
+
173
+ for _ in success_end..pending_end {
174
+ stdout. write_all ( b"#" ) ?;
175
+ }
176
+ }
177
+
178
+ if pending_end < width {
122
179
stdout. write_all ( b">" ) ?;
123
180
}
124
181
125
- let width_minus_filled = width - filled ;
182
+ let width_minus_filled = width - pending_end ;
126
183
if width_minus_filled > 1 {
127
184
let red_part_width = width_minus_filled - 1 ;
128
185
stdout. queue ( SetForegroundColor ( Color :: Red ) ) ?;
@@ -133,7 +190,7 @@ pub fn progress_bar<'a>(
133
190
134
191
stdout. queue ( SetForegroundColor ( Color :: Reset ) ) ?;
135
192
136
- write ! ( stdout, "] {progress :>3}/{total}" )
193
+ write ! ( stdout, "] {:>3}/{}" , failed + success , total )
137
194
}
138
195
139
196
pub fn clear_terminal ( stdout : & mut StdoutLock ) -> io:: Result < ( ) > {
0 commit comments