Skip to content

Commit 300d0b0

Browse files
authored
Merge pull request #94 from Enselic/handle-broken-pipe
Gracefully handle `ErrorKind::BrokenPipe` (`public-api ... | head -n 1`)
2 parents 8884216 + df44eb8 commit 300d0b0

File tree

2 files changed

+45
-3
lines changed

2 files changed

+45
-3
lines changed

src/main.rs

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,18 @@
1-
use std::io::{stdout, Write};
1+
use std::io::{stdout, ErrorKind, Write};
22
use std::path::{Path, PathBuf};
33

44
use public_api::diff::PublicItemsDiff;
55
use public_api::{public_api_from_rustdoc_json_str, Options, MINIMUM_RUSTDOC_JSON_VERSION};
66

7-
type Result<T> = std::result::Result<T, Box<dyn std::error::Error>>;
7+
#[derive(thiserror::Error, Debug)]
8+
enum Error {
9+
#[error(transparent)]
10+
PublicApiError(#[from] public_api::Error),
11+
#[error(transparent)]
12+
StdIoError(#[from] std::io::Error),
13+
}
14+
15+
type Result<T> = std::result::Result<T, Error>;
816

917
#[derive(Default)]
1018
struct Args {
@@ -13,7 +21,7 @@ struct Args {
1321
files: Vec<PathBuf>,
1422
}
1523

16-
fn main() -> Result<()> {
24+
fn main_() -> Result<()> {
1725
let args = args();
1826

1927
let mut options = Options::default();
@@ -155,3 +163,11 @@ fn args() -> Args {
155163

156164
args
157165
}
166+
167+
/// Wrapper to handle <https://github.com/rust-lang/rust/issues/46016>
168+
fn main() -> Result<()> {
169+
match main_() {
170+
Err(Error::StdIoError(e)) if e.kind() == ErrorKind::BrokenPipe => std::process::exit(141),
171+
result => result,
172+
}
173+
}

tests/bin_tests.rs

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
use std::{io::BufRead, str::from_utf8};
2+
13
use assert_cmd::Command;
24
use public_api::MINIMUM_RUSTDOC_JSON_VERSION;
35

@@ -120,6 +122,30 @@ Added:
120122
);
121123
}
122124

125+
/// Uses a bash one-liner to test that public-api gracefully handles
126+
/// `std::io::ErrorKind::BrokenPipe`
127+
#[test]
128+
#[serial]
129+
fn broken_pipe() {
130+
// Use the JSON for a somewhat large API so the pipe has time to become closed
131+
// before all output has been written to stdout
132+
let large_api = rustdoc_json_path_for_crate("./tests/crates/comprehensive_api");
133+
134+
// Now setup the actual one-liner
135+
let mut cmd = std::process::Command::new("bash");
136+
cmd.args([
137+
"-c",
138+
&format!(
139+
"./target/debug/public-api {} | head -n 1",
140+
large_api.to_string_lossy(),
141+
),
142+
]);
143+
144+
// Run it and assert on that there was no error printed
145+
assert_eq!(cmd.output().unwrap().stdout.lines().count(), 1);
146+
assert_eq!(from_utf8(&cmd.output().unwrap().stderr), Ok(""));
147+
}
148+
123149
#[test]
124150
fn short_help() {
125151
let mut cmd = Command::cargo_bin("public-api").unwrap();

0 commit comments

Comments
 (0)