From 5de0ba055cef7f2dc5451b1eaf0857deb77ae009 Mon Sep 17 00:00:00 2001 From: Akshay Date: Sun, 24 Oct 2021 13:24:52 +0530 Subject: add support for json out --- Cargo.lock | 47 +++++++++++++++++++++++++++++++++++++++++++++++ bin/Cargo.toml | 12 ++++++++++++ bin/src/traits.rs | 45 +++++++++++++++++++++++++++++++++++++++++++-- lib/Cargo.toml | 10 ++++++++++ lib/src/lib.rs | 44 ++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 156 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 5fe64ab..a527d30 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -169,6 +169,12 @@ dependencies = [ "hashbrown 0.11.2", ] +[[package]] +name = "itoa" +version = "0.4.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b71991ff56294aa922b450139ee08b3bfc70982c6b2c7562771375cf73542dd4" + [[package]] name = "lazy_static" version = "1.4.0" @@ -184,6 +190,8 @@ dependencies = [ "macros", "rnix", "rowan", + "serde", + "serde_json", ] [[package]] @@ -329,6 +337,43 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" +[[package]] +name = "ryu" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "71d301d4193d031abdd79ff7e3dd721168a9572ef3fe51a1517aba235bd8f86e" + +[[package]] +name = "serde" +version = "1.0.130" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f12d06de37cf59146fbdecab66aa99f9fe4f78722e3607577a5375d66bd0c913" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.130" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d7bc1a1ab1961464eae040d96713baa5a724a8152c1222492465b54322ec508b" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_json" +version = "1.0.68" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0f690853975602e1bfe1ccbf50504d67174e3bcf340f23b5ea9992e0587a52d8" +dependencies = [ + "itoa", + "ryu", + "serde", +] + [[package]] name = "similar" version = "2.1.0" @@ -350,6 +395,8 @@ dependencies = [ "globset", "lib", "rnix", + "serde", + "serde_json", "similar", "thiserror", "vfs", diff --git a/bin/Cargo.toml b/bin/Cargo.toml index af5a288..528f804 100644 --- a/bin/Cargo.toml +++ b/bin/Cargo.toml @@ -14,3 +14,15 @@ thiserror = "1.0.30" similar = "2.1.0" vfs = { path = "../vfs" } lib = { path = "../lib" } + +[dependencies.serde_json] +version = "1.0.68" +optional = true + +[dependencies.serde] +version = "1.0.68" +features = [ "derive" ] +optional = true + +[features] +json = [ "lib/json-out", "serde_json", "serde" ] diff --git a/bin/src/traits.rs b/bin/src/traits.rs index 5cf7208..a0e40d4 100644 --- a/bin/src/traits.rs +++ b/bin/src/traits.rs @@ -32,9 +32,10 @@ where format: OutFormat, ) -> io::Result<()> { match format { + #[cfg(feature = "json")] + OutFormat::Json => json::write_json(self, lint_result, vfs), OutFormat::StdErr => write_stderr(self, lint_result, vfs), OutFormat::Errfmt => write_errfmt(self, lint_result, vfs), - _ => Ok(()), } } } @@ -84,7 +85,11 @@ fn write_stderr( Ok(()) } -fn write_errfmt(writer: &mut T, lint_result: &LintResult, vfs: &ReadOnlyVfs) -> io::Result<()> { +fn write_errfmt( + writer: &mut T, + lint_result: &LintResult, + vfs: &ReadOnlyVfs, +) -> io::Result<()> { let file_id = lint_result.file_id; let src = str::from_utf8(vfs.get(file_id)).unwrap(); let path = vfs.file_path(file_id); @@ -107,6 +112,42 @@ fn write_errfmt(writer: &mut T, lint_result: &LintResult, vfs: &ReadOn Ok(()) } +#[cfg(feature = "json")] +mod json { + use crate::lint::LintResult; + use std::io::{self, Write}; + use vfs::ReadOnlyVfs; + + use lib::Report; + use serde::Serialize; + use serde_json; + + #[derive(Serialize)] + struct JsonReport<'μ> { + file: &'μ std::path::Path, + report: Vec<&'μ Report>, + } + + pub fn write_json( + writer: &mut T, + lint_result: &LintResult, + vfs: &ReadOnlyVfs, + ) -> io::Result<()> { + let file_id = lint_result.file_id; + let path = vfs.file_path(file_id); + writeln!( + writer, + "{}", + serde_json::to_string_pretty(&JsonReport { + file: path, + report: lint_result.reports.iter().collect::>() + }) + .unwrap() + )?; + Ok(()) + } +} + fn line(at: TextRange, src: &str) -> usize { let at = at.start().into(); src[..at].chars().filter(|&c| c == '\n').count() + 1 diff --git a/lib/Cargo.toml b/lib/Cargo.toml index e9e9e69..e9bca06 100644 --- a/lib/Cargo.toml +++ b/lib/Cargo.toml @@ -11,3 +11,13 @@ if_chain = "1.0" macros = { path = "../macros" } lazy_static = "1.0" rowan = "0.12.5" +serde_json = { version = "1.0.68", optional = true } + +[dependencies.serde] +version = "1.0.130" +features = [ "derive" ] +optional = true + +[features] +default = [] +json-out = [ "serde", "serde_json" ] diff --git a/lib/src/lib.rs b/lib/src/lib.rs index d8a3859..bde039f 100644 --- a/lib/src/lib.rs +++ b/lib/src/lib.rs @@ -6,8 +6,12 @@ pub use lints::LINTS; use rnix::{SyntaxElement, SyntaxKind, TextRange}; use std::{convert::Into, default::Default}; +#[cfg(feature = "json-out")] +use serde::{Serialize, ser::{SerializeStruct, Serializer}}; + /// Report generated by a lint #[derive(Debug, Default)] +#[cfg_attr(feature = "json-out", derive(Serialize))] pub struct Report { /// General information about this lint and where it applies. pub note: &'static str, @@ -95,6 +99,27 @@ impl Diagnostic { } } +#[cfg(feature = "json-out")] +impl Serialize for Diagnostic { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + let mut s = serializer.serialize_struct("Diagnostic", 3)?; + let at = { + let start = usize::from(self.at.start()); + let end = usize::from(self.at.end()); + (start, end) + }; + s.serialize_field("at", &at)?; + s.serialize_field("message", &self.message)?; + if let Some(suggestion) = &self.suggestion { + s.serialize_field("suggestion", suggestion)?; + } + s.end() + } +} + /// Suggested fix for a diagnostic, the fix is provided as a syntax element. /// Look at `make.rs` to construct fixes. #[derive(Debug)] @@ -119,6 +144,25 @@ impl Suggestion { } } +#[cfg(feature = "json-out")] +impl Serialize for Suggestion { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + let mut s = serializer.serialize_struct("Suggestion", 2)?; + let at = { + let start = usize::from(self.at.start()); + let end = usize::from(self.at.end()); + (start, end) + }; + let fix = self.fix.to_string(); + s.serialize_field("at", &at)?; + s.serialize_field("fix", &fix)?; + s.end() + } +} + /// Lint logic is defined via this trait. Do not implement manually, /// look at the `lint` attribute macro instead for implementing rules pub trait Rule { -- cgit v1.2.3