From 5fe3d5a7ab6a6b6b3163bb9f9f9f3ea1355c9983 Mon Sep 17 00:00:00 2001 From: cloudhead Date: Tue, 3 Dec 2024 12:36:57 +0100 Subject: [PATCH] cli: Handle broken pipe in `cob show` When piping the output in for eg. `head -n 1`, the `head` utility might close down the pipe at some point, yielding a broken pipe error. We catch that and simply return if that's the case. --- radicle-cli/src/commands/cob.rs | 86 +++++++++++++++++++++------------ 1 file changed, 56 insertions(+), 30 deletions(-) diff --git a/radicle-cli/src/commands/cob.rs b/radicle-cli/src/commands/cob.rs index 4787f245..8c3f916d 100644 --- a/radicle-cli/src/commands/cob.rs +++ b/radicle-cli/src/commands/cob.rs @@ -1,4 +1,6 @@ use std::ffi::OsString; +use std::io; +use std::io::Write; use std::str::FromStr; use anyhow::anyhow; @@ -10,7 +12,9 @@ use radicle::identity::Identity; use radicle::issue::cache::Issues; use radicle::patch::cache::Patches; use radicle::prelude::RepoId; +use radicle::storage::git; use radicle::storage::ReadStorage; +use radicle::Profile; use radicle_cob::object::collaboration::list; use serde_json::json; @@ -227,37 +231,16 @@ pub fn run(options: Options, ctx: impl term::Context) -> anyhow::Result<()> { } => { let repo = storage.repository(repo)?; - if type_name == cob::patch::TYPENAME.clone() { - let patches = term::cob::patches(&profile, &repo)?; - for oid in revs { - let oid = &oid.resolve(&repo.backend)?; - let Some(patch) = patches.get(oid)? else { - anyhow::bail!(cob::store::Error::NotFound(type_name, *oid)) - }; - serde_json::to_writer(std::io::stdout(), &patch)?; - println!() + match show(revs, &repo, type_name, &profile) { + Err(e) => { + if let Some(err) = e.downcast_ref::() { + if err.kind() == io::ErrorKind::BrokenPipe { + return Ok(()); + } + } + return Err(e); } - } else if type_name == cob::issue::TYPENAME.clone() { - let issues = term::cob::issues(&profile, &repo)?; - for oid in revs { - let oid = &oid.resolve(&repo.backend)?; - let Some(issue) = issues.get(oid)? else { - anyhow::bail!(cob::store::Error::NotFound(type_name, *oid)) - }; - serde_json::to_writer(std::io::stdout(), &issue)?; - println!() - } - } else if type_name == cob::identity::TYPENAME.clone() { - for oid in revs { - let oid = &oid.resolve(&repo.backend)?; - let Some(cob) = cob::get::(&repo, &type_name, oid)? else { - anyhow::bail!(cob::store::Error::NotFound(type_name, *oid)) - }; - serde_json::to_writer(std::io::stdout(), &cob.object)?; - println!() - } - } else { - anyhow::bail!("the type name '{type_name}' is unknown"); + Ok(()) => {} } } } @@ -265,6 +248,49 @@ pub fn run(options: Options, ctx: impl term::Context) -> anyhow::Result<()> { Ok(()) } +fn show( + revs: Vec, + repo: &git::Repository, + type_name: cob::TypeName, + profile: &Profile, +) -> Result<(), anyhow::Error> { + let mut stdout = std::io::stdout(); + + if type_name == cob::patch::TYPENAME.clone() { + let patches = term::cob::patches(&profile, repo)?; + for oid in revs { + let oid = &oid.resolve(&repo.backend)?; + let Some(patch) = patches.get(oid)? else { + anyhow::bail!(cob::store::Error::NotFound(type_name, *oid)); + }; + serde_json::to_writer(&stdout, &patch)?; + stdout.write_all(b"\n")?; + } + } else if type_name == cob::issue::TYPENAME.clone() { + let issues = term::cob::issues(&profile, repo)?; + for oid in revs { + let oid = &oid.resolve(&repo.backend)?; + let Some(issue) = issues.get(oid)? else { + anyhow::bail!(cob::store::Error::NotFound(type_name, *oid)) + }; + serde_json::to_writer(&stdout, &issue)?; + stdout.write_all(b"\n")?; + } + } else if type_name == cob::identity::TYPENAME.clone() { + for oid in revs { + let oid = &oid.resolve(&repo.backend)?; + let Some(cob) = cob::get::(repo, &type_name, oid)? else { + anyhow::bail!(cob::store::Error::NotFound(type_name, *oid)); + }; + serde_json::to_writer(&stdout, &cob.object)?; + stdout.write_all(b"\n")?; + } + } else { + anyhow::bail!("the type name '{type_name}' is unknown"); + } + Ok(()) +} + fn print_op_pretty(op: Op>) -> anyhow::Result<()> { let time = DateTime::::from( std::time::UNIX_EPOCH + std::time::Duration::from_secs(op.timestamp.as_secs()),