1use super::ArchiveStep;
5use crate::{helpers::plural, redact::Redactor};
6use camino::Utf8Path;
7use owo_colors::{OwoColorize, Style};
8use std::{
9 io::{self, Write},
10 time::Duration,
11};
12
13#[derive(Debug)]
14pub struct ArchiveReporter {
16 styles: Styles,
17 verbose: bool,
18 redactor: Redactor,
19
20 linked_path_hint_emitted: bool,
21 }
23
24impl ArchiveReporter {
25 pub fn new(verbose: bool, redactor: Redactor) -> Self {
27 Self {
28 styles: Styles::default(),
29 verbose,
30 redactor,
31
32 linked_path_hint_emitted: false,
33 }
34 }
35
36 pub fn colorize(&mut self) {
38 self.styles.colorize();
39 }
40
41 pub fn report_event(
43 &mut self,
44 event: ArchiveEvent<'_>,
45 mut writer: impl Write,
46 ) -> io::Result<()> {
47 match event {
48 ArchiveEvent::ArchiveStarted {
49 counts,
50 output_file,
51 } => {
52 write!(writer, "{:>12} ", "Archiving".style(self.styles.success))?;
53
54 self.report_counts(counts, &mut writer)?;
55
56 writeln!(
57 writer,
58 " to {}",
59 self.redactor
60 .redact_path(output_file)
61 .style(self.styles.bold)
62 )?;
63 }
64 ArchiveEvent::StdlibPathError { error } => {
65 write!(writer, "{:>12} ", "Warning".style(self.styles.bold))?;
66 writeln!(
67 writer,
68 "could not find standard library for host (proc macro tests may not work): {error}"
69 )?;
70 }
71 ArchiveEvent::ExtraPathMissing { path, warn } => {
72 if warn {
73 write!(writer, "{:>12} ", "Warning".style(self.styles.warning))?;
74 } else if self.verbose {
75 write!(writer, "{:>12} ", "Skipped".style(self.styles.skipped))?;
76 } else {
77 return Ok(()); }
79
80 writeln!(
81 writer,
82 "ignoring extra path `{}` because it does not exist",
83 self.redactor.redact_path(path).style(self.styles.bold),
84 )?;
85 }
86 ArchiveEvent::DirectoryAtDepthZero { path } => {
87 write!(writer, "{:>12} ", "Warning".style(self.styles.warning))?;
88 writeln!(
89 writer,
90 "ignoring extra path `{}` specified with depth 0 since it is a directory",
91 self.redactor.redact_path(path).style(self.styles.bold),
92 )?;
93 }
94 ArchiveEvent::RecursionDepthExceeded {
95 step,
96 path,
97 limit,
98 warn,
99 } => {
100 if warn {
101 write!(writer, "{:>12} ", "Warning".style(self.styles.warning))?;
102 } else if self.verbose {
103 write!(writer, "{:>12} ", "Skipped".style(self.styles.skipped))?;
104 } else {
105 return Ok(()); }
107
108 writeln!(
109 writer,
110 "while archiving {step}, recursion depth exceeded at {} (limit: {limit})",
111 self.redactor.redact_path(path).style(self.styles.bold),
112 )?;
113 }
114 ArchiveEvent::UnknownFileType { step, path } => {
115 write!(writer, "{:>12} ", "Warning".style(self.styles.warning))?;
116 writeln!(
117 writer,
118 "while archiving {step}, ignoring `{}` because it is not a file, \
119 directory, or symbolic link",
120 self.redactor.redact_path(path).style(self.styles.bold),
121 )?;
122 }
123 ArchiveEvent::LinkedPathNotFound { path, requested_by } => {
124 write!(writer, "{:>12} ", "Warning".style(self.styles.warning))?;
125 writeln!(
126 writer,
127 "linked path `{}` not found, requested by: {}",
128 self.redactor.redact_path(path).style(self.styles.bold),
129 requested_by.join(", ").style(self.styles.bold),
130 )?;
131 if !self.linked_path_hint_emitted {
132 write!(writer, "{:>12} ", "")?;
133 writeln!(
134 writer,
135 "(this is a bug in {} that should be fixed)",
136 plural::this_crate_str(requested_by.len())
137 )?;
138 self.linked_path_hint_emitted = true;
139 }
140 }
141 ArchiveEvent::Archived {
142 file_count,
143 output_file,
144 elapsed,
145 } => {
146 write!(writer, "{:>12} ", "Archived".style(self.styles.success))?;
147 writeln!(
148 writer,
149 "{} files to {} in {}",
150 self.redactor
151 .redact_file_count(file_count)
152 .style(self.styles.bold),
153 self.redactor
154 .redact_path(output_file)
155 .style(self.styles.bold),
156 self.redactor.redact_duration(elapsed),
157 )?;
158 }
159 ArchiveEvent::ExtractStarted {
160 test_binary_count,
161 non_test_binary_count,
162 build_script_out_dir_count,
163 linked_path_count,
164 dest_dir: destination_dir,
165 } => {
166 write!(writer, "{:>12} ", "Extracting".style(self.styles.success))?;
167
168 self.report_counts(
169 ArchiveCounts {
170 test_binary_count,
171 non_test_binary_count,
172 build_script_out_dir_count,
173 linked_path_count,
174 extra_path_count: 0,
177 stdlib_count: 0,
178 },
179 &mut writer,
180 )?;
181
182 writeln!(writer, " to {}", destination_dir.style(self.styles.bold))?;
183 }
184 ArchiveEvent::Extracted {
185 file_count,
186 dest_dir: destination_dir,
187 elapsed,
188 } => {
189 write!(writer, "{:>12} ", "Extracted".style(self.styles.success))?;
190 writeln!(
191 writer,
192 "{} {} to {} in {}",
193 self.redactor
194 .redact_file_count(file_count)
195 .style(self.styles.bold),
196 plural::files_str(file_count),
197 self.redactor
198 .redact_path(destination_dir)
199 .style(self.styles.bold),
200 self.redactor.redact_duration(elapsed),
201 )?;
202 }
203 }
204
205 Ok(())
206 }
207
208 fn report_counts(&mut self, counts: ArchiveCounts, mut writer: impl Write) -> io::Result<()> {
209 let ArchiveCounts {
210 test_binary_count,
211 non_test_binary_count,
212 build_script_out_dir_count,
213 linked_path_count,
214 extra_path_count,
215 stdlib_count,
216 } = counts;
217
218 let total_binary_count = test_binary_count + non_test_binary_count;
219 let non_test_text = if non_test_binary_count > 0 {
220 format!(
221 " (including {} non-test {})",
222 non_test_binary_count.style(self.styles.bold),
223 plural::binaries_str(non_test_binary_count),
224 )
225 } else {
226 "".to_owned()
227 };
228 let mut more = Vec::new();
229 if build_script_out_dir_count > 0 {
230 more.push(format!(
231 "{} build script output {}",
232 build_script_out_dir_count.style(self.styles.bold),
233 plural::directories_str(build_script_out_dir_count),
234 ));
235 }
236 if linked_path_count > 0 {
237 more.push(format!(
238 "{} linked {}",
239 linked_path_count.style(self.styles.bold),
240 plural::paths_str(linked_path_count),
241 ));
242 }
243 if extra_path_count > 0 {
244 more.push(format!(
245 "{} extra {}",
246 extra_path_count.style(self.styles.bold),
247 plural::paths_str(extra_path_count),
248 ));
249 }
250 if stdlib_count > 0 {
251 more.push(format!(
252 "{} standard {}",
253 stdlib_count.style(self.styles.bold),
254 plural::libraries_str(stdlib_count),
255 ));
256 }
257
258 write!(
259 writer,
260 "{} {}{non_test_text}",
261 total_binary_count.style(self.styles.bold),
262 plural::binaries_str(total_binary_count),
263 )?;
264
265 match more.len() {
266 0 => Ok(()),
267 1 => {
268 write!(writer, " and {}", more[0])
269 }
270 _ => {
271 write!(
272 writer,
273 ", {}, and {}",
274 more[..more.len() - 1].join(", "),
275 more.last().unwrap(),
276 )
277 }
278 }
279 }
280}
281
282#[derive(Debug, Default)]
283struct Styles {
284 bold: Style,
285 success: Style,
286 warning: Style,
287 skipped: Style,
288}
289
290impl Styles {
291 fn colorize(&mut self) {
292 self.bold = Style::new().bold();
293 self.success = Style::new().green().bold();
294 self.warning = Style::new().yellow().bold();
295 self.skipped = Style::new().bold();
296 }
297}
298
299#[derive(Clone, Debug)]
303#[non_exhaustive]
304pub enum ArchiveEvent<'a> {
305 ArchiveStarted {
307 counts: ArchiveCounts,
309
310 output_file: &'a Utf8Path,
312 },
313
314 StdlibPathError {
316 error: &'a str,
318 },
319
320 ExtraPathMissing {
322 path: &'a Utf8Path,
324
325 warn: bool,
327 },
328
329 DirectoryAtDepthZero {
331 path: &'a Utf8Path,
333 },
334
335 RecursionDepthExceeded {
337 step: ArchiveStep,
339
340 path: &'a Utf8Path,
342
343 limit: usize,
345
346 warn: bool,
348 },
349
350 UnknownFileType {
352 step: ArchiveStep,
354
355 path: &'a Utf8Path,
357 },
358
359 LinkedPathNotFound {
361 path: &'a Utf8Path,
363
364 requested_by: &'a [String],
366 },
367
368 Archived {
370 file_count: usize,
372
373 output_file: &'a Utf8Path,
375
376 elapsed: Duration,
378 },
379
380 ExtractStarted {
382 test_binary_count: usize,
384
385 non_test_binary_count: usize,
387
388 build_script_out_dir_count: usize,
390
391 linked_path_count: usize,
393
394 dest_dir: &'a Utf8Path,
396 },
397
398 Extracted {
400 file_count: usize,
402
403 dest_dir: &'a Utf8Path,
405
406 elapsed: Duration,
408 },
409}
410
411#[derive(Clone, Copy, Debug)]
413pub struct ArchiveCounts {
414 pub test_binary_count: usize,
416
417 pub non_test_binary_count: usize,
419
420 pub build_script_out_dir_count: usize,
422
423 pub linked_path_count: usize,
425
426 pub extra_path_count: usize,
428
429 pub stdlib_count: usize,
431}