11use crate :: prelude:: * ;
2+ use owo_colors:: { AnsiColors , OwoColorize } ;
23
34use std:: {
45 ops:: AddAssign ,
56 sync:: atomic:: { AtomicI64 , Ordering :: Relaxed } ,
67} ;
78
9+ /// Check if stdout is a TTY
10+ fn is_stdout_tty ( ) -> bool {
11+ atty:: is ( atty:: Stream :: Stdout )
12+ }
13+
814#[ derive( Default , Serialize ) ]
915pub struct Counter ( pub AtomicI64 ) ;
1016
@@ -107,6 +113,10 @@ pub struct UpdateStats {
107113 pub num_errors : Counter ,
108114 /// Processing counters for tracking in-process rows.
109115 pub processing : ProcessingCounters ,
116+ /// Cumulative total count of items (for display purposes)
117+ /// This represents the actual total after applying additions and deletions
118+ #[ serde( skip) ]
119+ pub cumulative_total : Counter ,
110120}
111121
112122impl UpdateStats {
@@ -119,6 +129,7 @@ impl UpdateStats {
119129 num_reprocesses : self . num_reprocesses . delta ( & base. num_reprocesses ) ,
120130 num_errors : self . num_errors . delta ( & base. num_errors ) ,
121131 processing : self . processing . delta ( & base. processing ) ,
132+ cumulative_total : self . cumulative_total . clone ( ) ,
122133 }
123134 }
124135
@@ -130,6 +141,9 @@ impl UpdateStats {
130141 self . num_reprocesses . merge ( & delta. num_reprocesses ) ;
131142 self . num_errors . merge ( & delta. num_errors ) ;
132143 self . processing . merge ( & delta. processing ) ;
144+ // Update cumulative total: add insertions, subtract deletions
145+ let net_change = delta. num_insertions . get ( ) - delta. num_deletions . get ( ) ;
146+ self . cumulative_total . inc ( net_change) ;
133147 }
134148
135149 pub fn has_any_change ( & self ) -> bool {
@@ -205,13 +219,13 @@ impl std::fmt::Display for UpdateStats {
205219 // Progress bar segments
206220 if total > 0 {
207221 if num_insertions > 0 {
208- segments. push ( ( num_insertions, "+" , format ! ( "\x1B [90m (+{} added)\x1B [0m " , num_insertions) ) ) ;
222+ segments. push ( ( num_insertions, "+" , format ! ( "(+{} added)" , num_insertions) ) ) ;
209223 }
210224 if num_deletions > 0 {
211- segments. push ( ( num_deletions, "-" , format ! ( "\x1B [90m (-{} removed)\x1B [0m " , num_deletions) ) ) ;
225+ segments. push ( ( num_deletions, "-" , format ! ( "(-{} removed)" , num_deletions) ) ) ;
212226 }
213227 if num_updates > 0 {
214- segments. push ( ( num_updates, "~" , format ! ( "\x1B [90m (~{} updated)\x1B [0m " , num_updates) ) ) ;
228+ segments. push ( ( num_updates, "~" , format ! ( "(~{} updated)" , num_updates) ) ) ;
215229 }
216230 if num_no_change > 0 {
217231 segments. push ( ( num_no_change, " " , "" . to_string ( ) ) ) ;
@@ -220,7 +234,12 @@ impl std::fmt::Display for UpdateStats {
220234
221235 // Error handling
222236 if num_errors > 0 {
223- write ! ( f, "{} rows failed" , num_errors) ?;
237+ let tty = is_stdout_tty ( ) ;
238+ if tty {
239+ write ! ( f, "{}" , format!( "{} rows failed" , num_errors) . color( AnsiColors :: White ) ) ?;
240+ } else {
241+ write ! ( f, "{} rows failed" , num_errors) ?;
242+ }
224243 if !segments. is_empty ( ) {
225244 write ! ( f, "; " ) ?;
226245 }
@@ -235,7 +254,7 @@ impl std::fmt::Display for UpdateStats {
235254 let bar_width = 40 ;
236255 let mut bar = String :: new ( ) ;
237256
238- let percentage = ( ( total - num_in_process) as f64 / total as f64 * 100.0 ) as i64 ;
257+ let _percentage = ( ( total - num_in_process) as f64 / total as f64 * 100.0 ) as i64 ;
239258 let mut remaining_width = bar_width;
240259
241260 for ( count, segment_type, _) in sorted_segments. iter ( ) {
@@ -273,29 +292,49 @@ impl std::fmt::Display for UpdateStats {
273292 if remaining_width > 0 {
274293 bar. push_str ( & " " . repeat ( remaining_width) ) ;
275294 }
276- write ! ( f, "[{}] {}/{} records " , bar, total - num_in_process, total) ?;
295+ let tty = is_stdout_tty ( ) ;
296+ // Use total from current operations - this represents the actual record count
297+ if tty {
298+ write ! ( f, "[{}] {}/{} records " , bar. color( AnsiColors :: BrightBlack ) , total - num_in_process, total) ?;
299+ } else {
300+ write ! ( f, "[{}] {}/{} records " , bar, total - num_in_process, total) ?;
301+ }
277302
278- // Add segment labels
303+ // Add segment labels with different grey shades for each segment type
279304 let mut first = true ;
280- for ( _, _ , label) in segments. iter ( ) {
305+ for ( _, segment_type , label) in segments. iter ( ) {
281306 if !label. is_empty ( ) {
282307 if !first {
283308 write ! ( f, " " ) ?;
284309 }
285- write ! ( f, "{}" , label) ?;
310+ if tty {
311+ match * segment_type {
312+ "+" => write ! ( f, "{}" , label. color( AnsiColors :: BrightBlack ) ) ?, // Lightest grey for additions
313+ "-" => write ! ( f, "{}" , label. color( AnsiColors :: White ) ) ?, // White for removals
314+ "~" => write ! ( f, "{}" , label. color( AnsiColors :: Black ) ) ?, // Dark grey for updates
315+ _ => write ! ( f, "{}" , label. color( AnsiColors :: Black ) ) ?, // Black for no-change
316+ }
317+ } else {
318+ write ! ( f, "{}" , label) ?;
319+ }
286320 first = false ;
287321 }
288322 }
289323 } else {
290324 write ! ( f, "No changes" ) ?;
291325 }
292326
293- // In-process info
327+ // In-process info with grey coloring
294328 if num_in_process > 0 {
295329 if !segments. is_empty ( ) {
296330 write ! ( f, " " ) ?;
297331 }
298- write ! ( f, "({} in process)" , num_in_process) ?;
332+ let tty = is_stdout_tty ( ) ;
333+ if tty {
334+ write ! ( f, "{}" , format!( "({} in process)" , num_in_process) . color( AnsiColors :: Black ) ) ?;
335+ } else {
336+ write ! ( f, "({} in process)" , num_in_process) ?;
337+ }
299338 }
300339
301340 Ok ( ( ) )
@@ -567,17 +606,19 @@ mod tests {
567606 // Test with no activity
568607 assert_eq ! ( format!( "{}" , stats) , "No changes" ) ;
569608
570- // Test with in-process rows
609+ // Test with in-process rows (no segments yet, so just shows in-process)
571610 stats. processing . start ( 5 ) ;
572- assert ! ( format!( "{}" , stats) . contains( "5 source rows IN PROCESS" ) ) ;
611+ let display = format ! ( "{}" , stats) ;
612+ assert ! ( display. contains( "5 in process" ) ) ;
573613
574614 // Test with mixed activity
575615 stats. num_insertions . inc ( 3 ) ;
576616 stats. num_errors . inc ( 1 ) ;
617+ stats. cumulative_total . inc ( 3 ) ;
577618 let display = format ! ( "{}" , stats) ;
578- assert ! ( display. contains( "1 source rows FAILED " ) ) ;
579- assert ! ( display. contains( "3 source rows processed " ) ) ;
580- assert ! ( display. contains( "5 source rows IN PROCESS " ) ) ;
619+ assert ! ( display. contains( "1 rows failed " ) ) ;
620+ assert ! ( display. contains( "(+3 added) " ) ) ;
621+ assert ! ( display. contains( "5 in process " ) ) ;
581622 }
582623
583624 #[ test]
0 commit comments