aboutsummaryrefslogtreecommitdiff
path: root/bin/src
diff options
context:
space:
mode:
authorAkshay <[email protected]>2021-11-06 09:21:55 +0000
committerAkshay <[email protected]>2021-11-06 09:21:55 +0000
commit324a333e671ed521138ad50fe463ed187874176e (patch)
tree8f294a109955881a14dbbb2bc088f14255fad687 /bin/src
parent63eba9d5c697e466d952588568719d0de1244ae4 (diff)
introducing "streaming" option to check, fix and single
Diffstat (limited to 'bin/src')
-rw-r--r--bin/src/config.rs92
-rw-r--r--bin/src/explain.rs11
-rw-r--r--bin/src/fix.rs88
-rw-r--r--bin/src/lint.rs21
-rw-r--r--bin/src/main.rs85
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
47impl Check { 51impl 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
93pub enum FixOut {
94 Diff,
95 Stream,
96 Write,
72} 97}
73 98
74impl Fix { 99impl 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
149impl 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
17pub 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;
3use rnix::TextRange; 3use rnix::TextRange;
4 4
5mod all; 5mod all;
6pub use all::all; 6use all::all;
7 7
8mod single; 8mod single;
9pub use single::single; 9use single::single;
10 10
11type Source<'a> = Cow<'a, str>; 11type Source<'a> = Cow<'a, str>;
12 12
@@ -30,3 +30,87 @@ impl<'a> FixResult<'a> {
30 } 30 }
31 } 31 }
32} 32}
33
34pub 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
37pub 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;
6mod lint; 6mod lint;
7mod traits; 7mod traits;
8 8
9use std::io::{self, BufRead}; 9use crate::err::StatixErr;
10
11use crate::{
12 err::{FixErr, SingleFixErr, StatixErr},
13 traits::WriteDiagnostic,
14};
15 10
16use clap::Clap; 11use clap::Clap;
17use config::{Opts, SubCommand}; 12use config::{Opts, SubCommand};
18use similar::TextDiff;
19 13
20fn _main() -> Result<(), StatixErr> { 14fn _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
99fn main() { 24fn main() {