From 20845a5cf55626a54b176f3ec9b9cf80bd5fc91d Mon Sep 17 00:00:00 2001 From: Conrad Ludgate Date: Tue, 2 May 2023 03:31:25 +0100 Subject: [PATCH] fix broken pipe on history list (#927) * fix #626 * slightly refactor --- Cargo.lock | 4 +- atuin/Cargo.toml | 2 +- atuin/src/command/client/history.rs | 74 ++++++++++++++++------------- 3 files changed, 44 insertions(+), 36 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index bc262bd0..06feb06a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1793,9 +1793,9 @@ dependencies = [ [[package]] name = "runtime-format" -version = "0.1.2" +version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b035308411b1af4683acc4fc928366443f08b893bb73e235c85de4c2be572495" +checksum = "09958d5b38bca768ede7928c767c89a08ba568144a7b61992aecae79b03c8c94" dependencies = [ "tinyvec", ] diff --git a/atuin/Cargo.toml b/atuin/Cargo.toml index 6b43aa34..1bd77289 100644 --- a/atuin/Cargo.toml +++ b/atuin/Cargo.toml @@ -68,7 +68,7 @@ fs-err = { workspace = true } whoami = { workspace = true } rpassword = "7.0" semver = { workspace = true } -runtime-format = "0.1.2" +runtime-format = "0.1.3" tiny-bip39 = "1" futures-util = "0.3" fuzzy-matcher = "0.3.7" diff --git a/atuin/src/command/client/history.rs b/atuin/src/command/client/history.rs index 76c796ef..4670e8c3 100644 --- a/atuin/src/command/client/history.rs +++ b/atuin/src/command/client/history.rs @@ -1,14 +1,14 @@ use std::{ env, fmt::{self, Display}, - io::{StdoutLock, Write}, + io::{self, Write}, time::Duration, }; use atuin_common::utils; use clap::Subcommand; use eyre::Result; -use runtime_format::{FormatKey, FormatKeyError, ParsedFmt}; +use runtime_format::{FormatKey, FormatKeyError, ParseSegment, ParsedFmt}; use atuin_client::{ database::{current_context, Database}, @@ -97,13 +97,45 @@ pub fn print_list(h: &[History], list_mode: ListMode, format: Option<&str>) { let w = std::io::stdout(); let mut w = w.lock(); - match list_mode { - ListMode::Human => print_human_list(&mut w, h, format), - ListMode::CmdOnly => print_cmd_only(&mut w, h), - ListMode::Regular => print_regular(&mut w, h, format), + let fmt_str = match list_mode { + ListMode::Human => format + .unwrap_or("{time} · {duration}\t{command}") + .replace("\\t", "\t"), + ListMode::Regular => format + .unwrap_or("{time}\t{command}\t{duration}") + .replace("\\t", "\t"), + // not used + ListMode::CmdOnly => String::new(), + }; + + let parsed_fmt = match list_mode { + ListMode::Human | ListMode::Regular => parse_fmt(&fmt_str), + ListMode::CmdOnly => std::iter::once(ParseSegment::Key("command")).collect(), + }; + + for h in h.iter().rev() { + match writeln!(w, "{}", parsed_fmt.with_args(&FmtHistory(h))) { + Ok(()) => {} + // ignore broken pipe (issue #626) + Err(e) if e.kind() == io::ErrorKind::BrokenPipe => { + return; + } + Err(err) => { + eprintln!("ERROR: History output failed with the following error: {err}"); + std::process::exit(1); + } + } } - w.flush().expect("failed to flush history"); + match w.flush() { + Ok(()) => {} + // ignore broken pipe (issue #626) + Err(e) if e.kind() == io::ErrorKind::BrokenPipe => {} + Err(err) => { + eprintln!("ERROR: History output failed with the following error: {err}"); + std::process::exit(1); + } + } } /// type wrapper around `History` so we can implement traits @@ -140,38 +172,14 @@ impl FormatKey for FmtHistory<'_> { } } -fn print_list_with(w: &mut StdoutLock, h: &[History], format: &str) { - let fmt = match ParsedFmt::new(format) { +fn parse_fmt(format: &str) -> ParsedFmt { + match ParsedFmt::new(format) { Ok(fmt) => fmt, Err(err) => { eprintln!("ERROR: History formatting failed with the following error: {err}"); println!("If your formatting string contains curly braces (eg: {{var}}) you need to escape them this way: {{{{var}}."); std::process::exit(1) } - }; - - for h in h.iter().rev() { - writeln!(w, "{}", fmt.with_args(&FmtHistory(h))).expect("failed to write history"); - } -} - -pub fn print_human_list(w: &mut StdoutLock, h: &[History], format: Option<&str>) { - let format = format - .unwrap_or("{time} · {duration}\t{command}") - .replace("\\t", "\t"); - print_list_with(w, h, &format); -} - -pub fn print_regular(w: &mut StdoutLock, h: &[History], format: Option<&str>) { - let format = format - .unwrap_or("{time}\t{command}\t{duration}") - .replace("\\t", "\t"); - print_list_with(w, h, &format); -} - -pub fn print_cmd_only(w: &mut StdoutLock, h: &[History]) { - for h in h.iter().rev() { - writeln!(w, "{}", h.command.trim()).expect("failed to write history"); } }