diff options
author | Akshay <[email protected]> | 2021-11-20 13:26:26 +0000 |
---|---|---|
committer | Akshay <[email protected]> | 2021-11-28 07:55:23 +0000 |
commit | 2b6012a79cb092e5d88c050cb494057efef28fc2 (patch) | |
tree | cd31973c32431a5cbf8c12ce574f799d065852be /bin | |
parent | 4e063b2abc402ac4d6902647e821978269025c7d (diff) |
introduce --config flag
Diffstat (limited to 'bin')
-rw-r--r-- | bin/Cargo.toml | 10 | ||||
-rw-r--r-- | bin/src/config.rs | 168 | ||||
-rw-r--r-- | bin/src/err.rs | 2 | ||||
-rw-r--r-- | bin/src/explain.rs | 7 | ||||
-rw-r--r-- | bin/src/fix.rs | 12 | ||||
-rw-r--r-- | bin/src/fix/all.rs | 18 | ||||
-rw-r--r-- | bin/src/fix/single.rs | 8 | ||||
-rw-r--r-- | bin/src/lib.rs | 9 | ||||
-rw-r--r-- | bin/src/lint.rs | 17 | ||||
-rw-r--r-- | bin/src/utils.rs | 24 |
10 files changed, 200 insertions, 75 deletions
diff --git a/bin/Cargo.toml b/bin/Cargo.toml index 7c48083..c898792 100644 --- a/bin/Cargo.toml +++ b/bin/Cargo.toml | |||
@@ -23,14 +23,14 @@ thiserror = "1.0.30" | |||
23 | similar = "2.1.0" | 23 | similar = "2.1.0" |
24 | vfs = { path = "../vfs" } | 24 | vfs = { path = "../vfs" } |
25 | lib = { path = "../lib" } | 25 | lib = { path = "../lib" } |
26 | 26 | toml = "0.5.8" | |
27 | [dependencies.serde_json] | ||
28 | version = "1.0.68" | ||
29 | optional = true | ||
30 | 27 | ||
31 | [dependencies.serde] | 28 | [dependencies.serde] |
32 | version = "1.0.68" | 29 | version = "1.0.68" |
33 | features = [ "derive" ] | 30 | features = [ "derive" ] |
31 | |||
32 | [dependencies.serde_json] | ||
33 | version = "1.0.68" | ||
34 | optional = true | 34 | optional = true |
35 | 35 | ||
36 | [dev-dependencies] | 36 | [dev-dependencies] |
@@ -38,4 +38,4 @@ insta = "1.8.0" | |||
38 | strip-ansi-escapes = "0.1.1" | 38 | strip-ansi-escapes = "0.1.1" |
39 | 39 | ||
40 | [features] | 40 | [features] |
41 | json = [ "lib/json-out", "serde_json", "serde" ] | 41 | json = [ "lib/json-out", "serde_json" ] |
diff --git a/bin/src/config.rs b/bin/src/config.rs index d3944ac..b6310e6 100644 --- a/bin/src/config.rs +++ b/bin/src/config.rs | |||
@@ -1,8 +1,15 @@ | |||
1 | use std::{default::Default, fmt, fs, path::PathBuf, str::FromStr}; | 1 | use std::{ |
2 | default::Default, | ||
3 | fmt, fs, | ||
4 | path::{Path, PathBuf}, | ||
5 | str::FromStr, | ||
6 | }; | ||
2 | 7 | ||
3 | use crate::{dirs, err::ConfigErr}; | 8 | use crate::{dirs, err::ConfigErr, utils, LintMap}; |
4 | 9 | ||
5 | use clap::Parser; | 10 | use clap::Parser; |
11 | use lib::LINTS; | ||
12 | use serde::{Deserialize, Serialize}; | ||
6 | use vfs::ReadOnlyVfs; | 13 | use vfs::ReadOnlyVfs; |
7 | 14 | ||
8 | #[derive(Parser, Debug)] | 15 | #[derive(Parser, Debug)] |
@@ -43,6 +50,10 @@ pub struct Check { | |||
43 | #[clap(short = 'o', long, default_value_t, parse(try_from_str))] | 50 | #[clap(short = 'o', long, default_value_t, parse(try_from_str))] |
44 | pub format: OutFormat, | 51 | pub format: OutFormat, |
45 | 52 | ||
53 | /// Path to statix.toml | ||
54 | #[clap(short = 'c', long = "config", default_value = ".")] | ||
55 | pub conf_path: PathBuf, | ||
56 | |||
46 | /// Enable "streaming" mode, accept file on stdin, output diagnostics on stdout | 57 | /// Enable "streaming" mode, accept file on stdin, output diagnostics on stdout |
47 | #[clap(short, long = "stdin")] | 58 | #[clap(short, long = "stdin")] |
48 | pub streaming: bool, | 59 | pub streaming: bool, |
@@ -65,6 +76,10 @@ impl Check { | |||
65 | vfs(files.collect::<Vec<_>>()) | 76 | vfs(files.collect::<Vec<_>>()) |
66 | } | 77 | } |
67 | } | 78 | } |
79 | |||
80 | pub fn lints(&self) -> Result<LintMap, ConfigErr> { | ||
81 | lints(&self.conf_path) | ||
82 | } | ||
68 | } | 83 | } |
69 | 84 | ||
70 | #[derive(Parser, Debug)] | 85 | #[derive(Parser, Debug)] |
@@ -85,6 +100,10 @@ pub struct Fix { | |||
85 | #[clap(short, long = "dry-run")] | 100 | #[clap(short, long = "dry-run")] |
86 | pub diff_only: bool, | 101 | pub diff_only: bool, |
87 | 102 | ||
103 | /// Path to statix.toml | ||
104 | #[clap(short = 'c', long = "config", default_value = ".")] | ||
105 | pub conf_path: PathBuf, | ||
106 | |||
88 | /// Enable "streaming" mode, accept file on stdin, output diagnostics on stdout | 107 | /// Enable "streaming" mode, accept file on stdin, output diagnostics on stdout |
89 | #[clap(short, long = "stdin")] | 108 | #[clap(short, long = "stdin")] |
90 | pub streaming: bool, | 109 | pub streaming: bool, |
@@ -125,6 +144,10 @@ impl Fix { | |||
125 | FixOut::Write | 144 | FixOut::Write |
126 | } | 145 | } |
127 | } | 146 | } |
147 | |||
148 | pub fn lints(&self) -> Result<LintMap, ConfigErr> { | ||
149 | lints(&self.conf_path) | ||
150 | } | ||
128 | } | 151 | } |
129 | 152 | ||
130 | #[derive(Parser, Debug)] | 153 | #[derive(Parser, Debug)] |
@@ -181,50 +204,6 @@ pub struct Explain { | |||
181 | pub target: u32, | 204 | pub target: u32, |
182 | } | 205 | } |
183 | 206 | ||
184 | fn parse_line_col(src: &str) -> Result<(usize, usize), ConfigErr> { | ||
185 | let parts = src.split(','); | ||
186 | match parts.collect::<Vec<_>>().as_slice() { | ||
187 | [line, col] => { | ||
188 | let l = line | ||
189 | .parse::<usize>() | ||
190 | .map_err(|_| ConfigErr::InvalidPosition(src.to_owned()))?; | ||
191 | let c = col | ||
192 | .parse::<usize>() | ||
193 | .map_err(|_| ConfigErr::InvalidPosition(src.to_owned()))?; | ||
194 | Ok((l, c)) | ||
195 | } | ||
196 | _ => Err(ConfigErr::InvalidPosition(src.to_owned())), | ||
197 | } | ||
198 | } | ||
199 | |||
200 | fn parse_warning_code(src: &str) -> Result<u32, ConfigErr> { | ||
201 | let mut char_stream = src.chars(); | ||
202 | let severity = char_stream | ||
203 | .next() | ||
204 | .ok_or_else(|| ConfigErr::InvalidWarningCode(src.to_owned()))? | ||
205 | .to_ascii_lowercase(); | ||
206 | match severity { | ||
207 | 'w' => char_stream | ||
208 | .collect::<String>() | ||
209 | .parse::<u32>() | ||
210 | .map_err(|_| ConfigErr::InvalidWarningCode(src.to_owned())), | ||
211 | _ => Ok(0), | ||
212 | } | ||
213 | } | ||
214 | |||
215 | fn vfs(files: Vec<PathBuf>) -> Result<ReadOnlyVfs, ConfigErr> { | ||
216 | let mut vfs = ReadOnlyVfs::default(); | ||
217 | for file in files.iter() { | ||
218 | if let Ok(data) = fs::read_to_string(&file) { | ||
219 | let _id = vfs.alloc_file_id(&file); | ||
220 | vfs.set_file_contents(&file, data.as_bytes()); | ||
221 | } else { | ||
222 | println!("{} contains non-utf8 content", file.display()); | ||
223 | }; | ||
224 | } | ||
225 | Ok(vfs) | ||
226 | } | ||
227 | |||
228 | #[derive(Debug, Copy, Clone)] | 207 | #[derive(Debug, Copy, Clone)] |
229 | pub enum OutFormat { | 208 | pub enum OutFormat { |
230 | #[cfg(feature = "json")] | 209 | #[cfg(feature = "json")] |
@@ -269,3 +248,100 @@ impl FromStr for OutFormat { | |||
269 | } | 248 | } |
270 | } | 249 | } |
271 | } | 250 | } |
251 | |||
252 | #[derive(Serialize, Deserialize, Debug)] | ||
253 | pub struct ConfFile { | ||
254 | disabled: Vec<String>, | ||
255 | } | ||
256 | |||
257 | impl Default for ConfFile { | ||
258 | fn default() -> Self { | ||
259 | let disabled = vec![]; | ||
260 | Self { disabled } | ||
261 | } | ||
262 | } | ||
263 | |||
264 | impl ConfFile { | ||
265 | pub fn from_path<P: AsRef<Path>>(path: P) -> Result<Self, ConfigErr> { | ||
266 | let path = path.as_ref(); | ||
267 | let config_file = fs::read_to_string(path).map_err(ConfigErr::InvalidPath)?; | ||
268 | toml::de::from_str(&config_file).map_err(|err| { | ||
269 | let pos = err.line_col(); | ||
270 | let msg = if let Some((line, col)) = pos { | ||
271 | format!("line {}, col {}", line, col) | ||
272 | } else { | ||
273 | "unknown".to_string() | ||
274 | }; | ||
275 | ConfigErr::ConfFileParse(msg) | ||
276 | }) | ||
277 | } | ||
278 | pub fn discover<P: AsRef<Path>>(path: P) -> Result<Self, ConfigErr> { | ||
279 | let cannonical_path = fs::canonicalize(path.as_ref()).map_err(ConfigErr::InvalidPath)?; | ||
280 | for p in cannonical_path.ancestors() { | ||
281 | let statix_toml_path = p.with_file_name("statix.toml"); | ||
282 | if statix_toml_path.exists() { | ||
283 | return Self::from_path(statix_toml_path); | ||
284 | }; | ||
285 | } | ||
286 | Ok(Self::default()) | ||
287 | } | ||
288 | pub fn dump(&self) -> String { | ||
289 | toml::ser::to_string_pretty(&self).unwrap() | ||
290 | } | ||
291 | } | ||
292 | |||
293 | fn parse_line_col(src: &str) -> Result<(usize, usize), ConfigErr> { | ||
294 | let parts = src.split(','); | ||
295 | match parts.collect::<Vec<_>>().as_slice() { | ||
296 | [line, col] => { | ||
297 | let do_parse = |val: &str| { | ||
298 | val.parse::<usize>() | ||
299 | .map_err(|_| ConfigErr::InvalidPosition(src.to_owned())) | ||
300 | }; | ||
301 | let l = do_parse(line)?; | ||
302 | let c = do_parse(col)?; | ||
303 | Ok((l, c)) | ||
304 | } | ||
305 | _ => Err(ConfigErr::InvalidPosition(src.to_owned())), | ||
306 | } | ||
307 | } | ||
308 | |||
309 | fn parse_warning_code(src: &str) -> Result<u32, ConfigErr> { | ||
310 | let mut char_stream = src.chars(); | ||
311 | let severity = char_stream | ||
312 | .next() | ||
313 | .ok_or_else(|| ConfigErr::InvalidWarningCode(src.to_owned()))? | ||
314 | .to_ascii_lowercase(); | ||
315 | match severity { | ||
316 | 'w' => char_stream | ||
317 | .collect::<String>() | ||
318 | .parse::<u32>() | ||
319 | .map_err(|_| ConfigErr::InvalidWarningCode(src.to_owned())), | ||
320 | _ => Ok(0), | ||
321 | } | ||
322 | } | ||
323 | |||
324 | fn vfs(files: Vec<PathBuf>) -> Result<ReadOnlyVfs, ConfigErr> { | ||
325 | let mut vfs = ReadOnlyVfs::default(); | ||
326 | for file in files.iter() { | ||
327 | if let Ok(data) = fs::read_to_string(&file) { | ||
328 | let _id = vfs.alloc_file_id(&file); | ||
329 | vfs.set_file_contents(&file, data.as_bytes()); | ||
330 | } else { | ||
331 | println!("{} contains non-utf8 content", file.display()); | ||
332 | }; | ||
333 | } | ||
334 | Ok(vfs) | ||
335 | } | ||
336 | |||
337 | fn lints(conf_path: &PathBuf) -> Result<LintMap, ConfigErr> { | ||
338 | let config_file = ConfFile::discover(conf_path)?; | ||
339 | Ok(utils::lint_map_of( | ||
340 | (&*LINTS) | ||
341 | .into_iter() | ||
342 | .filter(|l| !config_file.disabled.iter().any(|check| check == l.name())) | ||
343 | .cloned() | ||
344 | .collect::<Vec<_>>() | ||
345 | .as_slice(), | ||
346 | )) | ||
347 | } | ||
diff --git a/bin/src/err.rs b/bin/src/err.rs index b776d9f..f44d27b 100644 --- a/bin/src/err.rs +++ b/bin/src/err.rs | |||
@@ -14,6 +14,8 @@ pub enum ConfigErr { | |||
14 | InvalidPosition(String), | 14 | InvalidPosition(String), |
15 | #[error("unable to parse `{0}` as warning code")] | 15 | #[error("unable to parse `{0}` as warning code")] |
16 | InvalidWarningCode(String), | 16 | InvalidWarningCode(String), |
17 | #[error("unable to parse config file, error at: `{0}`")] | ||
18 | ConfFileParse(String), | ||
17 | } | 19 | } |
18 | 20 | ||
19 | // #[derive(Error, Debug)] | 21 | // #[derive(Error, Debug)] |
diff --git a/bin/src/explain.rs b/bin/src/explain.rs index de171aa..bcb8eed 100644 --- a/bin/src/explain.rs +++ b/bin/src/explain.rs | |||
@@ -1,11 +1,10 @@ | |||
1 | use crate::err::ExplainErr; | 1 | use crate::{err::ExplainErr, utils}; |
2 | |||
3 | use lib::LINTS; | ||
4 | 2 | ||
5 | pub fn explain(code: u32) -> Result<&'static str, ExplainErr> { | 3 | pub fn explain(code: u32) -> Result<&'static str, ExplainErr> { |
4 | let lints = utils::lint_map(); | ||
6 | match code { | 5 | match code { |
7 | 0 => Ok("syntax error"), | 6 | 0 => Ok("syntax error"), |
8 | _ => LINTS | 7 | _ => lints |
9 | .values() | 8 | .values() |
10 | .flatten() | 9 | .flatten() |
11 | .find(|l| l.code() == code) | 10 | .find(|l| l.code() == code) |
diff --git a/bin/src/fix.rs b/bin/src/fix.rs index e4ea94d..4268567 100644 --- a/bin/src/fix.rs +++ b/bin/src/fix.rs | |||
@@ -1,19 +1,21 @@ | |||
1 | use std::borrow::Cow; | 1 | use std::borrow::Cow; |
2 | 2 | ||
3 | use crate::LintMap; | ||
4 | |||
3 | use rnix::TextRange; | 5 | use rnix::TextRange; |
4 | 6 | ||
5 | mod all; | 7 | mod all; |
6 | use all::all; | 8 | use all::all_with; |
7 | 9 | ||
8 | mod single; | 10 | mod single; |
9 | use single::single; | 11 | use single::single; |
10 | 12 | ||
11 | type Source<'a> = Cow<'a, str>; | 13 | type Source<'a> = Cow<'a, str>; |
12 | 14 | ||
13 | #[derive(Debug)] | ||
14 | pub struct FixResult<'a> { | 15 | pub struct FixResult<'a> { |
15 | pub src: Source<'a>, | 16 | pub src: Source<'a>, |
16 | pub fixed: Vec<Fixed>, | 17 | pub fixed: Vec<Fixed>, |
18 | pub lints: &'a LintMap, | ||
17 | } | 19 | } |
18 | 20 | ||
19 | #[derive(Debug, Clone)] | 21 | #[derive(Debug, Clone)] |
@@ -23,10 +25,11 @@ pub struct Fixed { | |||
23 | } | 25 | } |
24 | 26 | ||
25 | impl<'a> FixResult<'a> { | 27 | impl<'a> FixResult<'a> { |
26 | fn empty(src: Source<'a>) -> Self { | 28 | fn empty(src: Source<'a>, lints: &'a LintMap) -> Self { |
27 | Self { | 29 | Self { |
28 | src, | 30 | src, |
29 | fixed: Vec::new(), | 31 | fixed: Vec::new(), |
32 | lints, | ||
30 | } | 33 | } |
31 | } | 34 | } |
32 | } | 35 | } |
@@ -43,8 +46,9 @@ pub mod main { | |||
43 | 46 | ||
44 | pub fn all(fix_config: FixConfig) -> Result<(), StatixErr> { | 47 | pub fn all(fix_config: FixConfig) -> Result<(), StatixErr> { |
45 | let vfs = fix_config.vfs()?; | 48 | let vfs = fix_config.vfs()?; |
49 | let lints = fix_config.lints()?; | ||
46 | for entry in vfs.iter() { | 50 | for entry in vfs.iter() { |
47 | match (fix_config.out(), super::all(entry.contents)) { | 51 | match (fix_config.out(), super::all_with(entry.contents, &lints)) { |
48 | (FixOut::Diff, fix_result) => { | 52 | (FixOut::Diff, fix_result) => { |
49 | let src = fix_result | 53 | let src = fix_result |
50 | .map(|r| r.src) | 54 | .map(|r| r.src) |
diff --git a/bin/src/fix/all.rs b/bin/src/fix/all.rs index 7f04f2c..bbc39e8 100644 --- a/bin/src/fix/all.rs +++ b/bin/src/fix/all.rs | |||
@@ -1,18 +1,21 @@ | |||
1 | use std::borrow::Cow; | 1 | use std::borrow::Cow; |
2 | 2 | ||
3 | use lib::{Report, LINTS}; | 3 | use lib::Report; |
4 | use rnix::{parser::ParseError as RnixParseErr, WalkEvent}; | 4 | use rnix::{parser::ParseError as RnixParseErr, WalkEvent}; |
5 | 5 | ||
6 | use crate::fix::{FixResult, Fixed}; | 6 | use crate::{ |
7 | fix::{FixResult, Fixed}, | ||
8 | LintMap, | ||
9 | }; | ||
7 | 10 | ||
8 | fn collect_fixes(source: &str) -> Result<Vec<Report>, RnixParseErr> { | 11 | fn collect_fixes(source: &str, lints: &LintMap) -> Result<Vec<Report>, RnixParseErr> { |
9 | let parsed = rnix::parse(source).as_result()?; | 12 | let parsed = rnix::parse(source).as_result()?; |
10 | 13 | ||
11 | Ok(parsed | 14 | Ok(parsed |
12 | .node() | 15 | .node() |
13 | .preorder_with_tokens() | 16 | .preorder_with_tokens() |
14 | .filter_map(|event| match event { | 17 | .filter_map(|event| match event { |
15 | WalkEvent::Enter(child) => LINTS.get(&child.kind()).map(|rules| { | 18 | WalkEvent::Enter(child) => lints.get(&child.kind()).map(|rules| { |
16 | rules | 19 | rules |
17 | .iter() | 20 | .iter() |
18 | .filter_map(|rule| rule.validate(&child)) | 21 | .filter_map(|rule| rule.validate(&child)) |
@@ -54,7 +57,7 @@ fn reorder(mut reports: Vec<Report>) -> Vec<Report> { | |||
54 | impl<'a> Iterator for FixResult<'a> { | 57 | impl<'a> Iterator for FixResult<'a> { |
55 | type Item = FixResult<'a>; | 58 | type Item = FixResult<'a>; |
56 | fn next(&mut self) -> Option<Self::Item> { | 59 | fn next(&mut self) -> Option<Self::Item> { |
57 | let all_reports = collect_fixes(&self.src).ok()?; | 60 | let all_reports = collect_fixes(&self.src, &self.lints).ok()?; |
58 | if all_reports.is_empty() { | 61 | if all_reports.is_empty() { |
59 | return None; | 62 | return None; |
60 | } | 63 | } |
@@ -74,13 +77,14 @@ impl<'a> Iterator for FixResult<'a> { | |||
74 | Some(FixResult { | 77 | Some(FixResult { |
75 | src: self.src.clone(), | 78 | src: self.src.clone(), |
76 | fixed, | 79 | fixed, |
80 | lints: self.lints, | ||
77 | }) | 81 | }) |
78 | } | 82 | } |
79 | } | 83 | } |
80 | 84 | ||
81 | pub fn all(src: &str) -> Option<FixResult> { | 85 | pub fn all_with<'a>(src: &'a str, lints: &'a LintMap) -> Option<FixResult<'a>> { |
82 | let src = Cow::from(src); | 86 | let src = Cow::from(src); |
83 | let _ = rnix::parse(&src).as_result().ok()?; | 87 | let _ = rnix::parse(&src).as_result().ok()?; |
84 | let initial = FixResult::empty(src); | 88 | let initial = FixResult::empty(src, lints); |
85 | initial.into_iter().last() | 89 | initial.into_iter().last() |
86 | } | 90 | } |
diff --git a/bin/src/fix/single.rs b/bin/src/fix/single.rs index b91dc45..d95cfda 100644 --- a/bin/src/fix/single.rs +++ b/bin/src/fix/single.rs | |||
@@ -1,10 +1,9 @@ | |||
1 | use std::{borrow::Cow, convert::TryFrom}; | 1 | use std::{borrow::Cow, convert::TryFrom}; |
2 | 2 | ||
3 | use lib::{Report, LINTS}; | 3 | use lib::Report; |
4 | use rnix::{TextSize, WalkEvent}; | 4 | use rnix::{TextSize, WalkEvent}; |
5 | 5 | ||
6 | use crate::err::SingleFixErr; | 6 | use crate::{err::SingleFixErr, fix::Source, utils}; |
7 | use crate::fix::Source; | ||
8 | 7 | ||
9 | pub struct SingleFixResult<'δ> { | 8 | pub struct SingleFixResult<'δ> { |
10 | pub src: Source<'δ>, | 9 | pub src: Source<'δ>, |
@@ -31,12 +30,13 @@ fn pos_to_byte(line: usize, col: usize, src: &str) -> Result<TextSize, SingleFix | |||
31 | fn find(offset: TextSize, src: &str) -> Result<Report, SingleFixErr> { | 30 | fn find(offset: TextSize, src: &str) -> Result<Report, SingleFixErr> { |
32 | // we don't really need the source to form a completely parsed tree | 31 | // we don't really need the source to form a completely parsed tree |
33 | let parsed = rnix::parse(src); | 32 | let parsed = rnix::parse(src); |
33 | let lints = utils::lint_map(); | ||
34 | 34 | ||
35 | parsed | 35 | parsed |
36 | .node() | 36 | .node() |
37 | .preorder_with_tokens() | 37 | .preorder_with_tokens() |
38 | .filter_map(|event| match event { | 38 | .filter_map(|event| match event { |
39 | WalkEvent::Enter(child) => LINTS.get(&child.kind()).map(|rules| { | 39 | WalkEvent::Enter(child) => lints.get(&child.kind()).map(|rules| { |
40 | rules | 40 | rules |
41 | .iter() | 41 | .iter() |
42 | .filter_map(|rule| rule.validate(&child)) | 42 | .filter_map(|rule| rule.validate(&child)) |
diff --git a/bin/src/lib.rs b/bin/src/lib.rs index 49c1a41..0105334 100644 --- a/bin/src/lib.rs +++ b/bin/src/lib.rs | |||
@@ -5,3 +5,12 @@ pub mod explain; | |||
5 | pub mod fix; | 5 | pub mod fix; |
6 | pub mod lint; | 6 | pub mod lint; |
7 | pub mod traits; | 7 | pub mod traits; |
8 | |||
9 | mod utils; | ||
10 | |||
11 | use std::collections::HashMap; | ||
12 | |||
13 | use lib::Lint; | ||
14 | use rnix::SyntaxKind; | ||
15 | |||
16 | pub type LintMap = HashMap<SyntaxKind, Vec<&'static Box<dyn Lint>>>; | ||
diff --git a/bin/src/lint.rs b/bin/src/lint.rs index 1138c23..3482d46 100644 --- a/bin/src/lint.rs +++ b/bin/src/lint.rs | |||
@@ -1,4 +1,6 @@ | |||
1 | use lib::{Report, LINTS}; | 1 | use crate::{utils, LintMap}; |
2 | |||
3 | use lib::Report; | ||
2 | use rnix::WalkEvent; | 4 | use rnix::WalkEvent; |
3 | use vfs::{FileId, VfsEntry}; | 5 | use vfs::{FileId, VfsEntry}; |
4 | 6 | ||
@@ -8,18 +10,17 @@ pub struct LintResult { | |||
8 | pub reports: Vec<Report>, | 10 | pub reports: Vec<Report>, |
9 | } | 11 | } |
10 | 12 | ||
11 | pub fn lint(vfs_entry: VfsEntry) -> LintResult { | 13 | pub fn lint_with(vfs_entry: VfsEntry, lints: &LintMap) -> LintResult { |
12 | let file_id = vfs_entry.file_id; | 14 | let file_id = vfs_entry.file_id; |
13 | let source = vfs_entry.contents; | 15 | let source = vfs_entry.contents; |
14 | let parsed = rnix::parse(source); | 16 | let parsed = rnix::parse(source); |
15 | 17 | ||
16 | let error_reports = parsed.errors().into_iter().map(Report::from_parse_err); | 18 | let error_reports = parsed.errors().into_iter().map(Report::from_parse_err); |
17 | |||
18 | let reports = parsed | 19 | let reports = parsed |
19 | .node() | 20 | .node() |
20 | .preorder_with_tokens() | 21 | .preorder_with_tokens() |
21 | .filter_map(|event| match event { | 22 | .filter_map(|event| match event { |
22 | WalkEvent::Enter(child) => LINTS.get(&child.kind()).map(|rules| { | 23 | WalkEvent::Enter(child) => lints.get(&child.kind()).map(|rules| { |
23 | rules | 24 | rules |
24 | .iter() | 25 | .iter() |
25 | .filter_map(|rule| rule.validate(&child)) | 26 | .filter_map(|rule| rule.validate(&child)) |
@@ -34,15 +35,21 @@ pub fn lint(vfs_entry: VfsEntry) -> LintResult { | |||
34 | LintResult { file_id, reports } | 35 | LintResult { file_id, reports } |
35 | } | 36 | } |
36 | 37 | ||
38 | pub fn lint(vfs_entry: VfsEntry) -> LintResult { | ||
39 | lint_with(vfs_entry, &utils::lint_map()) | ||
40 | } | ||
41 | |||
37 | pub mod main { | 42 | pub mod main { |
38 | use std::io; | 43 | use std::io; |
39 | 44 | ||
40 | use super::lint; | 45 | use super::lint_with; |
41 | use crate::{config::Check as CheckConfig, err::StatixErr, traits::WriteDiagnostic}; | 46 | use crate::{config::Check as CheckConfig, err::StatixErr, traits::WriteDiagnostic}; |
42 | 47 | ||
43 | pub fn main(check_config: CheckConfig) -> Result<(), StatixErr> { | 48 | pub fn main(check_config: CheckConfig) -> Result<(), StatixErr> { |
44 | let vfs = check_config.vfs()?; | 49 | let vfs = check_config.vfs()?; |
45 | let mut stdout = io::stdout(); | 50 | let mut stdout = io::stdout(); |
51 | let lints = check_config.lints()?; | ||
52 | let lint = |vfs_entry| lint_with(vfs_entry, &lints); | ||
46 | vfs.iter().map(lint).for_each(|r| { | 53 | vfs.iter().map(lint).for_each(|r| { |
47 | stdout.write(&r, &vfs, check_config.format).unwrap(); | 54 | stdout.write(&r, &vfs, check_config.format).unwrap(); |
48 | }); | 55 | }); |
diff --git a/bin/src/utils.rs b/bin/src/utils.rs new file mode 100644 index 0000000..747a761 --- /dev/null +++ b/bin/src/utils.rs | |||
@@ -0,0 +1,24 @@ | |||
1 | use std::collections::HashMap; | ||
2 | |||
3 | use lib::{Lint, LINTS}; | ||
4 | use rnix::SyntaxKind; | ||
5 | |||
6 | pub fn lint_map_of( | ||
7 | lints: &[&'static Box<dyn Lint>], | ||
8 | ) -> HashMap<SyntaxKind, Vec<&'static Box<dyn Lint>>> { | ||
9 | let mut map = HashMap::new(); | ||
10 | for lint in lints.iter() { | ||
11 | let lint = *lint; | ||
12 | let matches = lint.match_kind(); | ||
13 | for m in matches { | ||
14 | map.entry(m) | ||
15 | .and_modify(|v: &mut Vec<_>| v.push(lint)) | ||
16 | .or_insert_with(|| vec![lint]); | ||
17 | } | ||
18 | } | ||
19 | map | ||
20 | } | ||
21 | |||
22 | pub fn lint_map() -> HashMap<SyntaxKind, Vec<&'static Box<dyn Lint>>> { | ||
23 | lint_map_of(&*LINTS) | ||
24 | } | ||