diff --git a/apps/oxlint/src/lint.rs b/apps/oxlint/src/lint.rs index 2b12f15c959cf..d68385b779ed2 100644 --- a/apps/oxlint/src/lint.rs +++ b/apps/oxlint/src/lint.rs @@ -1,6 +1,6 @@ use std::{ env, fs, - io::{BufWriter, ErrorKind, Write}, + io::{ErrorKind, Write}, path::{Path, PathBuf}, process::ExitCode, time::Instant, @@ -34,16 +34,14 @@ impl Runner for LintRunner { Self { options, cwd: env::current_dir().expect("Failed to get current working directory") } } - fn run(self) -> CliRunResult { + fn run(self, stdout: &mut dyn Write) -> CliRunResult { let format_str = self.options.output_options.format; - let mut output_formatter = OutputFormatter::new(format_str); - - // stdio is blocked by LineWriter, use a BufWriter to reduce syscalls. - // See `https://github.com/rust-lang/rust/issues/60673`. - let mut stdout = BufWriter::new(std::io::stdout()); + let output_formatter = OutputFormatter::new(format_str); if self.options.list_rules { - output_formatter.all_rules(&mut stdout); + if let Some(output) = output_formatter.all_rules() { + stdout.write_all(output.as_bytes()).or_else(Self::check_for_writer_error).unwrap(); + } stdout.flush().unwrap(); return CliRunResult::None; } @@ -206,7 +204,7 @@ impl Runner for LintRunner { } }); - let diagnostic_result = diagnostic_service.run(&mut stdout); + let diagnostic_result = diagnostic_service.run(stdout); let diagnostic_failed = diagnostic_result.max_warnings_exceeded() || diagnostic_result.errors_count() > 0 @@ -334,7 +332,9 @@ mod test { let mut new_args = vec!["--silent"]; new_args.extend(args); let options = lint_command().run_inner(new_args.as_slice()).unwrap(); - match LintRunner::new(options).run() { + let mut output = Vec::new(); + + match LintRunner::new(options).run(&mut output) { CliRunResult::LintResult(lint_result) => lint_result, other => panic!("{other:?}"), } @@ -356,8 +356,9 @@ mod test { }; current_cwd.push(part_cwd); + let mut output = Vec::new(); - match LintRunner::new(options).with_cwd(current_cwd).run() { + match LintRunner::new(options).with_cwd(current_cwd).run(&mut output) { CliRunResult::LintResult(lint_result) => lint_result, other => panic!("{other:?}"), } @@ -367,7 +368,9 @@ mod test { let mut new_args = vec!["--quiet"]; new_args.extend(args); let options = lint_command().run_inner(new_args.as_slice()).unwrap(); - match LintRunner::new(options).run() { + let mut output = Vec::new(); + + match LintRunner::new(options).run(&mut output) { CliRunResult::InvalidOptions { message } => message, other => { panic!("Expected InvalidOptions, got {other:?}"); @@ -752,7 +755,8 @@ mod test { fn test_print_config_ban_all_rules() { let args = &["-A", "all", "--print-config"]; let options = lint_command().run_inner(args).unwrap(); - let ret = LintRunner::new(options).run(); + let mut output = Vec::new(); + let ret = LintRunner::new(options).run(&mut output); let CliRunResult::PrintConfigResult { config_file: config } = ret else { panic!("Expected PrintConfigResult, got {ret:?}") }; @@ -776,7 +780,8 @@ mod test { "--print-config", ]; let options = lint_command().run_inner(args).unwrap(); - let ret = LintRunner::new(options).run(); + let mut output = Vec::new(); + let ret = LintRunner::new(options).run(&mut output); let CliRunResult::PrintConfigResult { config_file: config } = ret else { panic!("Expected PrintConfigResult, got {ret:?}") }; @@ -793,7 +798,8 @@ mod test { fn test_init_config() { let args = &["--init"]; let options = lint_command().run_inner(args).unwrap(); - let ret = LintRunner::new(options).run(); + let mut output = Vec::new(); + let ret = LintRunner::new(options).run(&mut output); let CliRunResult::ConfigFileInitResult { message } = ret else { panic!("Expected configuration file to be created, got {ret:?}") }; diff --git a/apps/oxlint/src/main.rs b/apps/oxlint/src/main.rs index 867f1e1aa247f..356542471d96f 100644 --- a/apps/oxlint/src/main.rs +++ b/apps/oxlint/src/main.rs @@ -8,6 +8,7 @@ static GLOBAL: jemallocator::Jemalloc = jemallocator::Jemalloc; static GLOBAL: mimalloc::MiMalloc = mimalloc::MiMalloc; use oxlint::cli::{CliRunResult, LintRunner, Runner}; +use std::io::BufWriter; fn main() -> CliRunResult { init_tracing(); @@ -15,7 +16,11 @@ fn main() -> CliRunResult { let command = oxlint::cli::lint_command().run(); command.handle_threads(); - LintRunner::new(command).run() + // stdio is blocked by LineWriter, use a BufWriter to reduce syscalls. + // See `https://github.com/rust-lang/rust/issues/60673`. + let mut stdout = BufWriter::new(std::io::stdout()); + + LintRunner::new(command).run(&mut stdout) } // Initialize the data which relies on `is_atty` system calls so they don't block subsequent threads. diff --git a/apps/oxlint/src/output_formatter/checkstyle.rs b/apps/oxlint/src/output_formatter/checkstyle.rs index bdf151e1f5f1e..5c134e7295c43 100644 --- a/apps/oxlint/src/output_formatter/checkstyle.rs +++ b/apps/oxlint/src/output_formatter/checkstyle.rs @@ -1,4 +1,4 @@ -use std::{borrow::Cow, io::Write}; +use std::borrow::Cow; use rustc_hash::FxHashMap; @@ -13,8 +13,8 @@ use crate::output_formatter::InternalFormatter; pub struct CheckStyleOutputFormatter; impl InternalFormatter for CheckStyleOutputFormatter { - fn all_rules(&mut self, writer: &mut dyn Write) { - writeln!(writer, "flag --rules with flag --format=checkstyle is not allowed").unwrap(); + fn all_rules(&self) -> Option { + None } fn get_diagnostic_reporter(&self) -> Box { diff --git a/apps/oxlint/src/output_formatter/default.rs b/apps/oxlint/src/output_formatter/default.rs index 1ef5e692933e9..8c2a8b032823f 100644 --- a/apps/oxlint/src/output_formatter/default.rs +++ b/apps/oxlint/src/output_formatter/default.rs @@ -1,4 +1,4 @@ -use std::{io::Write, time::Duration}; +use std::time::Duration; use oxc_diagnostics::{ reporter::{DiagnosticReporter, DiagnosticResult}, @@ -12,13 +12,16 @@ use crate::output_formatter::InternalFormatter; pub struct DefaultOutputFormatter; impl InternalFormatter for DefaultOutputFormatter { - fn all_rules(&mut self, writer: &mut dyn Write) { + fn all_rules(&self) -> Option { + let mut output = String::new(); let table = RuleTable::new(); for section in table.sections { - writeln!(writer, "{}", section.render_markdown_table(None)).unwrap(); + output.push_str(section.render_markdown_table(None).as_str()); + output.push('\n'); } - writeln!(writer, "Default: {}", table.turned_on_by_default_count).unwrap(); - writeln!(writer, "Total: {}", table.total).unwrap(); + output.push_str(format!("Default: {}\n", table.turned_on_by_default_count).as_str()); + output.push_str(format!("Total: {}\n", table.total).as_str()); + Some(output) } fn lint_command_info(&self, lint_command_info: &super::LintCommandInfo) -> Option { @@ -125,11 +128,10 @@ mod test { #[test] fn all_rules() { - let mut writer = Vec::new(); - let mut formatter = DefaultOutputFormatter; + let formatter = DefaultOutputFormatter; + let result = formatter.all_rules(); - formatter.all_rules(&mut writer); - assert!(!writer.is_empty()); + assert!(result.is_some()); } #[test] diff --git a/apps/oxlint/src/output_formatter/github.rs b/apps/oxlint/src/output_formatter/github.rs index 897bffcb43685..1d5b51e941134 100644 --- a/apps/oxlint/src/output_formatter/github.rs +++ b/apps/oxlint/src/output_formatter/github.rs @@ -1,4 +1,4 @@ -use std::{borrow::Cow, io::Write}; +use std::borrow::Cow; use oxc_diagnostics::{ reporter::{DiagnosticReporter, DiagnosticResult, Info}, @@ -11,8 +11,8 @@ use crate::output_formatter::InternalFormatter; pub struct GithubOutputFormatter; impl InternalFormatter for GithubOutputFormatter { - fn all_rules(&mut self, writer: &mut dyn Write) { - writeln!(writer, "flag --rules with flag --format=github is not allowed").unwrap(); + fn all_rules(&self) -> Option { + None } fn get_diagnostic_reporter(&self) -> Box { diff --git a/apps/oxlint/src/output_formatter/json.rs b/apps/oxlint/src/output_formatter/json.rs index 5da8bf1d203b0..10ac337dabcb0 100644 --- a/apps/oxlint/src/output_formatter/json.rs +++ b/apps/oxlint/src/output_formatter/json.rs @@ -1,9 +1,8 @@ -use std::io::Write; - -use oxc_diagnostics::reporter::DiagnosticResult; -use oxc_diagnostics::{reporter::DiagnosticReporter, Error}; -use oxc_linter::rules::RULES; -use oxc_linter::RuleCategory; +use oxc_diagnostics::{ + reporter::{DiagnosticReporter, DiagnosticResult}, + Error, +}; +use oxc_linter::{rules::RULES, RuleCategory}; use miette::JSONReportHandler; @@ -13,7 +12,7 @@ use crate::output_formatter::InternalFormatter; pub struct JsonOutputFormatter; impl InternalFormatter for JsonOutputFormatter { - fn all_rules(&mut self, writer: &mut dyn Write) { + fn all_rules(&self) -> Option { #[derive(Debug, serde::Serialize)] struct RuleInfoJson<'a> { scope: &'a str, @@ -27,13 +26,10 @@ impl InternalFormatter for JsonOutputFormatter { category: rule.category(), }); - writer - .write_all( - serde_json::to_string_pretty(&rules_info.collect::>()) - .expect("Failed to serialize") - .as_bytes(), - ) - .unwrap(); + Some( + serde_json::to_string_pretty(&rules_info.collect::>()) + .expect("Failed to serialize"), + ) } fn get_diagnostic_reporter(&self) -> Box { diff --git a/apps/oxlint/src/output_formatter/mod.rs b/apps/oxlint/src/output_formatter/mod.rs index deca6a1db4522..1cfd083f38f1e 100644 --- a/apps/oxlint/src/output_formatter/mod.rs +++ b/apps/oxlint/src/output_formatter/mod.rs @@ -5,7 +5,6 @@ mod json; mod stylish; mod unix; -use std::io::{BufWriter, Stdout, Write}; use std::str::FromStr; use std::time::Duration; @@ -63,10 +62,7 @@ pub struct LintCommandInfo { /// The Formatter is then managed by [`OutputFormatter`]. trait InternalFormatter { /// Print all available rules by oxlint - /// Some Formatter do not know how to output the rules in the style, - /// instead you should print out that this combination of flags is not supported. - /// Example: "flag --rules with flag --format=checkstyle is not allowed" - fn all_rules(&mut self, writer: &mut dyn Write); + fn all_rules(&self) -> Option; /// At the end of the Lint command the Formatter can output extra information. fn lint_command_info(&self, _lint_command_info: &LintCommandInfo) -> Option { @@ -100,8 +96,8 @@ impl OutputFormatter { /// Print all available rules by oxlint /// See [`InternalFormatter::all_rules`] for more details. - pub fn all_rules(&mut self, writer: &mut BufWriter) { - self.internal.all_rules(writer); + pub fn all_rules(&self) -> Option { + self.internal.all_rules() } /// At the end of the Lint command we may output extra information. diff --git a/apps/oxlint/src/output_formatter/stylish.rs b/apps/oxlint/src/output_formatter/stylish.rs index c046fae484685..03704f04f786c 100644 --- a/apps/oxlint/src/output_formatter/stylish.rs +++ b/apps/oxlint/src/output_formatter/stylish.rs @@ -1,5 +1,3 @@ -use std::io::Write; - use oxc_diagnostics::{ reporter::{DiagnosticReporter, DiagnosticResult, Info}, Error, Severity, @@ -12,8 +10,8 @@ use crate::output_formatter::InternalFormatter; pub struct StylishOutputFormatter; impl InternalFormatter for StylishOutputFormatter { - fn all_rules(&mut self, writer: &mut dyn Write) { - writeln!(writer, "flag --rules with flag --format=stylish is not allowed").unwrap(); + fn all_rules(&self) -> Option { + None } fn get_diagnostic_reporter(&self) -> Box { diff --git a/apps/oxlint/src/output_formatter/unix.rs b/apps/oxlint/src/output_formatter/unix.rs index e1f0607c6db18..4d0b6ed9b678a 100644 --- a/apps/oxlint/src/output_formatter/unix.rs +++ b/apps/oxlint/src/output_formatter/unix.rs @@ -1,4 +1,4 @@ -use std::{borrow::Cow, io::Write}; +use std::borrow::Cow; use oxc_diagnostics::{ reporter::{DiagnosticReporter, DiagnosticResult, Info}, @@ -11,8 +11,8 @@ use crate::output_formatter::InternalFormatter; pub struct UnixOutputFormatter; impl InternalFormatter for UnixOutputFormatter { - fn all_rules(&mut self, writer: &mut dyn Write) { - writeln!(writer, "flag --rules with flag --format=unix is not allowed").unwrap(); + fn all_rules(&self) -> Option { + None } fn get_diagnostic_reporter(&self) -> Box { diff --git a/apps/oxlint/src/runner.rs b/apps/oxlint/src/runner.rs index 46fdd11b514a3..2c48cd2e02280 100644 --- a/apps/oxlint/src/runner.rs +++ b/apps/oxlint/src/runner.rs @@ -1,3 +1,5 @@ +use std::io::Write; + use crate::cli::CliRunResult; /// A trait for exposing functionality to the CLI. @@ -7,5 +9,5 @@ pub trait Runner { fn new(matches: Self::Options) -> Self; /// Executes the runner, providing some result to the CLI. - fn run(self) -> CliRunResult; + fn run(self, stdout: &mut dyn Write) -> CliRunResult; }