Implement Display on formatter structs (#6983)

Feedback from
https://github.com/astral-sh/ruff/pull/6948#discussion_r1308260021.
This commit is contained in:
Charlie Marsh 2023-08-29 12:57:26 -04:00 committed by GitHub
parent fad23bbe60
commit 8d1610d960
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 47 additions and 40 deletions

View file

@ -333,7 +333,7 @@ pub struct FormatCommand {
/// files would have been modified, and zero otherwise. /// files would have been modified, and zero otherwise.
#[arg(long)] #[arg(long)]
pub check: bool, pub check: bool,
/// Specify file to write the linter output to (default: stdout). /// Specify file to write the formatter output to (default: stdout).
#[arg(short, long)] #[arg(short, long)]
pub output_file: Option<PathBuf>, pub output_file: Option<PathBuf>,
/// The minimum Python version that should be supported. /// The minimum Python version that should be supported.

View file

@ -1,3 +1,4 @@
use std::fmt::{Display, Formatter};
use std::fs::File; use std::fs::File;
use std::io; use std::io;
use std::io::{BufWriter, Write}; use std::io::{BufWriter, Write};
@ -81,13 +82,13 @@ pub(crate) fn format(
let duration = start.elapsed(); let duration = start.elapsed();
debug!("Formatted files in: {:?}", duration); debug!("Formatted files in: {:?}", duration);
let summary = FormatResultSummary::from(results); let summary = FormatResultSummary::new(results, mode);
// Report on any errors. // Report on any errors.
if !errors.is_empty() { if !errors.is_empty() {
warn!("Encountered {} errors while formatting:", errors.len()); warn!("Encountered {} errors while formatting:", errors.len());
for error in &errors { for error in &errors {
error.show_user(); warn!("{error}");
} }
} }
@ -101,7 +102,7 @@ pub(crate) fn format(
} }
_ => Box::new(BufWriter::new(io::stdout())), _ => Box::new(BufWriter::new(io::stdout())),
}; };
summary.show_user(&mut writer, mode)?; writeln!(writer, "{summary}")?;
} }
match mode { match mode {
@ -162,8 +163,10 @@ enum FormatResult {
Skipped, Skipped,
} }
#[derive(Debug, Default)] #[derive(Debug)]
struct FormatResultSummary { struct FormatResultSummary {
/// The format mode that was used.
mode: FormatMode,
/// The number of files that were formatted. /// The number of files that were formatted.
formatted: usize, formatted: usize,
/// The number of files that were unchanged. /// The number of files that were unchanged.
@ -172,30 +175,34 @@ struct FormatResultSummary {
skipped: usize, skipped: usize,
} }
impl From<Vec<FormatResult>> for FormatResultSummary { impl FormatResultSummary {
fn from(diagnostics: Vec<FormatResult>) -> Self { fn new(diagnostics: Vec<FormatResult>, mode: FormatMode) -> Self {
let mut path_diagnostics = Self::default(); let mut summary = Self {
mode,
formatted: 0,
unchanged: 0,
skipped: 0,
};
for diagnostic in diagnostics { for diagnostic in diagnostics {
match diagnostic { match diagnostic {
FormatResult::Formatted => path_diagnostics.formatted += 1, FormatResult::Formatted => summary.formatted += 1,
FormatResult::Unchanged => path_diagnostics.unchanged += 1, FormatResult::Unchanged => summary.unchanged += 1,
FormatResult::Skipped => path_diagnostics.skipped += 1, FormatResult::Skipped => summary.skipped += 1,
} }
} }
path_diagnostics summary
} }
} }
impl FormatResultSummary { impl Display for FormatResultSummary {
/// Pretty-print a [`FormatResultSummary`] for user-facing display. fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
fn show_user(&self, writer: &mut dyn Write, mode: FormatMode) -> Result<(), io::Error> {
if self.formatted > 0 && self.unchanged > 0 { if self.formatted > 0 && self.unchanged > 0 {
writeln!( write!(
writer, f,
"{} file{} {}, {} file{} left unchanged", "{} file{} {}, {} file{} left unchanged",
self.formatted, self.formatted,
if self.formatted == 1 { "" } else { "s" }, if self.formatted == 1 { "" } else { "s" },
match mode { match self.mode {
FormatMode::Write => "reformatted", FormatMode::Write => "reformatted",
FormatMode::Check => "would be reformatted", FormatMode::Check => "would be reformatted",
}, },
@ -203,19 +210,19 @@ impl FormatResultSummary {
if self.unchanged == 1 { "" } else { "s" }, if self.unchanged == 1 { "" } else { "s" },
) )
} else if self.formatted > 0 { } else if self.formatted > 0 {
writeln!( write!(
writer, f,
"{} file{} {}", "{} file{} {}",
self.formatted, self.formatted,
if self.formatted == 1 { "" } else { "s" }, if self.formatted == 1 { "" } else { "s" },
match mode { match self.mode {
FormatMode::Write => "reformatted", FormatMode::Write => "reformatted",
FormatMode::Check => "would be reformatted", FormatMode::Check => "would be reformatted",
} }
) )
} else if self.unchanged > 0 { } else if self.unchanged > 0 {
writeln!( write!(
writer, f,
"{} file{} left unchanged", "{} file{} left unchanged",
self.unchanged, self.unchanged,
if self.unchanged == 1 { "" } else { "s" }, if self.unchanged == 1 { "" } else { "s" },
@ -229,62 +236,62 @@ impl FormatResultSummary {
/// An error that can occur while formatting a set of files. /// An error that can occur while formatting a set of files.
#[derive(Error, Debug)] #[derive(Error, Debug)]
enum FormatterIterationError { enum FormatterIterationError {
#[error("Failed to traverse: {0}")]
Ignore(#[from] ignore::Error), Ignore(#[from] ignore::Error),
#[error("Failed to read {0}: {1}")]
Read(PathBuf, io::Error), Read(PathBuf, io::Error),
#[error("Failed to write {0}: {1}")]
Write(PathBuf, io::Error), Write(PathBuf, io::Error),
#[error("Failed to format {0}: {1}")]
FormatModule(PathBuf, FormatModuleError), FormatModule(PathBuf, FormatModuleError),
} }
impl FormatterIterationError { impl Display for FormatterIterationError {
/// Pretty-print a [`FormatterIterationError`] for user-facing display. fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
fn show_user(&self) {
match self { match self {
Self::Ignore(err) => { Self::Ignore(err) => {
if let ignore::Error::WithPath { path, .. } = err { if let ignore::Error::WithPath { path, .. } = err {
warn!( write!(
f,
"{}{}{} {}", "{}{}{} {}",
"Failed to format ".bold(), "Failed to format ".bold(),
fs::relativize_path(path).bold(), fs::relativize_path(path).bold(),
":".bold(), ":".bold(),
err.io_error() err.io_error()
.map_or_else(|| err.to_string(), std::string::ToString::to_string) .map_or_else(|| err.to_string(), std::string::ToString::to_string)
); )
} else { } else {
warn!( write!(
f,
"{} {}", "{} {}",
"Encountered error:".bold(), "Encountered error:".bold(),
err.io_error() err.io_error()
.map_or_else(|| err.to_string(), std::string::ToString::to_string) .map_or_else(|| err.to_string(), std::string::ToString::to_string)
); )
} }
} }
Self::Read(path, err) => { Self::Read(path, err) => {
warn!( write!(
f,
"{}{}{} {err}", "{}{}{} {err}",
"Failed to read ".bold(), "Failed to read ".bold(),
fs::relativize_path(path).bold(), fs::relativize_path(path).bold(),
":".bold() ":".bold()
); )
} }
Self::Write(path, err) => { Self::Write(path, err) => {
warn!( write!(
f,
"{}{}{} {err}", "{}{}{} {err}",
"Failed to write ".bold(), "Failed to write ".bold(),
fs::relativize_path(path).bold(), fs::relativize_path(path).bold(),
":".bold() ":".bold()
); )
} }
Self::FormatModule(path, err) => { Self::FormatModule(path, err) => {
warn!( write!(
f,
"{}{}{} {err}", "{}{}{} {err}",
"Failed to format ".bold(), "Failed to format ".bold(),
fs::relativize_path(path).bold(), fs::relativize_path(path).bold(),
":".bold() ":".bold()
); )
} }
} }
} }