diff options
-rw-r--r-- | bin/src/config.rs | 92 | ||||
-rw-r--r-- | bin/src/explain.rs | 11 | ||||
-rw-r--r-- | bin/src/fix.rs | 88 | ||||
-rw-r--r-- | bin/src/lint.rs | 21 | ||||
-rw-r--r-- | bin/src/main.rs | 85 |
5 files changed, 205 insertions, 92 deletions
diff --git a/bin/src/config.rs b/bin/src/config.rs index e0f5cbd..25c2a7f 100644 --- a/bin/src/config.rs +++ b/bin/src/config.rs | |||
@@ -42,13 +42,28 @@ pub struct Check { | |||
42 | /// Supported values: stderr, errfmt, json (on feature flag only) | 42 | /// Supported values: stderr, errfmt, json (on feature flag only) |
43 | #[clap(short = 'o', long, default_value_t, parse(try_from_str))] | 43 | #[clap(short = 'o', long, default_value_t, parse(try_from_str))] |
44 | pub format: OutFormat, | 44 | pub format: OutFormat, |
45 | |||
46 | /// Enable "streaming" mode, accept file on stdin, output diagnostics on stdout | ||
47 | #[clap(short, long = "stdin")] | ||
48 | pub streaming: bool, | ||
45 | } | 49 | } |
46 | 50 | ||
47 | impl Check { | 51 | impl Check { |
48 | pub fn vfs(&self) -> Result<ReadOnlyVfs, ConfigErr> { | 52 | pub fn vfs(&self) -> Result<ReadOnlyVfs, ConfigErr> { |
49 | let ignore = dirs::build_ignore_set(&self.ignore, &self.target, self.unrestricted)?; | 53 | if self.streaming { |
50 | let files = dirs::walk_nix_files(ignore, &self.target)?; | 54 | use std::io::{self, BufRead}; |
51 | vfs(files.collect::<Vec<_>>()) | 55 | let src = io::stdin() |
56 | .lock() | ||
57 | .lines() | ||
58 | .map(|l| l.unwrap()) | ||
59 | .collect::<Vec<String>>() | ||
60 | .join("\n"); | ||
61 | Ok(ReadOnlyVfs::singleton("<stdin>", src.as_bytes())) | ||
62 | } else { | ||
63 | let ignore = dirs::build_ignore_set(&self.ignore, &self.target, self.unrestricted)?; | ||
64 | let files = dirs::walk_nix_files(ignore, &self.target)?; | ||
65 | vfs(files.collect::<Vec<_>>()) | ||
66 | } | ||
52 | } | 67 | } |
53 | } | 68 | } |
54 | 69 | ||
@@ -69,13 +84,46 @@ pub struct Fix { | |||
69 | /// Do not fix files in place, display a diff instead | 84 | /// Do not fix files in place, display a diff instead |
70 | #[clap(short, long = "dry-run")] | 85 | #[clap(short, long = "dry-run")] |
71 | pub diff_only: bool, | 86 | pub diff_only: bool, |
87 | |||
88 | /// Enable "streaming" mode, accept file on stdin, output diagnostics on stdout | ||
89 | #[clap(short, long = "stdin")] | ||
90 | pub streaming: bool, | ||
91 | } | ||
92 | |||
93 | pub enum FixOut { | ||
94 | Diff, | ||
95 | Stream, | ||
96 | Write, | ||
72 | } | 97 | } |
73 | 98 | ||
74 | impl Fix { | 99 | impl Fix { |
75 | pub fn vfs(&self) -> Result<ReadOnlyVfs, ConfigErr> { | 100 | pub fn vfs(&self) -> Result<ReadOnlyVfs, ConfigErr> { |
76 | let ignore = dirs::build_ignore_set(&self.ignore, &self.target, self.unrestricted)?; | 101 | if self.streaming { |
77 | let files = dirs::walk_nix_files(ignore, &self.target)?; | 102 | use std::io::{self, BufRead}; |
78 | vfs(files.collect::<Vec<_>>()) | 103 | let src = io::stdin() |
104 | .lock() | ||
105 | .lines() | ||
106 | .map(|l| l.unwrap()) | ||
107 | .collect::<Vec<String>>() | ||
108 | .join("\n"); | ||
109 | Ok(ReadOnlyVfs::singleton("<stdin>", src.as_bytes())) | ||
110 | } else { | ||
111 | let ignore = dirs::build_ignore_set(&self.ignore, &self.target, self.unrestricted)?; | ||
112 | let files = dirs::walk_nix_files(ignore, &self.target)?; | ||
113 | vfs(files.collect::<Vec<_>>()) | ||
114 | } | ||
115 | } | ||
116 | |||
117 | // i need this ugly helper because clap's data model | ||
118 | // does not reflect what i have in mind | ||
119 | pub fn out(&self) -> FixOut { | ||
120 | if self.diff_only { | ||
121 | FixOut::Diff | ||
122 | } else if self.streaming { | ||
123 | FixOut::Stream | ||
124 | } else { | ||
125 | FixOut::Write | ||
126 | } | ||
79 | } | 127 | } |
80 | } | 128 | } |
81 | 129 | ||
@@ -92,6 +140,38 @@ pub struct Single { | |||
92 | /// Do not fix files in place, display a diff instead | 140 | /// Do not fix files in place, display a diff instead |
93 | #[clap(short, long = "dry-run")] | 141 | #[clap(short, long = "dry-run")] |
94 | pub diff_only: bool, | 142 | pub diff_only: bool, |
143 | |||
144 | /// Enable "streaming" mode, accept file on stdin, output diagnostics on stdout | ||
145 | #[clap(short, long = "stdin")] | ||
146 | pub streaming: bool, | ||
147 | } | ||
148 | |||
149 | impl Single { | ||
150 | pub fn vfs(&self) -> Result<ReadOnlyVfs, ConfigErr> { | ||
151 | if self.streaming { | ||
152 | use std::io::{self, BufRead}; | ||
153 | let src = io::stdin() | ||
154 | .lock() | ||
155 | .lines() | ||
156 | .map(|l| l.unwrap()) | ||
157 | .collect::<Vec<String>>() | ||
158 | .join("\n"); | ||
159 | Ok(ReadOnlyVfs::singleton("<stdin>", src.as_bytes())) | ||
160 | } else { | ||
161 | let src = std::fs::read_to_string(self.target.as_ref().unwrap()) | ||
162 | .map_err(ConfigErr::InvalidPath)?; | ||
163 | Ok(ReadOnlyVfs::singleton("<stdin>", src.as_bytes())) | ||
164 | } | ||
165 | } | ||
166 | pub fn out(&self) -> FixOut { | ||
167 | if self.diff_only { | ||
168 | FixOut::Diff | ||
169 | } else if self.streaming { | ||
170 | FixOut::Stream | ||
171 | } else { | ||
172 | FixOut::Write | ||
173 | } | ||
174 | } | ||
95 | } | 175 | } |
96 | 176 | ||
97 | #[derive(Clap, Debug)] | 177 | #[derive(Clap, Debug)] |
diff --git a/bin/src/explain.rs b/bin/src/explain.rs index 6aefa7e..de171aa 100644 --- a/bin/src/explain.rs +++ b/bin/src/explain.rs | |||
@@ -13,3 +13,14 @@ pub fn explain(code: u32) -> Result<&'static str, ExplainErr> { | |||
13 | .ok_or(ExplainErr::LintNotFound(code)), | 13 | .ok_or(ExplainErr::LintNotFound(code)), |
14 | } | 14 | } |
15 | } | 15 | } |
16 | |||
17 | pub mod main { | ||
18 | |||
19 | use crate::{config::Explain as ExplainConfig, err::StatixErr}; | ||
20 | |||
21 | pub fn main(explain_config: ExplainConfig) -> Result<(), StatixErr> { | ||
22 | let explanation = super::explain(explain_config.target)?; | ||
23 | println!("{}", explanation); | ||
24 | Ok(()) | ||
25 | } | ||
26 | } | ||
diff --git a/bin/src/fix.rs b/bin/src/fix.rs index c378c13..e4ea94d 100644 --- a/bin/src/fix.rs +++ b/bin/src/fix.rs | |||
@@ -3,10 +3,10 @@ use std::borrow::Cow; | |||
3 | use rnix::TextRange; | 3 | use rnix::TextRange; |
4 | 4 | ||
5 | mod all; | 5 | mod all; |
6 | pub use all::all; | 6 | use all::all; |
7 | 7 | ||
8 | mod single; | 8 | mod single; |
9 | pub use single::single; | 9 | use single::single; |
10 | 10 | ||
11 | type Source<'a> = Cow<'a, str>; | 11 | type Source<'a> = Cow<'a, str>; |
12 | 12 | ||
@@ -30,3 +30,87 @@ impl<'a> FixResult<'a> { | |||
30 | } | 30 | } |
31 | } | 31 | } |
32 | } | 32 | } |
33 | |||
34 | pub mod main { | ||
35 | use std::borrow::Cow; | ||
36 | |||
37 | use crate::{ | ||
38 | config::{Fix as FixConfig, FixOut, Single as SingleConfig}, | ||
39 | err::{FixErr, StatixErr}, | ||
40 | }; | ||
41 | |||
42 | use similar::TextDiff; | ||
43 | |||
44 | pub fn all(fix_config: FixConfig) -> Result<(), StatixErr> { | ||
45 | let vfs = fix_config.vfs()?; | ||
46 | for entry in vfs.iter() { | ||
47 | match (fix_config.out(), super::all(entry.contents)) { | ||
48 | (FixOut::Diff, fix_result) => { | ||
49 | let src = fix_result | ||
50 | .map(|r| r.src) | ||
51 | .unwrap_or(Cow::Borrowed(entry.contents)); | ||
52 | let text_diff = TextDiff::from_lines(entry.contents, &src); | ||
53 | let old_file = format!("{}", entry.file_path.display()); | ||
54 | let new_file = format!("{} [fixed]", entry.file_path.display()); | ||
55 | println!( | ||
56 | "{}", | ||
57 | text_diff | ||
58 | .unified_diff() | ||
59 | .context_radius(4) | ||
60 | .header(&old_file, &new_file) | ||
61 | ); | ||
62 | } | ||
63 | (FixOut::Stream, fix_result) => { | ||
64 | let src = fix_result | ||
65 | .map(|r| r.src) | ||
66 | .unwrap_or(Cow::Borrowed(entry.contents)); | ||
67 | println!("{}", &src) | ||
68 | } | ||
69 | (FixOut::Write, Some(fix_result)) => { | ||
70 | let path = entry.file_path; | ||
71 | std::fs::write(path, &*fix_result.src).map_err(FixErr::InvalidPath)?; | ||
72 | } | ||
73 | _ => (), | ||
74 | }; | ||
75 | } | ||
76 | Ok(()) | ||
77 | } | ||
78 | |||
79 | pub fn single(single_config: SingleConfig) -> Result<(), StatixErr> { | ||
80 | let vfs = single_config.vfs()?; | ||
81 | let entry = vfs.iter().next().unwrap(); | ||
82 | let path = entry.file_path.display().to_string(); | ||
83 | let original_src = entry.contents; | ||
84 | let (line, col) = single_config.position; | ||
85 | |||
86 | match (single_config.out(), super::single(line, col, original_src)) { | ||
87 | (FixOut::Diff, single_result) => { | ||
88 | let fixed_src = single_result | ||
89 | .map(|r| r.src) | ||
90 | .unwrap_or(Cow::Borrowed(original_src)); | ||
91 | let text_diff = TextDiff::from_lines(original_src, &fixed_src); | ||
92 | let old_file = &path; | ||
93 | let new_file = format!("{} [fixed]", &path); | ||
94 | println!( | ||
95 | "{}", | ||
96 | text_diff | ||
97 | .unified_diff() | ||
98 | .context_radius(4) | ||
99 | .header(&old_file, &new_file) | ||
100 | ); | ||
101 | } | ||
102 | (FixOut::Stream, single_result) => { | ||
103 | let src = single_result | ||
104 | .map(|r| r.src) | ||
105 | .unwrap_or(Cow::Borrowed(original_src)); | ||
106 | println!("{}", &src) | ||
107 | } | ||
108 | (FixOut::Write, Ok(single_result)) => { | ||
109 | let path = entry.file_path; | ||
110 | std::fs::write(path, &*single_result.src).map_err(FixErr::InvalidPath)?; | ||
111 | } | ||
112 | (_, Err(e)) => return Err(e.into()), | ||
113 | }; | ||
114 | Ok(()) | ||
115 | } | ||
116 | } | ||
diff --git a/bin/src/lint.rs b/bin/src/lint.rs index b5856c1..da9ee22 100644 --- a/bin/src/lint.rs +++ b/bin/src/lint.rs | |||
@@ -13,10 +13,7 @@ pub fn lint(vfs_entry: VfsEntry) -> LintResult { | |||
13 | let source = vfs_entry.contents; | 13 | let source = vfs_entry.contents; |
14 | let parsed = rnix::parse(source); | 14 | let parsed = rnix::parse(source); |
15 | 15 | ||
16 | let error_reports = parsed | 16 | let error_reports = parsed.errors().into_iter().map(Report::from_parse_err); |
17 | .errors() | ||
18 | .into_iter() | ||
19 | .map(Report::from_parse_err); | ||
20 | 17 | ||
21 | let reports = parsed | 18 | let reports = parsed |
22 | .node() | 19 | .node() |
@@ -36,3 +33,19 @@ pub fn lint(vfs_entry: VfsEntry) -> LintResult { | |||
36 | 33 | ||
37 | LintResult { file_id, reports } | 34 | LintResult { file_id, reports } |
38 | } | 35 | } |
36 | |||
37 | pub mod main { | ||
38 | use std::io; | ||
39 | |||
40 | use super::lint; | ||
41 | use crate::{config::Check as CheckConfig, err::StatixErr, traits::WriteDiagnostic}; | ||
42 | |||
43 | pub fn main(check_config: CheckConfig) -> Result<(), StatixErr> { | ||
44 | let vfs = check_config.vfs()?; | ||
45 | let mut stderr = io::stderr(); | ||
46 | vfs.iter().map(lint).for_each(|r| { | ||
47 | stderr.write(&r, &vfs, check_config.format).unwrap(); | ||
48 | }); | ||
49 | Ok(()) | ||
50 | } | ||
51 | } | ||
diff --git a/bin/src/main.rs b/bin/src/main.rs index 3adf42e..fabc509 100644 --- a/bin/src/main.rs +++ b/bin/src/main.rs | |||
@@ -6,94 +6,19 @@ mod fix; | |||
6 | mod lint; | 6 | mod lint; |
7 | mod traits; | 7 | mod traits; |
8 | 8 | ||
9 | use std::io::{self, BufRead}; | 9 | use crate::err::StatixErr; |
10 | |||
11 | use crate::{ | ||
12 | err::{FixErr, SingleFixErr, StatixErr}, | ||
13 | traits::WriteDiagnostic, | ||
14 | }; | ||
15 | 10 | ||
16 | use clap::Clap; | 11 | use clap::Clap; |
17 | use config::{Opts, SubCommand}; | 12 | use config::{Opts, SubCommand}; |
18 | use similar::TextDiff; | ||
19 | 13 | ||
20 | fn _main() -> Result<(), StatixErr> { | 14 | fn _main() -> Result<(), StatixErr> { |
21 | let opts = Opts::parse(); | 15 | let opts = Opts::parse(); |
22 | match opts.cmd { | 16 | match opts.cmd { |
23 | SubCommand::Check(check_config) => { | 17 | SubCommand::Check(config) => lint::main::main(config), |
24 | let vfs = check_config.vfs()?; | 18 | SubCommand::Fix(config) => fix::main::all(config), |
25 | let mut stderr = io::stderr(); | 19 | SubCommand::Single(config) => fix::main::single(config), |
26 | vfs.iter().map(lint::lint).for_each(|r| { | 20 | SubCommand::Explain(config) => explain::main::main(config), |
27 | stderr.write(&r, &vfs, check_config.format).unwrap(); | ||
28 | }); | ||
29 | } | ||
30 | SubCommand::Fix(fix_config) => { | ||
31 | let vfs = fix_config.vfs()?; | ||
32 | for entry in vfs.iter() { | ||
33 | if let Some(fix_result) = fix::all(entry.contents) { | ||
34 | if fix_config.diff_only { | ||
35 | let text_diff = TextDiff::from_lines(entry.contents, &fix_result.src); | ||
36 | let old_file = format!("{}", entry.file_path.display()); | ||
37 | let new_file = format!("{} [fixed]", entry.file_path.display()); | ||
38 | println!( | ||
39 | "{}", | ||
40 | text_diff | ||
41 | .unified_diff() | ||
42 | .context_radius(4) | ||
43 | .header(&old_file, &new_file) | ||
44 | ); | ||
45 | } else { | ||
46 | let path = entry.file_path; | ||
47 | std::fs::write(path, &*fix_result.src).map_err(FixErr::InvalidPath)?; | ||
48 | } | ||
49 | } | ||
50 | } | ||
51 | } | ||
52 | // FIXME: this block nasty, configure in/out streams in `impl Single` maybe | ||
53 | SubCommand::Single(single_config) => { | ||
54 | let src = if let Some(path) = &single_config.target { | ||
55 | std::fs::read_to_string(&path).map_err(SingleFixErr::InvalidPath)? | ||
56 | } else { | ||
57 | io::stdin() | ||
58 | .lock() | ||
59 | .lines() | ||
60 | .map(|l| l.unwrap()) | ||
61 | .collect::<Vec<String>>() | ||
62 | .join("\n") | ||
63 | }; | ||
64 | |||
65 | let path_id = if let Some(path) = &single_config.target { | ||
66 | path.display().to_string() | ||
67 | } else { | ||
68 | "<unknown>".to_owned() | ||
69 | }; | ||
70 | |||
71 | let (line, col) = single_config.position; | ||
72 | let single_fix_result = fix::single(line, col, &src)?; | ||
73 | if single_config.diff_only { | ||
74 | let text_diff = TextDiff::from_lines(src.as_str(), &single_fix_result.src); | ||
75 | let old_file = path_id.to_string(); | ||
76 | let new_file = format!("{} [fixed]", path_id); | ||
77 | println!( | ||
78 | "{}", | ||
79 | text_diff | ||
80 | .unified_diff() | ||
81 | .context_radius(4) | ||
82 | .header(&old_file, &new_file) | ||
83 | ); | ||
84 | } else if let Some(path) = single_config.target { | ||
85 | std::fs::write(&path, &*single_fix_result.src) | ||
86 | .map_err(SingleFixErr::InvalidPath)?; | ||
87 | } else { | ||
88 | print!("{}", &*single_fix_result.src) | ||
89 | } | ||
90 | } | ||
91 | SubCommand::Explain(explain_config) => { | ||
92 | let explanation = explain::explain(explain_config.target)?; | ||
93 | println!("{}", explanation) | ||
94 | } | ||
95 | } | 21 | } |
96 | Ok(()) | ||
97 | } | 22 | } |
98 | 23 | ||
99 | fn main() { | 24 | fn main() { |