Skip to content

Commit

Permalink
Add rdjson output format support for lint result, then we can use r…
Browse files Browse the repository at this point in the history
  • Loading branch information
huacnlee committed Aug 1, 2023
1 parent 9b90650 commit b1601e7
Show file tree
Hide file tree
Showing 7 changed files with 242 additions and 21 deletions.
13 changes: 13 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,7 @@ $ autocorrect update
- [Intellij Platform Plugin](#intellij-platform-plugin)
- [GitHub Action](#github-action)
- [GitLab CI](#gitlab-ci)
- [Work with ReviewDog](#work-with-reviewdog)
- [Use for programming](#use-for-programming)

### Use in CLI
Expand Down Expand Up @@ -371,6 +372,18 @@ autocorrect:
# allow_failure: true
```

### Work with ReviewDog

> Since: 2.8.0

AutoCorrect can work with [reviewdog](https://github.com/reviewdog/reviewdog), so you can use it in CI/CD. ReviewDog will post a comment to your PR with the AutoCorrect change suggestions. Then the PR committer can easy to accept the suggestions.

Use `--format rdjson` option to output the lint results as the [reviewdog](https://github.com/reviewdog/reviewdog) supported format.

```bash
autocorrect --lint --format rdjson | reviewdog -f=rdjson -reporter=github-pr-check
```

### Use for programming

AutoCorrect makes for support use in many programming languages.
Expand Down
5 changes: 1 addition & 4 deletions autocorrect-cli/src/cli.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,10 @@ use clap::{Parser, Subcommand, ValueEnum};
pub(crate) enum OutputFormatter {
Diff,
Json,
Rdjson,
}

impl OutputFormatter {
pub fn is_json(&self) -> bool {
*self == OutputFormatter::Json
}

pub fn is_diff(&self) -> bool {
*self == OutputFormatter::Diff
}
Expand Down
36 changes: 20 additions & 16 deletions autocorrect-cli/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
use autocorrect::LintResult;
// autocorrect: false
use clap::Parser;
use std::ffi::OsString;
Expand Down Expand Up @@ -97,7 +98,7 @@ where
// calc run time
let start_t = SystemTime::now();

let mut lint_results: Vec<String> = Vec::new();
let mut lint_results: Vec<LintResult> = Vec::new();
let lint_errors_count = std::sync::Arc::new(std::sync::Mutex::new(0));
let lint_warnings_count = std::sync::Arc::new(std::sync::Mutex::new(0));

Expand Down Expand Up @@ -184,7 +185,7 @@ where
Ok(raw) => {
bench!(format!("Done {filepath}"), {
if cli.lint {
let mut lint_results: Vec<String> = Vec::new();
let mut lint_results: Vec<LintResult> = Vec::new();

let mut _err_count = 0;
let mut _warn_count = 0;
Expand Down Expand Up @@ -226,18 +227,14 @@ where
log::debug!("Lint result found: {} issues.", lint_results.len());

if cli.lint {
if cli.formatter.is_json() {
log::info!(
r#"{{"count": {},"messages": [{}]}}"#,
lint_results.len(),
lint_results.join(",")
);
} else {
if cli.formatter.is_diff() {
let _err_count = *lint_errors_count.lock().unwrap();
let _warn_count = *lint_warnings_count.lock().unwrap();

log::info!("\n");
log::info!("{}", lint_results.join(""));
lint_results.iter().for_each(|lint_result| {
log::info!("{}", lint_result.to_diff(cli.no_diff_bg_color))
});

log::info!(
"{}, {}\n",
Expand All @@ -252,6 +249,15 @@ where
// Exit with code = 1
std::process::exit(1);
}
} else {
if cli.formatter == cli::OutputFormatter::Json {
log::info!("{}", autocorrect::json::to_lint_results_json(lint_results));
} else {
log::info!(
"{}",
autocorrect::rdjson::to_lint_results_rdjson(lint_results)
)
}
}
} else if cli.fix {
log::info!("\n");
Expand Down Expand Up @@ -327,7 +333,7 @@ fn lint_and_output(
filetype: &str,
raw: &str,
cli: &Cli,
results: &mut Vec<String>,
results: &mut Vec<LintResult>,
errors_count: &mut usize,
warings_count: &mut usize,
) {
Expand All @@ -352,14 +358,12 @@ fn lint_and_output(
}
}

if diff_mode {
if cli.formatter.is_diff() {
if result.has_error() {
log::debug!("{}\n{}", filepath, result.error);
return;
}

results.push(result.to_diff(cli.no_diff_bg_color));
} else {
results.push(result.to_json());
}

results.push(result.clone());
}
2 changes: 1 addition & 1 deletion autocorrect/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -135,7 +135,7 @@ pub mod ignorer;
pub use code::{format_for, get_file_extension, is_support_type, lint_for};
pub use config::Config;
pub use format::*;
pub use result::{FormatResult, LineResult, LintResult};
pub use result::{json, rdjson, FormatResult, LineResult, LintResult};
pub use rule::{halfwidth, spellcheck};

#[cfg(test)]
Expand Down
53 changes: 53 additions & 0 deletions autocorrect/src/result/json.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
//! AutoCorrect Lint JSON
use crate::LintResult;

#[doc(hidden)]
pub fn to_lint_results_json(lint_results: Vec<LintResult>) -> String {
format!(
r#"{{"count": {},"messages": [{}]}}"#,
lint_results.len(),
lint_results
.iter()
.map(|r| r.to_json())
.collect::<Vec<_>>()
.join(",")
)
}

#[cfg(test)]
pub(crate) fn crate_test_lint_results() -> Vec<LintResult> {
use crate::result::{LineResult, Results, Severity};

let mut lint_result = LintResult::new("hello你好.\n这是第2行");
lint_result.filepath = "test/foo/bar.rs".to_owned();
lint_result.push(LineResult {
line: 1,
col: 1,
new: "hello 你好。".to_owned(),
old: "hello你好.".to_owned(),
severity: Severity::Error,
});
lint_result.push(LineResult {
line: 2,
col: 1,
new: "这是第 2 行".to_owned(),
old: "这是第2行".to_owned(),
severity: Severity::Error,
});

vec![lint_result]
}

#[cfg(test)]
mod tests {
#[test]
fn test_to_lint_results_json() {
let json = super::to_lint_results_json(crate::result::json::crate_test_lint_results());

let expected = r#"{"count": 1,"messages": [{"filepath":"test/foo/bar.rs","lines":[{"l":1,"c":1,"new":"hello 你好。","old":"hello你好.","severity":1},{"l":2,"c":1,"new":"这是第 2 行","old":"这是第2行","severity":1}],"error":""}]}"#;
if expected != json {
println!("--------------- json:\n{}", json);
}
assert_json_eq!(expected, json);
}
}
2 changes: 2 additions & 0 deletions autocorrect/src/result.rs → autocorrect/src/result/mod.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
pub mod json;
pub mod rdjson;
use serde::{Deserialize, Serialize};
use serde_repr::*;

Expand Down
152 changes: 152 additions & 0 deletions autocorrect/src/result/rdjson.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,152 @@
//! Rdjson format for reviewdog
//! https://github.com/reviewdog/reviewdog/tree/master/proto/rdf
use super::LintResult;
use serde::{Deserialize, Serialize};

#[derive(Serialize, Deserialize, Clone)]
struct RdfJson {
source: RdfSource,
severity: String,
diagnostics: String,
}

#[derive(Serialize, Deserialize, Clone)]
struct RdfSource {
name: String,
url: String,
}

#[derive(Serialize, Deserialize, Clone)]
struct RdfDiagnostic {
message: String,
severity: String,
code: RdfCode,
location: RdfLocation,
suggestions: Vec<RdfSuggetion>,
}

#[derive(Serialize, Deserialize, Clone)]
struct RdfLocation {
path: String,
range: RdfRange,
}

#[derive(Serialize, Deserialize, Clone)]
struct RdfRange {
start: Option<RdfLineColumn>,
end: Option<RdfLineColumn>,
}

#[derive(Serialize, Deserialize, Clone)]
struct RdfLineColumn {
line: usize,
column: usize,
}

#[derive(Serialize, Deserialize, Clone)]
struct RdfSuggetion {
text: String,
range: RdfRange,
}

#[derive(Serialize, Deserialize, Clone)]
struct RdfCode {
value: Option<String>,
url: String,
}

fn to_severity_str(severity: super::Severity) -> String {
match severity {
super::Severity::Error => "ERROR".to_owned(),
super::Severity::Warning => "WARNING".to_owned(),
super::Severity::Pass => "PASS".to_owned(),
}
}

/// RDF JSONSchema
/// https://github.com/reviewdog/reviewdog/blob/master/proto/rdf/jsonschema/Diagnostic.jsonschema
#[doc(hidden)]
pub(crate) fn to_rdjson_diagnostic(lint_result: &LintResult, pretty: bool) -> String {
let range = RdfRange {
start: Some(RdfLineColumn {
line: lint_result.line,
column: lint_result.col,
}),
end: None,
};

let mut rdf_diagnostic: RdfDiagnostic = RdfDiagnostic {
message: "".to_owned(),
location: RdfLocation {
path: lint_result.filepath.clone(),
range: range.clone(),
},
severity: "UNKNOWN_SEVERITY".to_owned(),
code: RdfCode {
value: Some("AutoCorrect".to_owned()),
url: "https://github.com/huacnlee/autocorrect".to_owned(),
},
suggestions: vec![],
};

lint_result.lines.iter().for_each(|line_result| {
if rdf_diagnostic.severity == "UNKNOWN_SEVERITY" {
rdf_diagnostic.severity = to_severity_str(line_result.severity);
}

let suggestion = RdfSuggetion {
text: line_result.new.clone(),
range: RdfRange {
start: Some(RdfLineColumn {
line: line_result.line,
column: line_result.col,
}),
end: Some(RdfLineColumn {
line: line_result.line + line_result.old.split("\n").count() - 1,
column: line_result.col
+ line_result
.old
.split("\n")
.last()
.unwrap_or("")
.chars()
.count(),
}),
},
};

rdf_diagnostic.suggestions.push(suggestion);
});

if pretty {
serde_json::to_string_pretty(&rdf_diagnostic).unwrap()
} else {
serde_json::to_string(&rdf_diagnostic).unwrap()
}
}

#[doc(hidden)]
pub fn to_lint_results_rdjson(lint_results: Vec<LintResult>) -> String {
let diagnostics = lint_results
.iter()
.map(|r| to_rdjson_diagnostic(r, false))
.collect::<Vec<_>>()
.join(",");
format!(
r#"{{"source":{{"name":"AutoCorrect Lint","url": "https://github.com/huacnlee/autocorrect"}},"diagnostics": [{diagnostics}]}}"#,
)
}

#[cfg(test)]
mod tests {
#[test]
fn test_to_lint_results_rdjson() {
let rdjson = super::to_lint_results_rdjson(crate::result::json::crate_test_lint_results());

let expected = r#"{"source":{"name":"AutoCorrect Lint","url": "https://github.com/huacnlee/autocorrect"},"diagnostics": [{"message":"","severity":"ERROR","code":{"value":"AutoCorrect","url":"https://github.com/huacnlee/autocorrect"},"location":{"path":"test/foo/bar.rs","range":{"start":{"line":1,"column":1},"end":null}},"suggestions":[{"text":"hello 你好。","range":{"start":{"line":1,"column":1},"end":{"line":1,"column":9}}},{"text":"这是第 2 行","range":{"start":{"line":2,"column":1},"end":{"line":2,"column":6}}}]}]}"#;
if expected != rdjson {
println!("--------------- rdjson:\n{}", rdjson);
}
assert_json_eq!(expected, rdjson);
}
}

0 comments on commit b1601e7

Please sign in to comment.