Skip to content

Commit d0b3888

Browse files
authored
feat: Add support for --sarif flag to qlty smells command (#2037)
This PR adds support for generating SARIF output using the --sarif flag when running the `qlty smells` command. --- Notes: I'm not an expert in Rust, and I encountered a few challenges while implementing this feature, so happy for any comments to improve it. The main issue was that the existing `SarifFormatter` only supported a single type: `qlty_check::Report`. Since Rust doesn't have inheritance, I resolved this by extracting a shared trait (`SarifTrait`) and implementing it for both `qlty_check::Report` and `qlty_analysis::Report`. The `SarifFormatter` was then updated to accept any type that implements this trait. I'm not completely confident that all the structs and traits are placed in the most appropriate modules, so I'm open to feedback or suggestions about how to better organize them according to project conventions or best practices.
1 parent fddcf6e commit d0b3888

File tree

3 files changed

+24
-16
lines changed

3 files changed

+24
-16
lines changed

qlty-cli/src/commands/check.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -363,7 +363,7 @@ impl Check {
363363
formatter.write_to(&mut std::io::stdout())?;
364364
Ok(false)
365365
} else if self.sarif {
366-
let formatter = SarifFormatter::boxed(report.clone());
366+
let formatter = SarifFormatter::boxed(report.messages.clone(), report.issues.clone());
367367
formatter.write_to(&mut std::io::stdout())?;
368368
Ok(false)
369369
} else {

qlty-cli/src/commands/smells.rs

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
use crate::format::SarifFormatter;
12
use crate::ui::Highlighter;
23
use crate::ui::Steps;
34
use crate::{Arguments, CommandError, CommandSuccess};
@@ -46,9 +47,13 @@ pub struct Smells {
4647
pub quiet: bool,
4748

4849
/// JSON output
49-
#[arg(long, hide = true)]
50+
#[arg(long, hide = true, conflicts_with = "sarif")]
5051
json: bool,
5152

53+
/// SARIF output
54+
#[arg(long, conflicts_with = "json")]
55+
sarif: bool,
56+
5257
/// Files to analyze
5358
pub paths: Vec<PathBuf>,
5459
}
@@ -101,7 +106,7 @@ impl Smells {
101106

102107
steps.start(SPARKLES, "Reporting... ");
103108
println!();
104-
self.write_stdout(&workspace, &report.issues)?;
109+
self.write_stdout(&workspace, &report)?;
105110

106111
CommandSuccess::ok()
107112
}
@@ -207,11 +212,15 @@ impl Smells {
207212
Ok(executor.report())
208213
}
209214

210-
fn write_stdout(&self, workspace: &Workspace, issues: &[Issue]) -> Result<()> {
215+
fn write_stdout(&self, workspace: &Workspace, report: &Report) -> Result<()> {
211216
if self.json {
212-
self.write_stdout_json(issues)
217+
self.write_stdout_json(&report.issues)
218+
} else if self.sarif {
219+
let formatter = SarifFormatter::boxed(report.messages.clone(), report.issues.clone());
220+
formatter.write_to(&mut std::io::stdout())?;
221+
Ok(())
213222
} else {
214-
self.write_stdout_text(workspace, issues)
223+
self.write_stdout_text(workspace, &report.issues)
215224
}
216225
}
217226

qlty-cli/src/format/sarif.rs

Lines changed: 9 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
use anyhow::Result;
2-
use qlty_check::Report;
32
use qlty_config::version::{BUILD_DATE, LONG_VERSION, QLTY_VERSION};
43
use qlty_formats::Formatter;
54
use qlty_types::analysis::v1::{Category, Issue, Language, Level, Location, Message, MessageLevel};
@@ -9,16 +8,17 @@ use std::io::Write;
98

109
#[derive(Debug)]
1110
pub struct SarifFormatter {
12-
report: Report,
11+
pub messages: Vec<Message>,
12+
pub issues: Vec<Issue>,
1313
}
1414

1515
impl SarifFormatter {
16-
pub fn new(report: Report) -> Self {
17-
Self { report }
16+
pub fn new(messages: Vec<Message>, issues: Vec<Issue>) -> Self {
17+
Self { messages, issues }
1818
}
1919

20-
pub fn boxed(report: Report) -> Box<dyn Formatter> {
21-
Box::new(Self::new(report))
20+
pub fn boxed(messages: Vec<Message>, issues: Vec<Issue>) -> Box<dyn Formatter> {
21+
Box::new(Self::new(messages, issues))
2222
}
2323

2424
fn convert_level(&self, level: Level) -> &'static str {
@@ -267,7 +267,7 @@ impl SarifFormatter {
267267
let mut rules = vec![];
268268
let mut rule_ids = std::collections::HashSet::new();
269269

270-
for issue in &self.report.issues {
270+
for issue in &self.issues {
271271
if !rule_ids.contains(&issue.rule_key) {
272272
rule_ids.insert(issue.rule_key.clone());
273273

@@ -284,14 +284,12 @@ impl SarifFormatter {
284284
}
285285

286286
let results = self
287-
.report
288287
.issues
289288
.iter()
290289
.map(|issue| self.serialize_issue(issue))
291290
.collect::<Vec<_>>();
292291

293292
let notifications = self
294-
.report
295293
.messages
296294
.iter()
297295
.map(|message| self.serialize_notification(message))
@@ -339,6 +337,7 @@ impl Formatter for SarifFormatter {
339337
mod test {
340338
use super::*;
341339
use qlty_analysis::{workspace_entries::TargetMode, IssueCount};
340+
use qlty_check::Report;
342341
use qlty_types::analysis::v1::{
343342
Category, ExecutionVerb, Mode, Range, Replacement, Suggestion, SuggestionSource,
344343
};
@@ -494,7 +493,7 @@ mod test {
494493
counts: IssueCount::default(),
495494
};
496495

497-
let formatter = SarifFormatter::boxed(report);
496+
let formatter = SarifFormatter::boxed(report.messages.clone(), report.issues.clone());
498497
let output = formatter.read().unwrap();
499498
let output_str = String::from_utf8_lossy(&output);
500499

0 commit comments

Comments
 (0)