Add `OutputHandle` enum to use instead of `&mut dyn io::Write`

This commit is contained in:
Peter Hebden 2023-07-09 01:08:14 +01:00 committed by David Peter
parent d295b3a394
commit 103a2f0d9b
4 changed files with 107 additions and 52 deletions

View File

@ -12,7 +12,7 @@ use crate::line_range::{LineRanges, RangeCheckResult};
use crate::output::OutputType; use crate::output::OutputType;
#[cfg(feature = "paging")] #[cfg(feature = "paging")]
use crate::paging::PagingMode; use crate::paging::PagingMode;
use crate::printer::{InteractivePrinter, Printer, SimplePrinter}; use crate::printer::{InteractivePrinter, OutputHandle, Printer, SimplePrinter};
use clircle::{Clircle, Identifier}; use clircle::{Clircle, Identifier};
@ -26,13 +26,18 @@ impl<'b> Controller<'b> {
Controller { config, assets } Controller { config, assets }
} }
pub fn run(&self, inputs: Vec<Input>) -> Result<bool> { pub fn run(
self.run_with_error_handler(inputs, default_error_handler) &self,
inputs: Vec<Input>,
output_buffer: Option<&mut dyn std::fmt::Write>,
) -> Result<bool> {
self.run_with_error_handler(inputs, output_buffer, default_error_handler)
} }
pub fn run_with_error_handler( pub fn run_with_error_handler(
&self, &self,
inputs: Vec<Input>, inputs: Vec<Input>,
output_buffer: Option<&mut dyn std::fmt::Write>,
handle_error: impl Fn(&Error, &mut dyn Write), handle_error: impl Fn(&Error, &mut dyn Write),
) -> Result<bool> { ) -> Result<bool> {
let mut output_type; let mut output_type;
@ -74,7 +79,11 @@ impl<'b> Controller<'b> {
clircle::Identifier::stdout() clircle::Identifier::stdout()
}; };
let writer = output_type.handle()?; //let writer = output_type.handle()?;
let writer = output_buffer.map_or_else(
|| OutputHandle::IoWrite(output_type.handle().unwrap()),
|buf| OutputHandle::FmtWrite(buf),
);
let mut no_errors: bool = true; let mut no_errors: bool = true;
let stderr = io::stderr(); let stderr = io::stderr();
@ -88,11 +97,16 @@ impl<'b> Controller<'b> {
self.print_input(input, writer, io::empty(), identifier, is_first) self.print_input(input, writer, io::empty(), identifier, is_first)
}; };
if let Err(error) = result { if let Err(error) = result {
match writer {
OutputHandle::FmtWrite(writer) => todo!(),
OutputHandle::IoWrite(writer) => {
if attached_to_pager { if attached_to_pager {
handle_error(&error, writer); handle_error(&error, writer);
} else { } else {
handle_error(&error, &mut stderr.lock()); handle_error(&error, &mut stderr.lock());
} }
}
}
no_errors = false; no_errors = false;
} }
} }
@ -103,7 +117,7 @@ impl<'b> Controller<'b> {
fn print_input<R: BufRead>( fn print_input<R: BufRead>(
&self, &self,
input: Input, input: Input,
writer: &mut dyn Write, writer: OutputHandle,
stdin: R, stdin: R,
stdout_identifier: Option<&Identifier>, stdout_identifier: Option<&Identifier>,
is_first: bool, is_first: bool,
@ -164,7 +178,7 @@ impl<'b> Controller<'b> {
fn print_file( fn print_file(
&self, &self,
printer: &mut dyn Printer, printer: &mut dyn Printer,
writer: &mut dyn Write, writer: OutputHandle,
input: &mut OpenedInput, input: &mut OpenedInput,
add_header_padding: bool, add_header_padding: bool,
#[cfg(feature = "git")] line_changes: &Option<LineChanges>, #[cfg(feature = "git")] line_changes: &Option<LineChanges>,
@ -202,7 +216,7 @@ impl<'b> Controller<'b> {
fn print_file_ranges( fn print_file_ranges(
&self, &self,
printer: &mut dyn Printer, printer: &mut dyn Printer,
writer: &mut dyn Write, writer: OutputHandle,
reader: &mut InputReader, reader: &mut InputReader,
line_ranges: &LineRanges, line_ranges: &LineRanges,
) -> Result<()> { ) -> Result<()> {

View File

@ -7,6 +7,8 @@ pub enum Error {
#[error(transparent)] #[error(transparent)]
Io(#[from] ::std::io::Error), Io(#[from] ::std::io::Error),
#[error(transparent)] #[error(transparent)]
Fmt(#[from] ::std::fmt::Error),
#[error(transparent)]
SyntectError(#[from] ::syntect::Error), SyntectError(#[from] ::syntect::Error),
#[error(transparent)] #[error(transparent)]
SyntectLoadingError(#[from] ::syntect::LoadingError), SyntectLoadingError(#[from] ::syntect::LoadingError),

View File

@ -300,7 +300,7 @@ impl<'a> PrettyPrinter<'a> {
// Run the controller // Run the controller
let controller = Controller::new(&self.config, &self.assets); let controller = Controller::new(&self.config, &self.assets);
controller.run(inputs.into_iter().map(|i| i.into()).collect()) controller.run(inputs.into_iter().map(|i| i.into()).collect(), None)
} }
} }

View File

@ -1,4 +1,5 @@
use std::io::Write; use std::fmt;
use std::io;
use std::vec::Vec; use std::vec::Vec;
use nu_ansi_term::Color::{Fixed, Green, Red, Yellow}; use nu_ansi_term::Color::{Fixed, Green, Red, Yellow};
@ -36,21 +37,52 @@ use crate::terminal::{as_terminal_escaped, to_ansi_color};
use crate::vscreen::AnsiStyle; use crate::vscreen::AnsiStyle;
use crate::wrapping::WrappingMode; use crate::wrapping::WrappingMode;
pub enum OutputHandle<'a> {
IoWrite(&'a mut dyn io::Write),
FmtWrite(&'a mut dyn fmt::Write),
}
macro_rules! write_handle {
($dst:expr, $($arg:tt)*) => {
match $dst {
OutputHandle::IoWrite(ref mut handle) => {
write!(handle, $($arg)*).map_err(|e| Error::from(e))
}
OutputHandle::FmtWrite(ref mut handle) => {
write!(handle, $($arg)*).map_err(|e| Error::from(e))
}
}
}
}
macro_rules! writeln_handle {
($dst:expr, $($arg:tt)*) => {
match $dst {
OutputHandle::IoWrite(ref mut handle) => {
writeln!(handle, $($arg)*).map_err(|e| Error::from(e))
}
OutputHandle::FmtWrite(ref mut handle) => {
writeln!(handle, $($arg)*).map_err(|e| Error::from(e))
}
}
}
}
pub(crate) trait Printer { pub(crate) trait Printer {
fn print_header( fn print_header(
&mut self, &mut self,
handle: &mut dyn Write, handle: OutputHandle,
input: &OpenedInput, input: &OpenedInput,
add_header_padding: bool, add_header_padding: bool,
) -> Result<()>; ) -> Result<()>;
fn print_footer(&mut self, handle: &mut dyn Write, input: &OpenedInput) -> Result<()>; fn print_footer(&mut self, handle: OutputHandle, input: &OpenedInput) -> Result<()>;
fn print_snip(&mut self, handle: &mut dyn Write) -> Result<()>; fn print_snip(&mut self, handle: OutputHandle) -> Result<()>;
fn print_line( fn print_line(
&mut self, &mut self,
out_of_range: bool, out_of_range: bool,
handle: &mut dyn Write, handle: OutputHandle,
line_number: usize, line_number: usize,
line_buffer: &[u8], line_buffer: &[u8],
) -> Result<()>; ) -> Result<()>;
@ -69,25 +101,25 @@ impl<'a> SimplePrinter<'a> {
impl<'a> Printer for SimplePrinter<'a> { impl<'a> Printer for SimplePrinter<'a> {
fn print_header( fn print_header(
&mut self, &mut self,
_handle: &mut dyn Write, _handle: OutputHandle,
_input: &OpenedInput, _input: &OpenedInput,
_add_header_padding: bool, _add_header_padding: bool,
) -> Result<()> { ) -> Result<()> {
Ok(()) Ok(())
} }
fn print_footer(&mut self, _handle: &mut dyn Write, _input: &OpenedInput) -> Result<()> { fn print_footer(&mut self, _handle: OutputHandle, _input: &OpenedInput) -> Result<()> {
Ok(()) Ok(())
} }
fn print_snip(&mut self, _handle: &mut dyn Write) -> Result<()> { fn print_snip(&mut self, _handle: OutputHandle) -> Result<()> {
Ok(()) Ok(())
} }
fn print_line( fn print_line(
&mut self, &mut self,
out_of_range: bool, out_of_range: bool,
handle: &mut dyn Write, handle: OutputHandle,
_line_number: usize, _line_number: usize,
line_buffer: &[u8], line_buffer: &[u8],
) -> Result<()> { ) -> Result<()> {
@ -98,9 +130,12 @@ impl<'a> Printer for SimplePrinter<'a> {
self.config.tab_width, self.config.tab_width,
self.config.nonprintable_notation, self.config.nonprintable_notation,
); );
write!(handle, "{}", line)?; write_handle!(handle, "{}", line)?;
} else { } else {
handle.write_all(line_buffer)? match handle {
OutputHandle::IoWrite(handle) => handle.write_all(line_buffer)?,
OutputHandle::FmtWrite(handle) => todo!(),
}
}; };
} }
Ok(()) Ok(())
@ -218,8 +253,8 @@ impl<'a> InteractivePrinter<'a> {
}) })
} }
fn print_horizontal_line_term(&mut self, handle: &mut dyn Write, style: Style) -> Result<()> { fn print_horizontal_line_term(&mut self, handle: OutputHandle, style: Style) -> Result<()> {
writeln!( writeln_handle!(
handle, handle,
"{}", "{}",
style.paint("".repeat(self.config.term_width)) style.paint("".repeat(self.config.term_width))
@ -227,13 +262,13 @@ impl<'a> InteractivePrinter<'a> {
Ok(()) Ok(())
} }
fn print_horizontal_line(&mut self, handle: &mut dyn Write, grid_char: char) -> Result<()> { fn print_horizontal_line(&mut self, handle: OutputHandle, grid_char: char) -> Result<()> {
if self.panel_width == 0 { if self.panel_width == 0 {
self.print_horizontal_line_term(handle, self.colors.grid)?; self.print_horizontal_line_term(handle, self.colors.grid)?;
} else { } else {
let hline = "".repeat(self.config.term_width - (self.panel_width + 1)); let hline = "".repeat(self.config.term_width - (self.panel_width + 1));
let hline = format!("{}{}{}", "".repeat(self.panel_width), grid_char, hline); let hline = format!("{}{}{}", "".repeat(self.panel_width), grid_char, hline);
writeln!(handle, "{}", self.colors.grid.paint(hline))?; writeln_handle!(handle, "{}", self.colors.grid.paint(hline))?;
} }
Ok(()) Ok(())
@ -257,9 +292,9 @@ impl<'a> InteractivePrinter<'a> {
} }
} }
fn print_header_component_indent(&mut self, handle: &mut dyn Write) -> std::io::Result<()> { fn print_header_component_indent(&mut self, handle: OutputHandle) -> Result<()> {
if self.config.style_components.grid() { if self.config.style_components.grid() {
write!( write_handle!(
handle, handle,
"{}{}", "{}{}",
" ".repeat(self.panel_width), " ".repeat(self.panel_width),
@ -268,7 +303,7 @@ impl<'a> InteractivePrinter<'a> {
.paint(if self.panel_width > 0 { "" } else { "" }), .paint(if self.panel_width > 0 { "" } else { "" }),
) )
} else { } else {
write!(handle, "{}", " ".repeat(self.panel_width)) write_handle!(handle, "{}", " ".repeat(self.panel_width))
} }
} }
@ -285,7 +320,7 @@ impl<'a> InteractivePrinter<'a> {
impl<'a> Printer for InteractivePrinter<'a> { impl<'a> Printer for InteractivePrinter<'a> {
fn print_header( fn print_header(
&mut self, &mut self,
handle: &mut dyn Write, handle: OutputHandle,
input: &OpenedInput, input: &OpenedInput,
add_header_padding: bool, add_header_padding: bool,
) -> Result<()> { ) -> Result<()> {
@ -295,7 +330,7 @@ impl<'a> Printer for InteractivePrinter<'a> {
if !self.config.style_components.header() { if !self.config.style_components.header() {
if Some(ContentType::BINARY) == self.content_type && !self.config.show_nonprintable { if Some(ContentType::BINARY) == self.content_type && !self.config.show_nonprintable {
writeln!( writeln_handle!(
handle, handle,
"{}: Binary content from {} will not be printed to the terminal \ "{}: Binary content from {} will not be printed to the terminal \
(but will be present if the output of 'bat' is piped). You can use 'bat -A' \ (but will be present if the output of 'bat' is piped). You can use 'bat -A' \
@ -343,7 +378,7 @@ impl<'a> Printer for InteractivePrinter<'a> {
} else { } else {
// Only pad space between files, if we haven't already drawn a horizontal rule // Only pad space between files, if we haven't already drawn a horizontal rule
if add_header_padding && !self.config.style_components.rule() { if add_header_padding && !self.config.style_components.rule() {
writeln!(handle)?; writeln_handle!(handle,)?;
} }
} }
@ -351,7 +386,7 @@ impl<'a> Printer for InteractivePrinter<'a> {
self.print_header_component_indent(handle)?; self.print_header_component_indent(handle)?;
match component { match component {
StyleComponent::HeaderFilename => writeln!( StyleComponent::HeaderFilename => writeln_handle!(
handle, handle,
"{}{}{}", "{}{}{}",
description description
@ -367,7 +402,7 @@ impl<'a> Printer for InteractivePrinter<'a> {
.size .size
.map(|s| format!("{}", ByteSize(s))) .map(|s| format!("{}", ByteSize(s)))
.unwrap_or_else(|| "-".into()); .unwrap_or_else(|| "-".into());
writeln!(handle, "Size: {}", self.colors.header_value.paint(bsize)) writeln_handle!(handle, "Size: {}", self.colors.header_value.paint(bsize))
} }
_ => Ok(()), _ => Ok(()),
} }
@ -384,7 +419,7 @@ impl<'a> Printer for InteractivePrinter<'a> {
Ok(()) Ok(())
} }
fn print_footer(&mut self, handle: &mut dyn Write, _input: &OpenedInput) -> Result<()> { fn print_footer(&mut self, handle: OutputHandle, _input: &OpenedInput) -> Result<()> {
if self.config.style_components.grid() if self.config.style_components.grid()
&& (self.content_type.map_or(false, |c| c.is_text()) || self.config.show_nonprintable) && (self.content_type.map_or(false, |c| c.is_text()) || self.config.show_nonprintable)
{ {
@ -394,7 +429,7 @@ impl<'a> Printer for InteractivePrinter<'a> {
} }
} }
fn print_snip(&mut self, handle: &mut dyn Write) -> Result<()> { fn print_snip(&mut self, handle: OutputHandle) -> Result<()> {
let panel = self.create_fake_panel(" ..."); let panel = self.create_fake_panel(" ...");
let panel_count = panel.chars().count(); let panel_count = panel.chars().count();
@ -407,13 +442,13 @@ impl<'a> Printer for InteractivePrinter<'a> {
let snip_right = let snip_right =
"".repeat((self.config.term_width - panel_count - snip_left_count - title_count) / 2); "".repeat((self.config.term_width - panel_count - snip_left_count - title_count) / 2);
writeln!( writeln_handle!(
handle, handle,
"{}", "{}",
self.colors self.colors
.grid .grid
.paint(format!("{}{}{}{}", panel, snip_left, title, snip_right)) .paint(format!("{}{}{}{}", panel, snip_left, title, snip_right))
)?; );
Ok(()) Ok(())
} }
@ -421,7 +456,7 @@ impl<'a> Printer for InteractivePrinter<'a> {
fn print_line( fn print_line(
&mut self, &mut self,
out_of_range: bool, out_of_range: bool,
handle: &mut dyn Write, handle: OutputHandle,
line_number: usize, line_number: usize,
line_buffer: &[u8], line_buffer: &[u8],
) -> Result<()> { ) -> Result<()> {
@ -508,7 +543,7 @@ impl<'a> Printer for InteractivePrinter<'a> {
.map(|d| d.generate(line_number, false, self)); .map(|d| d.generate(line_number, false, self));
for deco in decorations { for deco in decorations {
write!(handle, "{} ", deco.text)?; write_handle!(handle, "{} ", deco.text)?;
cursor_max -= deco.width + 1; cursor_max -= deco.width + 1;
} }
} }
@ -526,7 +561,7 @@ impl<'a> Printer for InteractivePrinter<'a> {
// ANSI escape passthrough. // ANSI escape passthrough.
(ansi, true) => { (ansi, true) => {
self.ansi_style.update(ansi); self.ansi_style.update(ansi);
write!(handle, "{}", ansi)?; write_handle!(handle, "{}", ansi)?;
} }
// Regular text. // Regular text.
@ -534,7 +569,7 @@ impl<'a> Printer for InteractivePrinter<'a> {
let text = &*self.preprocess(text, &mut cursor_total); let text = &*self.preprocess(text, &mut cursor_total);
let text_trimmed = text.trim_end_matches(|c| c == '\r' || c == '\n'); let text_trimmed = text.trim_end_matches(|c| c == '\r' || c == '\n');
write!( write_handle!(
handle, handle,
"{}", "{}",
as_terminal_escaped( as_terminal_escaped(
@ -559,9 +594,13 @@ impl<'a> Printer for InteractivePrinter<'a> {
} else { } else {
0 0
}; };
write!(handle, "{}", ansi_style.paint(" ".repeat(width)))?; write_handle!(
handle,
"{}",
ansi_style.paint(" ".repeat(width))
)?;
} }
write!(handle, "{}", &text[text_trimmed.len()..])?; write_handle!(handle, "{}", &text[text_trimmed.len()..])?;
} }
} }
} }
@ -569,7 +608,7 @@ impl<'a> Printer for InteractivePrinter<'a> {
} }
if !self.config.style_components.plain() && line.bytes().next_back() != Some(b'\n') { if !self.config.style_components.plain() && line.bytes().next_back() != Some(b'\n') {
writeln!(handle)?; writeln_handle!(handle,)?;
} }
} else { } else {
for &(style, region) in &regions { for &(style, region) in &regions {
@ -579,7 +618,7 @@ impl<'a> Printer for InteractivePrinter<'a> {
// ANSI escape passthrough. // ANSI escape passthrough.
(ansi, true) => { (ansi, true) => {
self.ansi_style.update(ansi); self.ansi_style.update(ansi);
write!(handle, "{}", ansi)?; write_handle!(handle, "{}", ansi)?;
} }
// Regular text. // Regular text.
@ -624,7 +663,7 @@ impl<'a> Printer for InteractivePrinter<'a> {
} }
// It wraps. // It wraps.
write!( write_handle!(
handle, handle,
"{}\n{}", "{}\n{}",
as_terminal_escaped( as_terminal_escaped(
@ -650,7 +689,7 @@ impl<'a> Printer for InteractivePrinter<'a> {
// flush the buffer // flush the buffer
cursor += current_width; cursor += current_width;
write!( write_handle!(
handle, handle,
"{}", "{}",
as_terminal_escaped( as_terminal_escaped(
@ -673,18 +712,18 @@ impl<'a> Printer for InteractivePrinter<'a> {
..Default::default() ..Default::default()
}; };
write!( write_handle!(
handle, handle,
"{}", "{}",
ansi_style.paint(" ".repeat(cursor_max - cursor)) ansi_style.paint(" ".repeat(cursor_max - cursor))
)?; )?;
} }
writeln!(handle)?; writeln_handle!(handle,)?;
} }
if highlight_this_line && self.config.theme == "ansi" { if highlight_this_line && self.config.theme == "ansi" {
self.ansi_style.update("^[24m"); self.ansi_style.update("^[24m");
write!(handle, "\x1B[24m")?; write_handle!(handle, "\x1B[24m")?;
} }
Ok(()) Ok(())