git_same/output/progress/
sync.rs1use crate::git::{FetchResult, PullResult};
2use crate::operations::sync::SyncProgress;
3use crate::output::Verbosity;
4use crate::types::OwnedRepo;
5use console::style;
6use indicatif::{MultiProgress, ProgressBar};
7use std::path::Path;
8use std::sync::atomic::{AtomicUsize, Ordering};
9use std::sync::Arc;
10
11use super::styles::progress_style;
12
13pub struct SyncProgressBar {
15 #[allow(dead_code)]
16 multi: MultiProgress,
17 main_bar: ProgressBar,
18 verbosity: Verbosity,
19 updates_count: Arc<AtomicUsize>,
20}
21
22impl SyncProgressBar {
23 pub fn new(total: usize, verbosity: Verbosity, operation: &str) -> Self {
25 let multi = MultiProgress::new();
26 let main_bar = multi.add(ProgressBar::new(total as u64));
27 main_bar.set_style(progress_style());
28 main_bar.set_message(format!("{}ing repositories...", operation));
29 main_bar.enable_steady_tick(std::time::Duration::from_millis(100));
30
31 Self {
32 multi,
33 main_bar,
34 verbosity,
35 updates_count: Arc::new(AtomicUsize::new(0)),
36 }
37 }
38
39 pub fn finish(&self, success: usize, failed: usize, skipped: usize) {
41 let updates = self.updates_count.load(Ordering::SeqCst);
42 let msg = format!(
43 "{} {} synced ({} with updates), {} failed, {} skipped",
44 style("✓").green(),
45 success,
46 updates,
47 failed,
48 skipped
49 );
50 self.main_bar.finish_with_message(msg);
51 }
52}
53
54impl SyncProgress for SyncProgressBar {
55 fn on_start(&self, repo: &OwnedRepo, _path: &Path, _index: usize, _total: usize) {
56 if self.verbosity >= Verbosity::Verbose {
57 self.main_bar
58 .set_message(format!("Syncing {}...", style(repo.full_name()).cyan()));
59 }
60 }
61
62 fn on_fetch_complete(
63 &self,
64 repo: &OwnedRepo,
65 result: &FetchResult,
66 _index: usize,
67 _total: usize,
68 ) {
69 self.main_bar.inc(1);
70 if result.updated {
71 self.updates_count.fetch_add(1, Ordering::SeqCst);
72 }
73 if self.verbosity >= Verbosity::Debug {
74 let status = if result.updated {
75 "updated"
76 } else {
77 "up to date"
78 };
79 self.main_bar.suspend(|| {
80 println!(
81 "{} {} {}",
82 style("✓").green(),
83 repo.full_name(),
84 style(status).dim()
85 );
86 });
87 }
88 }
89
90 fn on_pull_complete(
91 &self,
92 repo: &OwnedRepo,
93 result: &PullResult,
94 _index: usize,
95 _total: usize,
96 ) {
97 self.main_bar.inc(1);
98 if result.updated {
99 self.updates_count.fetch_add(1, Ordering::SeqCst);
100 }
101 if self.verbosity >= Verbosity::Debug {
102 let status = if !result.updated {
103 "up to date"
104 } else if result.fast_forward {
105 "fast-forward"
106 } else {
107 "merged"
108 };
109 self.main_bar.suspend(|| {
110 println!(
111 "{} {} {}",
112 style("✓").green(),
113 repo.full_name(),
114 style(status).dim()
115 );
116 });
117 }
118 }
119
120 fn on_error(&self, repo: &OwnedRepo, error: &str, _index: usize, _total: usize) {
121 self.main_bar.inc(1);
122 if self.verbosity >= Verbosity::Normal {
123 self.main_bar.suspend(|| {
124 eprintln!(
125 "{} Failed to sync {}: {}",
126 style("✗").red(),
127 repo.full_name(),
128 error
129 );
130 });
131 }
132 }
133
134 fn on_skip(&self, repo: &OwnedRepo, reason: &str, _index: usize, _total: usize) {
135 self.main_bar.inc(1);
136 if self.verbosity >= Verbosity::Verbose {
137 self.main_bar.suspend(|| {
138 println!(
139 "{} Skipped {}: {}",
140 style("→").dim(),
141 repo.full_name(),
142 reason
143 );
144 });
145 }
146 }
147}
148
149#[cfg(test)]
150#[path = "sync_tests.rs"]
151mod tests;