From 2cc5492f9e3ecfbc9a52eb87614f1d8c5ac60506 Mon Sep 17 00:00:00 2001 From: Narthana Epa Date: Sat, 21 Sep 2024 23:11:35 +1000 Subject: [PATCH] Add a header to log files --- Cargo.lock | 174 ++++++++++++++++++++++++++++++++++++++ Cargo.toml | 1 + src/lib.rs | 34 ++++++-- src/log_file.rs | 13 +++ src/main.rs | 10 +++ tests/integration_test.rs | 1 + 6 files changed, 225 insertions(+), 8 deletions(-) create mode 100644 src/log_file.rs diff --git a/Cargo.lock b/Cargo.lock index 7d3f54d..a4f5821 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,6 +2,21 @@ # It is not intended for manual editing. version = 3 +[[package]] +name = "android-tzdata" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0" + +[[package]] +name = "android_system_properties" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" +dependencies = [ + "libc", +] + [[package]] name = "anstream" version = "0.6.15" @@ -67,6 +82,12 @@ dependencies = [ "wait-timeout", ] +[[package]] +name = "autocfg" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0" + [[package]] name = "bitflags" version = "2.6.0" @@ -84,12 +105,41 @@ dependencies = [ "serde", ] +[[package]] +name = "bumpalo" +version = "3.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c" + +[[package]] +name = "cc" +version = "1.1.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07b1695e2c7e8fc85310cde85aeaab7e3097f593c91d209d3f9df76c928100f0" +dependencies = [ + "shlex", +] + [[package]] name = "cfg-if" version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" +[[package]] +name = "chrono" +version = "0.4.38" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a21f936df1771bf62b77f047b726c4625ff2e8aa607c01ec06e5a05bd8463401" +dependencies = [ + "android-tzdata", + "iana-time-zone", + "js-sys", + "num-traits", + "wasm-bindgen", + "windows-targets", +] + [[package]] name = "clap" version = "4.5.17" @@ -136,6 +186,12 @@ version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d3fd119d74b830634cea2a0f58bbd0d54540518a14397557951e79340abc28c0" +[[package]] +name = "core-foundation-sys" +version = "0.8.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" + [[package]] name = "crossbeam" version = "0.8.4" @@ -242,6 +298,29 @@ version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" +[[package]] +name = "iana-time-zone" +version = "0.1.61" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "235e081f3925a06703c2d0117ea8b91f042756fd6e7a6e5d901e8ca1a996b220" +dependencies = [ + "android_system_properties", + "core-foundation-sys", + "iana-time-zone-haiku", + "js-sys", + "wasm-bindgen", + "windows-core", +] + +[[package]] +name = "iana-time-zone-haiku" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" +dependencies = [ + "cc", +] + [[package]] name = "indenter" version = "0.3.3" @@ -254,6 +333,15 @@ version = "1.70.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf" +[[package]] +name = "js-sys" +version = "0.3.70" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1868808506b929d7b0cfa8f75951347aa71bb21144b7791bae35d9bccfcfe37a" +dependencies = [ + "wasm-bindgen", +] + [[package]] name = "libc" version = "0.2.158" @@ -266,12 +354,27 @@ version = "0.4.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89" +[[package]] +name = "log" +version = "0.4.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24" + [[package]] name = "memchr" version = "2.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" +[[package]] +name = "num-traits" +version = "0.2.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" +dependencies = [ + "autocfg", +] + [[package]] name = "once_cell" version = "1.19.0" @@ -344,6 +447,7 @@ name = "runner" version = "0.1.0" dependencies = [ "assert_cmd", + "chrono", "clap", "crossbeam", "eyre", @@ -384,6 +488,12 @@ dependencies = [ "syn", ] +[[package]] +name = "shlex" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" + [[package]] name = "strsim" version = "0.11.1" @@ -441,6 +551,70 @@ dependencies = [ "libc", ] +[[package]] +name = "wasm-bindgen" +version = "0.2.93" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a82edfc16a6c469f5f44dc7b571814045d60404b55a0ee849f9bcfa2e63dd9b5" +dependencies = [ + "cfg-if", + "once_cell", + "wasm-bindgen-macro", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.93" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9de396da306523044d3302746f1208fa71d7532227f15e347e2d93e4145dd77b" +dependencies = [ + "bumpalo", + "log", + "once_cell", + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.93" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "585c4c91a46b072c92e908d99cb1dcdf95c5218eeb6f3bf1efa991ee7a68cccf" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.93" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "afc340c74d9005395cf9dd098506f7f44e38f2b4a21c6aaacf9a105ea5e1e836" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.93" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c62a0a307cb4a311d3a07867860911ca130c3494e8c2719593806c08bc5d0484" + +[[package]] +name = "windows-core" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9" +dependencies = [ + "windows-targets", +] + [[package]] name = "windows-sys" version = "0.52.0" diff --git a/Cargo.toml b/Cargo.toml index e8bb625..143628d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -4,6 +4,7 @@ version = "0.1.0" edition = "2021" [dependencies] +chrono = "0.4.38" clap = { version = "4.5.17", features = ["derive", "env"] } crossbeam = "0.8.4" eyre = "0.6.12" diff --git a/src/lib.rs b/src/lib.rs index 6e644d0..e8256ed 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,24 +1,30 @@ mod flat_map_err; +mod log_file; use crate::flat_map_err::FlatMapErr; +use chrono::Utc; use core::panic; use crossbeam::channel::{bounded, select, Receiver, TrySendError}; use eyre::{eyre, Result}; use std::{ - fs::OpenOptions, io::{stderr, stdin, stdout, Read, Write}, path::Path, process::{Command, Stdio}, thread::{self, Scope}, }; +fn default_header(name: &str) -> String { + format!("[{}]: Begin runner log of {name}", Utc::now()) +} + fn io_streams( + header: Option<&str>, writer: W, log_path: Option<&Path>, ) -> Result<(Stdio, Vec>)> { match log_path { Some(path) => { - let file = OpenOptions::new().create(true).append(true).open(path)?; + let file = log_file::new(header, path)?; Ok((Stdio::piped(), vec![Box::new(writer), Box::new(file)])) } None => Ok((Stdio::inherit(), vec![Box::new(writer)])), @@ -36,13 +42,23 @@ fn io_streams( pub fn run( cmd: &str, args: &[&str], + no_header: bool, stdin_log_path: Option<&Path>, stdout_log_path: Option<&Path>, stderr_log_path: Option<&Path>, ) -> Result { let in_io = stdout_log_path.map_or(Stdio::inherit(), |_| Stdio::piped()); - let (out_io, mut out_writers) = io_streams(stdout(), stdout_log_path)?; - let (err_io, mut err_writers) = io_streams(stderr(), stderr_log_path)?; + + let (out_header, err_header) = if no_header { + (None, None) + } else { + ( + Some(default_header("stdout")), + Some(default_header("stderr")), + ) + }; + let (out_io, mut out_writers) = io_streams(out_header.as_deref(), stdout(), stdout_log_path)?; + let (err_io, mut err_writers) = io_streams(err_header.as_deref(), stderr(), stderr_log_path)?; let mut child = Command::new(cmd) .args(args) @@ -53,10 +69,12 @@ pub fn run( match child.stdin.take() { Some(child_in) => { - let in_file = OpenOptions::new() - .create(true) - .append(true) - .open(stdin_log_path.unwrap())?; + let header = if no_header { + None + } else { + Some(default_header("stdin")) + }; + let in_file = log_file::new(header.as_deref(), stdin_log_path.unwrap())?; let mut in_writers: Vec> = vec![Box::new(child_in), Box::new(in_file)]; diff --git a/src/log_file.rs b/src/log_file.rs new file mode 100644 index 0000000..d4a46c3 --- /dev/null +++ b/src/log_file.rs @@ -0,0 +1,13 @@ +use std::{ + fs::{File, OpenOptions}, + io::{Result, Write}, + path::Path, +}; + +pub(crate) fn new(header: Option<&str>, path: &Path) -> Result { + let mut file = OpenOptions::new().create(true).append(true).open(path)?; + if let Some(header) = header { + writeln!(file, "{header}")?; + } + Ok(file) +} diff --git a/src/main.rs b/src/main.rs index ed1d291..fa36389 100644 --- a/src/main.rs +++ b/src/main.rs @@ -18,6 +18,9 @@ struct Args { #[arg(short, long, env)] err_file: Option, + #[arg(long, env)] + no_header: bool, + /// The command to run and its arguments. A command must be specified, arguments are space delimited. #[arg(last = true, required = true, num_args = 1..)] exec: Vec, @@ -30,6 +33,7 @@ fn main() -> Result<()> { let code = runner::run( exec[0], &exec[1..], + args.no_header, args.in_file.as_deref(), args.out_file.as_deref(), args.err_file.as_deref(), @@ -53,6 +57,7 @@ mod test { in_file: None, out_file: None, err_file: None, + no_header: false, exec: vec!["echo".to_string(), "hello".to_string()], }, ), @@ -62,6 +67,7 @@ mod test { in_file: Some("in.txt".into()), out_file: None, err_file: None, + no_header: false, exec: vec!["echo".to_string(), "hello".to_string()], }, ), @@ -71,6 +77,7 @@ mod test { in_file: None, out_file: Some("out.txt".into()), err_file: None, + no_header: false, exec: vec!["echo".to_string(), "hello".to_string()], }, ), @@ -80,6 +87,7 @@ mod test { in_file: None, out_file: None, err_file: Some("err.txt".into()), + no_header: false, exec: vec!["echo".to_string(), "hello".to_string()], }, ), @@ -98,6 +106,7 @@ mod test { in_file: Some("in.txt".into()), out_file: Some("out.txt".into()), err_file: None, + no_header: false, exec: vec!["echo".to_string(), "hello".to_string()], }, ), @@ -118,6 +127,7 @@ mod test { in_file: Some("in.txt".into()), out_file: Some("out.txt".into()), err_file: Some("err.txt".into()), + no_header: false, exec: vec!["echo".to_string(), "hello".to_string()], }, ), diff --git a/tests/integration_test.rs b/tests/integration_test.rs index fc613dc..83f15b8 100644 --- a/tests/integration_test.rs +++ b/tests/integration_test.rs @@ -59,6 +59,7 @@ fn child_exit_with_files() -> Result<()> { err_file_path .to_str() .ok_or_else(|| eyre!("invalid err-file path"))?, + "--no-header", "--", "cat", ])