Ignore BrokenPipe error when piping to pager

If the pager terminates before we wrote everything to stdout, we get a
BrokenPipe error.  We just want to ignore this error but the print*!
macros panic.  Therefore we replace the calls to print*! with calls to
write*! and ignore the BrokenPipe error kind when handling the error.
This commit is contained in:
Robin Krahl 2020-07-17 23:37:28 +02:00
parent 02b3116ecd
commit 5d37789837

View File

@ -1,6 +1,8 @@
// SPDX-FileCopyrightText: 2020 Robin Krahl <robin.krahl@ireas.org>
// SPDX-License-Identifier: MIT
use std::io::{self, Write};
use html2text::render::text_renderer;
use crate::doc;
@ -20,35 +22,46 @@ impl RichViewer {
}
}
fn print(&self, s: &str) {
fn print_doc(&self, doc: &doc::Doc) -> io::Result<()> {
self.print_heading(&doc.title, 1)?;
self.print_opt(doc.definition.as_deref())?;
self.print_opt(doc.description.as_deref())?;
Ok(())
}
fn print(&self, s: &str) -> io::Result<()> {
let lines = html2text::from_read_rich(s.as_bytes(), self.line_length);
for line in lines {
for element in line.iter() {
if let text_renderer::TaggedLineElement::Str(ts) = element {
self.render_string(ts);
self.render_string(ts)?;
}
}
println!();
writeln!(io::stdout())?;
}
Ok(())
}
fn print_opt(&self, s: Option<&str>) {
fn print_opt(&self, s: Option<&str>) -> io::Result<()> {
if let Some(s) = s {
println!();
self.print(s);
writeln!(io::stdout())?;
self.print(s)
} else {
Ok(())
}
}
fn print_heading(&self, s: &str, level: usize) {
print!("{}{} ", termion::style::Bold, "#".repeat(level));
self.print(s);
print!("{}", termion::style::Reset);
fn print_heading(&self, s: &str, level: usize) -> io::Result<()> {
let prefix = "#".repeat(level);
write!(io::stdout(), "{}{} ", termion::style::Bold, prefix)?;
self.print(s)?;
write!(io::stdout(), "{}", termion::style::Reset)
}
fn render_string(&self, ts: &RichString) {
fn render_string(&self, ts: &RichString) -> io::Result<()> {
let start_style = get_style(ts, get_start_style);
let end_style = get_style(ts, get_end_style);
print!("{}{}{}", start_style, ts.s, end_style);
write!(io::stdout(), "{}{}{}", start_style, ts.s, end_style)
}
}
@ -56,10 +69,20 @@ impl viewer::Viewer for RichViewer {
fn open(&self, doc: &doc::Doc) -> anyhow::Result<()> {
viewer::spawn_pager();
self.print_heading(&doc.title, 1);
self.print_opt(doc.definition.as_deref());
self.print_opt(doc.description.as_deref());
self.print_doc(doc)
.or_else(ignore_pipe_error)
.map_err(Into::into)
}
}
fn ignore_pipe_error(error: io::Error) -> io::Result<()> {
// If the pager is terminated before we can write everything to stdout, we will receive a
// BrokenPipe error. But we dont want to report this error to the user. See also:
// https://github.com/rust-lang/rust/issues/46016
if error.kind() == io::ErrorKind::BrokenPipe {
Ok(())
} else {
Err(error)
}
}