From 324a333e671ed521138ad50fe463ed187874176e Mon Sep 17 00:00:00 2001 From: Akshay Date: Sat, 6 Nov 2021 14:51:55 +0530 Subject: introducing "streaming" option to check, fix and single --- bin/src/config.rs | 92 ++++++++++++++++++++++++++++++++++++++++++++++++++---- bin/src/explain.rs | 11 +++++++ bin/src/fix.rs | 88 +++++++++++++++++++++++++++++++++++++++++++++++++-- bin/src/lint.rs | 21 ++++++++++--- 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 { /// Supported values: stderr, errfmt, json (on feature flag only) #[clap(short = 'o', long, default_value_t, parse(try_from_str))] pub format: OutFormat, + + /// Enable "streaming" mode, accept file on stdin, output diagnostics on stdout + #[clap(short, long = "stdin")] + pub streaming: bool, } impl Check { pub fn vfs(&self) -> Result { - let ignore = dirs::build_ignore_set(&self.ignore, &self.target, self.unrestricted)?; - let files = dirs::walk_nix_files(ignore, &self.target)?; - vfs(files.collect::>()) + if self.streaming { + use std::io::{self, BufRead}; + let src = io::stdin() + .lock() + .lines() + .map(|l| l.unwrap()) + .collect::>() + .join("\n"); + Ok(ReadOnlyVfs::singleton("", src.as_bytes())) + } else { + let ignore = dirs::build_ignore_set(&self.ignore, &self.target, self.unrestricted)?; + let files = dirs::walk_nix_files(ignore, &self.target)?; + vfs(files.collect::>()) + } } } @@ -69,13 +84,46 @@ pub struct Fix { /// Do not fix files in place, display a diff instead #[clap(short, long = "dry-run")] pub diff_only: bool, + + /// Enable "streaming" mode, accept file on stdin, output diagnostics on stdout + #[clap(short, long = "stdin")] + pub streaming: bool, +} + +pub enum FixOut { + Diff, + Stream, + Write, } impl Fix { pub fn vfs(&self) -> Result { - let ignore = dirs::build_ignore_set(&self.ignore, &self.target, self.unrestricted)?; - let files = dirs::walk_nix_files(ignore, &self.target)?; - vfs(files.collect::>()) + if self.streaming { + use std::io::{self, BufRead}; + let src = io::stdin() + .lock() + .lines() + .map(|l| l.unwrap()) + .collect::>() + .join("\n"); + Ok(ReadOnlyVfs::singleton("", src.as_bytes())) + } else { + let ignore = dirs::build_ignore_set(&self.ignore, &self.target, self.unrestricted)?; + let files = dirs::walk_nix_files(ignore, &self.target)?; + vfs(files.collect::>()) + } + } + + // i need this ugly helper because clap's data model + // does not reflect what i have in mind + pub fn out(&self) -> FixOut { + if self.diff_only { + FixOut::Diff + } else if self.streaming { + FixOut::Stream + } else { + FixOut::Write + } } } @@ -92,6 +140,38 @@ pub struct Single { /// Do not fix files in place, display a diff instead #[clap(short, long = "dry-run")] pub diff_only: bool, + + /// Enable "streaming" mode, accept file on stdin, output diagnostics on stdout + #[clap(short, long = "stdin")] + pub streaming: bool, +} + +impl Single { + pub fn vfs(&self) -> Result { + if self.streaming { + use std::io::{self, BufRead}; + let src = io::stdin() + .lock() + .lines() + .map(|l| l.unwrap()) + .collect::>() + .join("\n"); + Ok(ReadOnlyVfs::singleton("", src.as_bytes())) + } else { + let src = std::fs::read_to_string(self.target.as_ref().unwrap()) + .map_err(ConfigErr::InvalidPath)?; + Ok(ReadOnlyVfs::singleton("", src.as_bytes())) + } + } + pub fn out(&self) -> FixOut { + if self.diff_only { + FixOut::Diff + } else if self.streaming { + FixOut::Stream + } else { + FixOut::Write + } + } } #[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> { .ok_or(ExplainErr::LintNotFound(code)), } } + +pub mod main { + + use crate::{config::Explain as ExplainConfig, err::StatixErr}; + + pub fn main(explain_config: ExplainConfig) -> Result<(), StatixErr> { + let explanation = super::explain(explain_config.target)?; + println!("{}", explanation); + Ok(()) + } +} 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; use rnix::TextRange; mod all; -pub use all::all; +use all::all; mod single; -pub use single::single; +use single::single; type Source<'a> = Cow<'a, str>; @@ -30,3 +30,87 @@ impl<'a> FixResult<'a> { } } } + +pub mod main { + use std::borrow::Cow; + + use crate::{ + config::{Fix as FixConfig, FixOut, Single as SingleConfig}, + err::{FixErr, StatixErr}, + }; + + use similar::TextDiff; + + pub fn all(fix_config: FixConfig) -> Result<(), StatixErr> { + let vfs = fix_config.vfs()?; + for entry in vfs.iter() { + match (fix_config.out(), super::all(entry.contents)) { + (FixOut::Diff, fix_result) => { + let src = fix_result + .map(|r| r.src) + .unwrap_or(Cow::Borrowed(entry.contents)); + let text_diff = TextDiff::from_lines(entry.contents, &src); + let old_file = format!("{}", entry.file_path.display()); + let new_file = format!("{} [fixed]", entry.file_path.display()); + println!( + "{}", + text_diff + .unified_diff() + .context_radius(4) + .header(&old_file, &new_file) + ); + } + (FixOut::Stream, fix_result) => { + let src = fix_result + .map(|r| r.src) + .unwrap_or(Cow::Borrowed(entry.contents)); + println!("{}", &src) + } + (FixOut::Write, Some(fix_result)) => { + let path = entry.file_path; + std::fs::write(path, &*fix_result.src).map_err(FixErr::InvalidPath)?; + } + _ => (), + }; + } + Ok(()) + } + + pub fn single(single_config: SingleConfig) -> Result<(), StatixErr> { + let vfs = single_config.vfs()?; + let entry = vfs.iter().next().unwrap(); + let path = entry.file_path.display().to_string(); + let original_src = entry.contents; + let (line, col) = single_config.position; + + match (single_config.out(), super::single(line, col, original_src)) { + (FixOut::Diff, single_result) => { + let fixed_src = single_result + .map(|r| r.src) + .unwrap_or(Cow::Borrowed(original_src)); + let text_diff = TextDiff::from_lines(original_src, &fixed_src); + let old_file = &path; + let new_file = format!("{} [fixed]", &path); + println!( + "{}", + text_diff + .unified_diff() + .context_radius(4) + .header(&old_file, &new_file) + ); + } + (FixOut::Stream, single_result) => { + let src = single_result + .map(|r| r.src) + .unwrap_or(Cow::Borrowed(original_src)); + println!("{}", &src) + } + (FixOut::Write, Ok(single_result)) => { + let path = entry.file_path; + std::fs::write(path, &*single_result.src).map_err(FixErr::InvalidPath)?; + } + (_, Err(e)) => return Err(e.into()), + }; + Ok(()) + } +} 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 { let source = vfs_entry.contents; let parsed = rnix::parse(source); - let error_reports = parsed - .errors() - .into_iter() - .map(Report::from_parse_err); + let error_reports = parsed.errors().into_iter().map(Report::from_parse_err); let reports = parsed .node() @@ -36,3 +33,19 @@ pub fn lint(vfs_entry: VfsEntry) -> LintResult { LintResult { file_id, reports } } + +pub mod main { + use std::io; + + use super::lint; + use crate::{config::Check as CheckConfig, err::StatixErr, traits::WriteDiagnostic}; + + pub fn main(check_config: CheckConfig) -> Result<(), StatixErr> { + let vfs = check_config.vfs()?; + let mut stderr = io::stderr(); + vfs.iter().map(lint).for_each(|r| { + stderr.write(&r, &vfs, check_config.format).unwrap(); + }); + Ok(()) + } +} 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; mod lint; mod traits; -use std::io::{self, BufRead}; - -use crate::{ - err::{FixErr, SingleFixErr, StatixErr}, - traits::WriteDiagnostic, -}; +use crate::err::StatixErr; use clap::Clap; use config::{Opts, SubCommand}; -use similar::TextDiff; fn _main() -> Result<(), StatixErr> { let opts = Opts::parse(); match opts.cmd { - SubCommand::Check(check_config) => { - let vfs = check_config.vfs()?; - let mut stderr = io::stderr(); - vfs.iter().map(lint::lint).for_each(|r| { - stderr.write(&r, &vfs, check_config.format).unwrap(); - }); - } - SubCommand::Fix(fix_config) => { - let vfs = fix_config.vfs()?; - for entry in vfs.iter() { - if let Some(fix_result) = fix::all(entry.contents) { - if fix_config.diff_only { - let text_diff = TextDiff::from_lines(entry.contents, &fix_result.src); - let old_file = format!("{}", entry.file_path.display()); - let new_file = format!("{} [fixed]", entry.file_path.display()); - println!( - "{}", - text_diff - .unified_diff() - .context_radius(4) - .header(&old_file, &new_file) - ); - } else { - let path = entry.file_path; - std::fs::write(path, &*fix_result.src).map_err(FixErr::InvalidPath)?; - } - } - } - } - // FIXME: this block nasty, configure in/out streams in `impl Single` maybe - SubCommand::Single(single_config) => { - let src = if let Some(path) = &single_config.target { - std::fs::read_to_string(&path).map_err(SingleFixErr::InvalidPath)? - } else { - io::stdin() - .lock() - .lines() - .map(|l| l.unwrap()) - .collect::>() - .join("\n") - }; - - let path_id = if let Some(path) = &single_config.target { - path.display().to_string() - } else { - "".to_owned() - }; - - let (line, col) = single_config.position; - let single_fix_result = fix::single(line, col, &src)?; - if single_config.diff_only { - let text_diff = TextDiff::from_lines(src.as_str(), &single_fix_result.src); - let old_file = path_id.to_string(); - let new_file = format!("{} [fixed]", path_id); - println!( - "{}", - text_diff - .unified_diff() - .context_radius(4) - .header(&old_file, &new_file) - ); - } else if let Some(path) = single_config.target { - std::fs::write(&path, &*single_fix_result.src) - .map_err(SingleFixErr::InvalidPath)?; - } else { - print!("{}", &*single_fix_result.src) - } - } - SubCommand::Explain(explain_config) => { - let explanation = explain::explain(explain_config.target)?; - println!("{}", explanation) - } + SubCommand::Check(config) => lint::main::main(config), + SubCommand::Fix(config) => fix::main::all(config), + SubCommand::Single(config) => fix::main::single(config), + SubCommand::Explain(config) => explain::main::main(config), } - Ok(()) } fn main() { -- cgit v1.2.3