diff options
-rw-r--r-- | Cargo.lock | 47 | ||||
-rw-r--r-- | bin/Cargo.toml | 12 | ||||
-rw-r--r-- | bin/src/traits.rs | 45 | ||||
-rw-r--r-- | lib/Cargo.toml | 10 | ||||
-rw-r--r-- | lib/src/lib.rs | 44 |
5 files changed, 156 insertions, 2 deletions
@@ -170,6 +170,12 @@ dependencies = [ | |||
170 | ] | 170 | ] |
171 | 171 | ||
172 | [[package]] | 172 | [[package]] |
173 | name = "itoa" | ||
174 | version = "0.4.8" | ||
175 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
176 | checksum = "b71991ff56294aa922b450139ee08b3bfc70982c6b2c7562771375cf73542dd4" | ||
177 | |||
178 | [[package]] | ||
173 | name = "lazy_static" | 179 | name = "lazy_static" |
174 | version = "1.4.0" | 180 | version = "1.4.0" |
175 | source = "registry+https://github.com/rust-lang/crates.io-index" | 181 | source = "registry+https://github.com/rust-lang/crates.io-index" |
@@ -184,6 +190,8 @@ dependencies = [ | |||
184 | "macros", | 190 | "macros", |
185 | "rnix", | 191 | "rnix", |
186 | "rowan", | 192 | "rowan", |
193 | "serde", | ||
194 | "serde_json", | ||
187 | ] | 195 | ] |
188 | 196 | ||
189 | [[package]] | 197 | [[package]] |
@@ -330,6 +338,43 @@ source = "registry+https://github.com/rust-lang/crates.io-index" | |||
330 | checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" | 338 | checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" |
331 | 339 | ||
332 | [[package]] | 340 | [[package]] |
341 | name = "ryu" | ||
342 | version = "1.0.5" | ||
343 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
344 | checksum = "71d301d4193d031abdd79ff7e3dd721168a9572ef3fe51a1517aba235bd8f86e" | ||
345 | |||
346 | [[package]] | ||
347 | name = "serde" | ||
348 | version = "1.0.130" | ||
349 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
350 | checksum = "f12d06de37cf59146fbdecab66aa99f9fe4f78722e3607577a5375d66bd0c913" | ||
351 | dependencies = [ | ||
352 | "serde_derive", | ||
353 | ] | ||
354 | |||
355 | [[package]] | ||
356 | name = "serde_derive" | ||
357 | version = "1.0.130" | ||
358 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
359 | checksum = "d7bc1a1ab1961464eae040d96713baa5a724a8152c1222492465b54322ec508b" | ||
360 | dependencies = [ | ||
361 | "proc-macro2", | ||
362 | "quote", | ||
363 | "syn", | ||
364 | ] | ||
365 | |||
366 | [[package]] | ||
367 | name = "serde_json" | ||
368 | version = "1.0.68" | ||
369 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
370 | checksum = "0f690853975602e1bfe1ccbf50504d67174e3bcf340f23b5ea9992e0587a52d8" | ||
371 | dependencies = [ | ||
372 | "itoa", | ||
373 | "ryu", | ||
374 | "serde", | ||
375 | ] | ||
376 | |||
377 | [[package]] | ||
333 | name = "similar" | 378 | name = "similar" |
334 | version = "2.1.0" | 379 | version = "2.1.0" |
335 | source = "registry+https://github.com/rust-lang/crates.io-index" | 380 | source = "registry+https://github.com/rust-lang/crates.io-index" |
@@ -350,6 +395,8 @@ dependencies = [ | |||
350 | "globset", | 395 | "globset", |
351 | "lib", | 396 | "lib", |
352 | "rnix", | 397 | "rnix", |
398 | "serde", | ||
399 | "serde_json", | ||
353 | "similar", | 400 | "similar", |
354 | "thiserror", | 401 | "thiserror", |
355 | "vfs", | 402 | "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" | |||
14 | similar = "2.1.0" | 14 | similar = "2.1.0" |
15 | vfs = { path = "../vfs" } | 15 | vfs = { path = "../vfs" } |
16 | lib = { path = "../lib" } | 16 | lib = { path = "../lib" } |
17 | |||
18 | [dependencies.serde_json] | ||
19 | version = "1.0.68" | ||
20 | optional = true | ||
21 | |||
22 | [dependencies.serde] | ||
23 | version = "1.0.68" | ||
24 | features = [ "derive" ] | ||
25 | optional = true | ||
26 | |||
27 | [features] | ||
28 | 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 | |||
32 | format: OutFormat, | 32 | format: OutFormat, |
33 | ) -> io::Result<()> { | 33 | ) -> io::Result<()> { |
34 | match format { | 34 | match format { |
35 | #[cfg(feature = "json")] | ||
36 | OutFormat::Json => json::write_json(self, lint_result, vfs), | ||
35 | OutFormat::StdErr => write_stderr(self, lint_result, vfs), | 37 | OutFormat::StdErr => write_stderr(self, lint_result, vfs), |
36 | OutFormat::Errfmt => write_errfmt(self, lint_result, vfs), | 38 | OutFormat::Errfmt => write_errfmt(self, lint_result, vfs), |
37 | _ => Ok(()), | ||
38 | } | 39 | } |
39 | } | 40 | } |
40 | } | 41 | } |
@@ -84,7 +85,11 @@ fn write_stderr<T: Write>( | |||
84 | Ok(()) | 85 | Ok(()) |
85 | } | 86 | } |
86 | 87 | ||
87 | fn write_errfmt<T: Write>(writer: &mut T, lint_result: &LintResult, vfs: &ReadOnlyVfs) -> io::Result<()> { | 88 | fn write_errfmt<T: Write>( |
89 | writer: &mut T, | ||
90 | lint_result: &LintResult, | ||
91 | vfs: &ReadOnlyVfs, | ||
92 | ) -> io::Result<()> { | ||
88 | let file_id = lint_result.file_id; | 93 | let file_id = lint_result.file_id; |
89 | let src = str::from_utf8(vfs.get(file_id)).unwrap(); | 94 | let src = str::from_utf8(vfs.get(file_id)).unwrap(); |
90 | let path = vfs.file_path(file_id); | 95 | let path = vfs.file_path(file_id); |
@@ -107,6 +112,42 @@ fn write_errfmt<T: Write>(writer: &mut T, lint_result: &LintResult, vfs: &ReadOn | |||
107 | Ok(()) | 112 | Ok(()) |
108 | } | 113 | } |
109 | 114 | ||
115 | #[cfg(feature = "json")] | ||
116 | mod json { | ||
117 | use crate::lint::LintResult; | ||
118 | use std::io::{self, Write}; | ||
119 | use vfs::ReadOnlyVfs; | ||
120 | |||
121 | use lib::Report; | ||
122 | use serde::Serialize; | ||
123 | use serde_json; | ||
124 | |||
125 | #[derive(Serialize)] | ||
126 | struct JsonReport<'μ> { | ||
127 | file: &'μ std::path::Path, | ||
128 | report: Vec<&'μ Report>, | ||
129 | } | ||
130 | |||
131 | pub fn write_json<T: Write>( | ||
132 | writer: &mut T, | ||
133 | lint_result: &LintResult, | ||
134 | vfs: &ReadOnlyVfs, | ||
135 | ) -> io::Result<()> { | ||
136 | let file_id = lint_result.file_id; | ||
137 | let path = vfs.file_path(file_id); | ||
138 | writeln!( | ||
139 | writer, | ||
140 | "{}", | ||
141 | serde_json::to_string_pretty(&JsonReport { | ||
142 | file: path, | ||
143 | report: lint_result.reports.iter().collect::<Vec<_>>() | ||
144 | }) | ||
145 | .unwrap() | ||
146 | )?; | ||
147 | Ok(()) | ||
148 | } | ||
149 | } | ||
150 | |||
110 | fn line(at: TextRange, src: &str) -> usize { | 151 | fn line(at: TextRange, src: &str) -> usize { |
111 | let at = at.start().into(); | 152 | let at = at.start().into(); |
112 | src[..at].chars().filter(|&c| c == '\n').count() + 1 | 153 | 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" | |||
11 | macros = { path = "../macros" } | 11 | macros = { path = "../macros" } |
12 | lazy_static = "1.0" | 12 | lazy_static = "1.0" |
13 | rowan = "0.12.5" | 13 | rowan = "0.12.5" |
14 | serde_json = { version = "1.0.68", optional = true } | ||
15 | |||
16 | [dependencies.serde] | ||
17 | version = "1.0.130" | ||
18 | features = [ "derive" ] | ||
19 | optional = true | ||
20 | |||
21 | [features] | ||
22 | default = [] | ||
23 | 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; | |||
6 | use rnix::{SyntaxElement, SyntaxKind, TextRange}; | 6 | use rnix::{SyntaxElement, SyntaxKind, TextRange}; |
7 | use std::{convert::Into, default::Default}; | 7 | use std::{convert::Into, default::Default}; |
8 | 8 | ||
9 | #[cfg(feature = "json-out")] | ||
10 | use serde::{Serialize, ser::{SerializeStruct, Serializer}}; | ||
11 | |||
9 | /// Report generated by a lint | 12 | /// Report generated by a lint |
10 | #[derive(Debug, Default)] | 13 | #[derive(Debug, Default)] |
14 | #[cfg_attr(feature = "json-out", derive(Serialize))] | ||
11 | pub struct Report { | 15 | pub struct Report { |
12 | /// General information about this lint and where it applies. | 16 | /// General information about this lint and where it applies. |
13 | pub note: &'static str, | 17 | pub note: &'static str, |
@@ -95,6 +99,27 @@ impl Diagnostic { | |||
95 | } | 99 | } |
96 | } | 100 | } |
97 | 101 | ||
102 | #[cfg(feature = "json-out")] | ||
103 | impl Serialize for Diagnostic { | ||
104 | fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> | ||
105 | where | ||
106 | S: Serializer, | ||
107 | { | ||
108 | let mut s = serializer.serialize_struct("Diagnostic", 3)?; | ||
109 | let at = { | ||
110 | let start = usize::from(self.at.start()); | ||
111 | let end = usize::from(self.at.end()); | ||
112 | (start, end) | ||
113 | }; | ||
114 | s.serialize_field("at", &at)?; | ||
115 | s.serialize_field("message", &self.message)?; | ||
116 | if let Some(suggestion) = &self.suggestion { | ||
117 | s.serialize_field("suggestion", suggestion)?; | ||
118 | } | ||
119 | s.end() | ||
120 | } | ||
121 | } | ||
122 | |||
98 | /// Suggested fix for a diagnostic, the fix is provided as a syntax element. | 123 | /// Suggested fix for a diagnostic, the fix is provided as a syntax element. |
99 | /// Look at `make.rs` to construct fixes. | 124 | /// Look at `make.rs` to construct fixes. |
100 | #[derive(Debug)] | 125 | #[derive(Debug)] |
@@ -119,6 +144,25 @@ impl Suggestion { | |||
119 | } | 144 | } |
120 | } | 145 | } |
121 | 146 | ||
147 | #[cfg(feature = "json-out")] | ||
148 | impl Serialize for Suggestion { | ||
149 | fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> | ||
150 | where | ||
151 | S: Serializer, | ||
152 | { | ||
153 | let mut s = serializer.serialize_struct("Suggestion", 2)?; | ||
154 | let at = { | ||
155 | let start = usize::from(self.at.start()); | ||
156 | let end = usize::from(self.at.end()); | ||
157 | (start, end) | ||
158 | }; | ||
159 | let fix = self.fix.to_string(); | ||
160 | s.serialize_field("at", &at)?; | ||
161 | s.serialize_field("fix", &fix)?; | ||
162 | s.end() | ||
163 | } | ||
164 | } | ||
165 | |||
122 | /// Lint logic is defined via this trait. Do not implement manually, | 166 | /// Lint logic is defined via this trait. Do not implement manually, |
123 | /// look at the `lint` attribute macro instead for implementing rules | 167 | /// look at the `lint` attribute macro instead for implementing rules |
124 | pub trait Rule { | 168 | pub trait Rule { |