From d5239978bee5c7078a902ef0ea36992b4a2f8647 Mon Sep 17 00:00:00 2001 From: Ben King <9087625+benfdking@users.noreply.github.com> Date: Wed, 30 Oct 2024 20:39:29 +0100 Subject: [PATCH] feat: ability to pipe fix and lint code --- crates/cli/src/main.rs | 211 ++++++++++++++++++++++++++++------------ crates/cli/src/stdin.rs | 3 + 2 files changed, 150 insertions(+), 64 deletions(-) create mode 100644 crates/cli/src/stdin.rs diff --git a/crates/cli/src/main.rs b/crates/cli/src/main.rs index 4bf41be1c..76a4220c9 100644 --- a/crates/cli/src/main.rs +++ b/crates/cli/src/main.rs @@ -3,8 +3,10 @@ use commands::{FixArgs, Format, LintArgs}; use sqruff_lib::cli::formatters::OutputStreamFormatter; use sqruff_lib::core::config::FluffConfig; use sqruff_lib::core::linter::core::Linter; +use stdin::is_std_in_flag_input; use std::path::Path; use std::sync::Arc; +use std::io::Read; use crate::commands::{Cli, Commands}; #[cfg(feature = "codegen-docs")] @@ -15,6 +17,7 @@ mod commands; mod docs; mod github_action; mod ignore; +mod stdin; #[cfg(all( not(target_os = "windows"), @@ -59,43 +62,89 @@ fn main() { match cli.command { Commands::Lint(LintArgs { paths, format }) => { let mut linter = linter(config, format); - let result = linter.lint_paths(paths, false, &ignorer); - let count: usize = result.paths.iter().map(|path| path.files.len()).sum(); - - // TODO this should be cleaned up better - if matches!(format, Format::GithubAnnotationNative) { - for path in result.paths { - for file in path.files { - for violation in file.violations { - let line = format!( - "::error title=sqruff,file={},line={},col={}::{}: {}", - file.path, - violation.line_no, - violation.line_pos, - violation.rule.as_ref().unwrap().code, - violation.description - ); - eprintln!("{line}"); + + if is_std_in_flag_input(&paths) { + // Read SQL input from stdin + let mut sql_input = String::new(); + std::io::stdin().read_to_string(&mut sql_input).unwrap(); + // Lint the input SQL string + let result = linter.lint_string_wrapped(&sql_input, None, false); + + if matches!(format, Format::GithubAnnotationNative) { + // Handle GithubAnnotationNative format + for path in result.paths { + for file in path.files { + for violation in file.violations { + let line = format!( + "::error title=sqruff,file={},line={},col={}::{}: {}", + file.path, + violation.line_no, + violation.line_pos, + violation.rule.as_ref().unwrap().code, + violation.description + ); + eprintln!("{line}"); + } } } + } else { + // Use formatter to output results + linter.formatter_mut().unwrap().dispatch_linting_result(&result); } - } - eprintln!("The linter processed {count} file(s)."); - linter.formatter_mut().unwrap().completion_message(); + linter.formatter_mut().unwrap().completion_message(); - std::process::exit( - if linter - .formatter() - .unwrap() - .has_fail - .load(std::sync::atomic::Ordering::SeqCst) - { - 1 - } else { - 0 - }, - ) + std::process::exit( + if linter + .formatter() + .unwrap() + .has_fail + .load(std::sync::atomic::Ordering::SeqCst) + { + 1 + } else { + 0 + }, + ); + } else { + let result = linter.lint_paths(paths, false, &ignorer); + let count: usize = result.paths.iter().map(|path| path.files.len()).sum(); + + // TODO this should be cleaned up better + if matches!(format, Format::GithubAnnotationNative) { + for path in result.paths { + for file in path.files { + for violation in file.violations { + let line = format!( + "::error title=sqruff,file={},line={},col={}::{}: {}", + file.path, + violation.line_no, + violation.line_pos, + violation.rule.as_ref().unwrap().code, + violation.description + ); + eprintln!("{line}"); + } + } + } + } + + eprintln!("The linter processed {count} file(s)."); + linter.formatter_mut().unwrap().completion_message(); + + std::process::exit( + if linter + .formatter() + .unwrap() + .has_fail + .load(std::sync::atomic::Ordering::SeqCst) + { + 1 + } else { + 0 + }, + ) + } } Commands::Fix(FixArgs { paths, @@ -103,46 +152,80 @@ fn main() { format, }) => { let mut linter = linter(config, format); - let result = linter.lint_paths(paths, true, &ignorer); - - if result - .paths - .iter() - .map(|path| path.files.iter().all(|file| file.violations.is_empty())) - .all(|v| v) - { - let count_files = result + + if is_std_in_flag_input(&paths) { + // Read SQL input from stdin + let mut sql_input = String::new(); + std::io::stdin().read_to_string(&mut sql_input).unwrap(); + // Fix the input SQL string + let result = linter.lint_string_wrapped(&sql_input, None, true); + + if !result .paths .iter() - .map(|path| path.files.len()) - .sum::(); - println!("{} files processed, nothing to fix.", count_files); - return; - } - - if !force { - match check_user_input() { - Some(true) => { - eprintln!("Attempting fixes..."); + .flat_map(|path| &path.files) + .all(|file| file.violations.is_empty()) + { + if !force { + match check_user_input() { + Some(true) => { + eprintln!("Attempting fixes..."); + } + Some(false) => return, + None => { + eprintln!("Invalid input, please enter 'Y' or 'N'"); + eprintln!("Aborting..."); + return; + } + } } - Some(false) => return, - None => { - eprintln!("Invalid input, please enter 'Y' or 'N'"); - eprintln!("Aborting..."); - return; + } + + // Output the fixed SQL + let fixed_sql = result.paths[0].files[0].fix_string(); + println!("{}", fixed_sql); + } else { + let result = linter.lint_paths(paths, true, &ignorer); + + if result + .paths + .iter() + .map(|path| path.files.iter().all(|file| file.violations.is_empty())) + .all(|v| v) + { + let count_files = result + .paths + .iter() + .map(|path| path.files.len()) + .sum::(); + println!("{} files processed, nothing to fix.", count_files); + return; + } + + if !force { + match check_user_input() { + Some(true) => { + eprintln!("Attempting fixes..."); + } + Some(false) => return, + None => { + eprintln!("Invalid input, please enter 'Y' or 'N'"); + eprintln!("Aborting..."); + return; + } } } - } - for linted_dir in result.paths { - for mut file in linted_dir.files { - let path = std::mem::take(&mut file.path); - let write_buff = file.fix_string(); - std::fs::write(path, write_buff).unwrap(); + for linted_dir in result.paths { + for mut file in linted_dir.files { + let path = std::mem::take(&mut file.path); + let write_buff = file.fix_string(); + std::fs::write(path, write_buff).unwrap(); + } } - } - linter.formatter_mut().unwrap().completion_message(); + linter.formatter_mut().unwrap().completion_message(); + } } Commands::Lsp => sqruff_lsp::run(), } diff --git a/crates/cli/src/stdin.rs b/crates/cli/src/stdin.rs new file mode 100644 index 000000000..f4852aa5b --- /dev/null +++ b/crates/cli/src/stdin.rs @@ -0,0 +1,3 @@ +pub fn is_std_in_flag_input(inputs: &Vec) -> bool { + inputs == ["-"] + } \ No newline at end of file