From df44eb8430de034146d04cd25c25f9172adc632b Mon Sep 17 00:00:00 2001 From: Martin Nordholts Date: Sat, 14 May 2022 08:35:39 +0200 Subject: [PATCH] Gracefully handle `ErrorKind::BrokenPipe` (`public-api ... | head -n 1`) So that we don't get an error when doing something like this: % RUSTDOCFLAGS='-Z unstable-options --output-format json' cargo +nightly doc --manifest-path tests/crates/comprehensive_api/Cargo.toml --lib --no-deps % ./target/debug/public-api tests/crates/comprehensive_api/target/doc/comprehensive_api.json | head -n 1 pub async fn comprehensive_api::functions::async_fn() -> impl Future Error: Os { code: 32, kind: BrokenPipe, message: "Broken pipe" } --- src/main.rs | 22 +++++++++++++++++++--- tests/bin_tests.rs | 26 ++++++++++++++++++++++++++ 2 files changed, 45 insertions(+), 3 deletions(-) diff --git a/src/main.rs b/src/main.rs index bb474705..da3af8e5 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,10 +1,18 @@ -use std::io::{stdout, Write}; +use std::io::{stdout, ErrorKind, Write}; use std::path::{Path, PathBuf}; use public_api::diff::PublicItemsDiff; use public_api::{public_api_from_rustdoc_json_str, Options, MINIMUM_RUSTDOC_JSON_VERSION}; -type Result = std::result::Result>; +#[derive(thiserror::Error, Debug)] +enum Error { + #[error(transparent)] + PublicApiError(#[from] public_api::Error), + #[error(transparent)] + StdIoError(#[from] std::io::Error), +} + +type Result = std::result::Result; #[derive(Default)] struct Args { @@ -13,7 +21,7 @@ struct Args { files: Vec, } -fn main() -> Result<()> { +fn main_() -> Result<()> { let args = args(); let mut options = Options::default(); @@ -155,3 +163,11 @@ fn args() -> Args { args } + +/// Wrapper to handle +fn main() -> Result<()> { + match main_() { + Err(Error::StdIoError(e)) if e.kind() == ErrorKind::BrokenPipe => std::process::exit(141), + result => result, + } +} diff --git a/tests/bin_tests.rs b/tests/bin_tests.rs index b3f1f975..bc613877 100644 --- a/tests/bin_tests.rs +++ b/tests/bin_tests.rs @@ -1,3 +1,5 @@ +use std::{io::BufRead, str::from_utf8}; + use assert_cmd::Command; use public_api::MINIMUM_RUSTDOC_JSON_VERSION; @@ -120,6 +122,30 @@ Added: ); } +/// Uses a bash one-liner to test that public-api gracefully handles +/// `std::io::ErrorKind::BrokenPipe` +#[test] +#[serial] +fn broken_pipe() { + // Use the JSON for a somewhat large API so the pipe has time to become closed + // before all output has been written to stdout + let large_api = rustdoc_json_path_for_crate("./tests/crates/comprehensive_api"); + + // Now setup the actual one-liner + let mut cmd = std::process::Command::new("bash"); + cmd.args([ + "-c", + &format!( + "./target/debug/public-api {} | head -n 1", + large_api.to_string_lossy(), + ), + ]); + + // Run it and assert on that there was no error printed + assert_eq!(cmd.output().unwrap().stdout.lines().count(), 1); + assert_eq!(from_utf8(&cmd.output().unwrap().stderr), Ok("")); +} + #[test] fn short_help() { let mut cmd = Command::cargo_bin("public-api").unwrap();