diff --git a/Cargo.lock b/Cargo.lock index c0e5cfa..e2220b7 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -65,15 +65,6 @@ dependencies = [ "libc", ] -[[package]] -name = "ansi_term" -version = "0.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d52a9bb7ec0cf484c551830a7ce27bd20d67eac647e1befb56b0be4ee39a55d2" -dependencies = [ - "winapi", -] - [[package]] name = "appendlist" version = "1.4.0" @@ -506,6 +497,27 @@ dependencies = [ "inout", ] +[[package]] +name = "color-print" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3aa954171903797d5623e047d9ab69d91b493657917bdfb8c2c80ecaf9cdb6f4" +dependencies = [ + "color-print-proc-macro", +] + +[[package]] +name = "color-print-proc-macro" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "692186b5ebe54007e45a59aea47ece9eb4108e141326c304cdc91699a7118a22" +dependencies = [ + "nom", + "proc-macro2", + "quote", + "syn 2.0.98", +] + [[package]] name = "concurrent-queue" version = "2.5.0" @@ -1633,10 +1645,10 @@ dependencies = [ name = "pgxn_build" version = "0.1.0" dependencies = [ - "ansi_term", "assertables", "cargo_toml", "chrono", + "color-print", "hex", "httpmock", "iri-string", diff --git a/Cargo.toml b/Cargo.toml index 9daec2f..369ab7f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -15,9 +15,9 @@ exclude = [ ".github", ".vscode", ".gitignore", ".ci", ".pre-*.yaml"] # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -ansi_term = "0.12.1" cargo_toml = "0.21.0" chrono = "0.4.39" +color-print = "0.3.7" hex = "0.4.3" iri-string = "0.7.7" log = { version = "0.4.25", features = ["kv"] } diff --git a/src/pipeline/mod.rs b/src/pipeline/mod.rs index 3c6e7aa..e552b60 100644 --- a/src/pipeline/mod.rs +++ b/src/pipeline/mod.rs @@ -1,11 +1,13 @@ //! Build Pipeline interface definition. use crate::{error::BuildError, pg_config::PgConfig}; +use color_print::cwriteln; use log::debug; use std::{ io::{self, BufRead, BufReader, IsTerminal, Write}, path::Path, process::{Command, Stdio}, + thread, }; /// Defines the interface for build pipelines to configure, compile, and test @@ -84,68 +86,53 @@ pub(crate) trait Pipeline> { fn pipe_command(mut cmd: Command, mut out: O, mut err: E) -> Result<(), BuildError> where - O: io::Write + IsTerminal, - E: io::Write + IsTerminal, + O: io::Write + IsTerminal + std::marker::Send + 'static, + E: io::Write + IsTerminal + std::marker::Send + 'static, { + // Create pipes from the child's stdout and stderr. cmd.stdout(Stdio::piped()).stderr(Stdio::piped()); - debug!(command:? = cmd; "Executing"); // Spawn the child process. + debug!(command:? = cmd; "Executing"); let mut child = cmd .spawn() .map_err(|e| BuildError::Command(format!("{:?}", cmd), e.kind().to_string()))?; - let grey = ansi_term::Color::Fixed(244).dimmed(); - let red = ansi_term::Color::Red; - { - // https://stackoverflow.com/a/41024767/79202 - let child_out = child - .stdout - .take() - .ok_or_else(|| BuildError::Command(format!("{:?}", cmd), "no stdout".to_string()))?; - let child_err = child - .stderr - .take() - .ok_or_else(|| BuildError::Command(format!("{:?}", cmd), "no stderr".to_string()))?; - - let mut child_out = BufReader::new(child_out); - let mut child_err = BufReader::new(child_err); - - loop { - let (stdout_len, stderr_len) = match (child_out.fill_buf(), child_err.fill_buf()) { - (Ok(child_out), Ok(child_err)) => { - if out.is_terminal() { - write!(out, "{}", grey.prefix())?; - out.write_all(child_out)?; - write!(out, "{}", grey.suffix())?; - } else { - out.write_all(child_out)?; - } - if err.is_terminal() { - write!(err, "{}", red.prefix())?; - err.write_all(child_err)?; - write!(err, "{}", red.suffix())?; - } else { - err.write_all(child_err)?; - } - - (child_out.len(), child_err.len()) - } - other => panic!("Some better error handling here... {:?}", other), - }; - - if stdout_len == 0 && stderr_len == 0 { - // if let Ok(Some(_)) = child.try_wait() { - break; - } + // Grab the stdout and stderr pipes. + let child_out = child + .stdout + .take() + .ok_or_else(|| BuildError::Command(format!("{:?}", cmd), "no stdout".to_string()))?; + let child_err = child + .stderr + .take() + .ok_or_else(|| BuildError::Command(format!("{:?}", cmd), "no stderr".to_string()))?; + + // Read from the pipes and write to final output in separate threads. + // https://stackoverflow.com/a/72831067/79202 + let stdout_thread = thread::spawn(move || -> Result<(), io::Error> { + let stdout_lines = BufReader::new(child_out).lines(); + for line in stdout_lines { + cwriteln!(out, "<244>{}", line.unwrap())?; + } + Ok(()) + }); - child_out.consume(stdout_len); - child_err.consume(stderr_len); + let stderr_thread = thread::spawn(move || -> Result<(), io::Error> { + let stderr_lines = BufReader::new(child_err).lines(); + for line in stderr_lines { + cwriteln!(err, "{}", line.unwrap())?; } - } + Ok(()) + }); + + // Wait for the child and output threads to finish. + let res = child.wait(); + stdout_thread.join().unwrap()?; + stderr_thread.join().unwrap()?; - // Execute the command. - match child.wait() { + // Determine how the command finished. + match res { Ok(status) => { if !status.success() { return Err(BuildError::Command(