Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Use tab writer to improve output of invocations and errors #1356

Merged
merged 4 commits into from
Dec 21, 2024
Merged
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
220 changes: 158 additions & 62 deletions qlty-cli/src/ui/invocations.rs
Original file line number Diff line number Diff line change
@@ -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,
Expand All @@ -19,7 +22,6 @@ pub fn print_invocations(
}

if verbose >= 1 {
writeln!(writer)?;
writeln!(
writer,
"{}{}{}",
Expand All @@ -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 => {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Found 19 lines of similar code in 3 locations (mass = 69) [qlty:similar-code]

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 => {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Found 19 lines of similar code in 3 locations (mass = 69) [qlty:similar-code]

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 => {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Found 19 lines of similar code in 3 locations (mass = 69) [qlty:similar-code]

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!(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

format! in format! args [clippy:format_in_format_args]

"{}\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() {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Deeply nested control flow (level = 5) [qlty:nested-control-flow]

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() {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Deeply nested control flow (level = 5) [qlty:nested-control-flow]

writeln!(writer, " {}", style(line).red())?;
tw.write(format!(" {}", style(line).red()).as_bytes())?;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

written amount is not handled [clippy:unused_io_amount]

}
}
} 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!(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

format! in format! args [clippy:format_in_format_args]

"{}\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(())
Expand Down
Loading