From 25f777f0947dc55325ae9a0edab8882958509bb3 Mon Sep 17 00:00:00 2001 From: Bryan Helmkamp Date: Fri, 20 Dec 2024 15:57:49 -0500 Subject: [PATCH] --- qlty-cli/src/ui/invocations.rs | 220 +++++++++++++++++++++++---------- 1 file changed, 158 insertions(+), 62 deletions(-) diff --git a/qlty-cli/src/ui/invocations.rs b/qlty-cli/src/ui/invocations.rs index bd3cc2a4..82a767fd 100644 --- a/qlty-cli/src/ui/invocations.rs +++ b/qlty-cli/src/ui/invocations.rs @@ -1,8 +1,11 @@ +use std::io::Write as _; + use anyhow::Result; use console::style; use num_format::{Locale, ToFormattedString as _}; use qlty_analysis::utils::fs::path_to_string; use qlty_check::{executor::InvocationStatus, Report}; +use tabwriter::TabWriter; pub fn print_invocations( writer: &mut dyn std::io::Write, @@ -19,7 +22,6 @@ pub fn print_invocations( } if verbose >= 1 { - writeln!(writer)?; writeln!( writer, "{}{}{}", @@ -32,88 +34,182 @@ pub fn print_invocations( writeln!(writer)?; } - let mut printed_summary = false; let cwd = std::env::current_dir().expect("Unable to identify current directory"); + let mut tw = TabWriter::new(vec![]); + + // Print a JOBS summary in verbose mode + if verbose >= 1 { + for invocation in &report.invocations { + let absolute_outfile_path = invocation.outfile_path(); + let outfile_path = pathdiff::diff_paths(absolute_outfile_path, &cwd).unwrap(); + + match invocation.status() { + InvocationStatus::Success => { + tw.write_all( + format!( + "{}\t{}\t{} {}\t{:.2}s\t{}\n", + invocation.invocation.plugin_name, + style("Success").green(), + invocation.invocation.targets_count, + if invocation.invocation.targets_count == 1 { + "target" + } else { + "targets" + }, + invocation.invocation.duration_secs, + style(path_to_string(outfile_path)).dim().underlined(), + ) + .as_bytes(), + ) + .unwrap(); + } + InvocationStatus::LintError => { + tw.write_all( + format!( + "{}\t{}\t{} {}\t{:.2}s\t{}\n", + invocation.invocation.plugin_name, + style("Error").red(), + invocation.invocation.targets_count, + if invocation.invocation.targets_count == 1 { + "target" + } else { + "targets" + }, + invocation.invocation.duration_secs, + style(path_to_string(outfile_path)).dim().underlined(), + ) + .as_bytes(), + ) + .unwrap(); + } + InvocationStatus::ParseError => { + tw.write_all( + format!( + "{}\t{}\t{} {}\t{:.2}s\t{}\n", + invocation.invocation.plugin_name, + style("Parse error").red(), + invocation.invocation.targets_count, + if invocation.invocation.targets_count == 1 { + "target" + } else { + "targets" + }, + invocation.invocation.duration_secs, + style(path_to_string(outfile_path)).dim().underlined(), + ) + .as_bytes(), + ) + .unwrap(); + } + } + } + + tw.flush().unwrap(); + let written = String::from_utf8(tw.into_inner().unwrap()).unwrap(); + + if !written.is_empty() { + writeln!(writer, "{}", written)?; + } + } + + let mut tw = TabWriter::new(vec![]); + let mut errors_count = 0; for invocation in &report.invocations { let absolute_outfile_path = invocation.outfile_path(); let outfile_path = pathdiff::diff_paths(absolute_outfile_path, &cwd).unwrap(); match invocation.status() { - InvocationStatus::Success => { - if verbose >= 1 { - writeln!( - writer, - "{} {} checked {} files in {:.2}s {}", - style("Success").green(), - invocation.invocation.plugin_name, - invocation.plan.workspace_entries.len(), - invocation.invocation.duration_secs, - style(path_to_string(outfile_path)).dim(), - )?; + InvocationStatus::Success => {} + InvocationStatus::LintError => { + errors_count += 1; - printed_summary = true; - } - } - InvocationStatus::LintError => match invocation.invocation.exit_code { - Some(code) => { - writeln!( - writer, - "{} {}: Exited with code {:?} {}", - style("Lint error").red(), - style(&invocation.invocation.plugin_name).red().bold(), - code, - style(path_to_string(outfile_path)).dim(), - )?; - - if invocation.invocation.stderr.is_empty() { - if !invocation.invocation.stdout.is_empty() { + match invocation.invocation.exit_code { + Some(code) => { + tw.write_all( + format!( + "{}\t{}\t{}\t{}\n", + invocation.invocation.plugin_name, + style("Error").red(), + format!( + "Exited with code {:?} in {:.2}s", + code, invocation.invocation.duration_secs + ), + style(path_to_string(outfile_path)).dim().underlined(), + ) + .as_bytes(), + ) + .unwrap(); + + if invocation.invocation.stderr.is_empty() { + if !invocation.invocation.stdout.is_empty() { + let text: String = + invocation.invocation.stdout.chars().take(2048).collect(); + + for line in text.lines() { + tw.write(format!(" {}", style(line).red()).as_bytes())?; + } + } + } else { let text: String = - invocation.invocation.stdout.chars().take(2048).collect(); + invocation.invocation.stderr.chars().take(2048).collect(); for line in text.lines() { - writeln!(writer, " {}", style(line).red())?; + tw.write(format!(" {}", style(line).red()).as_bytes())?; } } - } else { - let text: String = - invocation.invocation.stderr.chars().take(2048).collect(); - - for line in text.lines() { - writeln!(writer, " {}", style(line).red())?; - } } - - printed_summary = true; - } - None => { - writeln!( - writer, - "{} {}: Exited with unknown status {}", - style("Lint error").red(), - style(&invocation.invocation.plugin_name).red().bold(), - style(path_to_string(invocation.outfile_path())).dim(), - )?; - printed_summary = true; + None => { + tw.write_all( + format!( + "{}\t{}\t{}\t{}\n", + invocation.invocation.plugin_name, + style("Error").red(), + format!( + "Exited with unknown status in {:.2}s", + invocation.invocation.duration_secs + ), + style(path_to_string(outfile_path)).dim().underlined(), + ) + .as_bytes(), + ) + .unwrap(); + } } - }, + } InvocationStatus::ParseError => { - writeln!( - writer, - "{} {}: {} {}", - style("Parse error").red(), - invocation.invocation.plugin_name, - invocation.invocation.parser_error.as_ref().unwrap(), - style(path_to_string(outfile_path)).dim(), - )?; - - printed_summary = true; + errors_count += 1; + + tw.write_all( + format!( + "{}\t{}\t{}\t{}\n", + invocation.invocation.plugin_name, + style("Parse error").red(), + invocation.invocation.parser_error.as_ref().unwrap(), + style(path_to_string(outfile_path)).dim().underlined(), + ) + .as_bytes(), + ) + .unwrap(); } } } - if printed_summary { + tw.flush().unwrap(); + let written = String::from_utf8(tw.into_inner().unwrap()).unwrap(); + + if !written.is_empty() { + writeln!( + writer, + "{}{}{}", + style(" ERRORS: ").bold().reverse(), + style(errors_count.to_formatted_string(&Locale::en)) + .bold() + .reverse(), + style(" ").bold().reverse() + )?; writeln!(writer)?; + writeln!(writer, "{}", written)?; } Ok(())