From 937b460df0356d86b5ffb2555781d18afefee630 Mon Sep 17 00:00:00 2001 From: Miles Johnson Date: Thu, 1 Feb 2024 19:19:29 -0800 Subject: [PATCH] internal: More console buffer improvements. (#1306) * Update console. * Update init. * Use mem. * Use an atomic bool. * Rework format checks. --- crates/cli/src/commands/init/bun.rs | 15 ++- crates/cli/src/commands/init/node.rs | 15 ++- crates/cli/src/commands/init/rust.rs | 15 ++- crates/cli/src/commands/init/typescript.rs | 15 ++- crates/cli/src/systems/execute.rs | 4 +- nextgen/common/src/env.rs | 4 +- nextgen/console/src/console.rs | 109 +++++++++++---------- nextgen/console/src/printer.rs | 3 +- 8 files changed, 85 insertions(+), 95 deletions(-) diff --git a/crates/cli/src/commands/init/bun.rs b/crates/cli/src/commands/init/bun.rs index 730e10ff8dd..5d75fc526bb 100644 --- a/crates/cli/src/commands/init/bun.rs +++ b/crates/cli/src/commands/init/bun.rs @@ -7,7 +7,6 @@ use moon_config::load_toolchain_bun_config_template; use moon_console::Console; use starbase::AppResult; use starbase_styles::color; -use std::io::Write; use std::path::Path; use tera::{Context, Tera}; @@ -25,29 +24,27 @@ pub async fn init_bun( console.out.print_header("Bun")?; console.out.write_raw(|buffer| { - buffer.write_all( + buffer.extend_from_slice( format!( "Toolchain: {}\n", color::url("https://moonrepo.dev/docs/concepts/toolchain") ) .as_bytes(), - )?; - buffer.write_all( + ); + buffer.extend_from_slice( format!( "Handbook: {}\n", color::url("https://moonrepo.dev/docs/guides/javascript/bun-handbook") ) .as_bytes(), - )?; - buffer.write_all( + ); + buffer.extend_from_slice( format!( "Config: {}\n\n", color::url("https://moonrepo.dev/docs/config/toolchain#bun") ) .as_bytes(), - )?; - - Ok(()) + ); })?; } diff --git a/crates/cli/src/commands/init/node.rs b/crates/cli/src/commands/init/node.rs index 7fb4e87e5d6..6d9fde044d0 100644 --- a/crates/cli/src/commands/init/node.rs +++ b/crates/cli/src/commands/init/node.rs @@ -11,7 +11,6 @@ use moon_node_lang::package_json::PackageJson; use starbase::AppResult; use starbase_styles::color; use starbase_utils::fs; -use std::io::Write; use std::path::Path; use tera::{Context, Tera}; @@ -118,29 +117,27 @@ pub async fn init_node( console.out.print_header("Node")?; console.out.write_raw(|buffer| { - buffer.write_all( + buffer.extend_from_slice( format!( "Toolchain: {}\n", color::url("https://moonrepo.dev/docs/concepts/toolchain") ) .as_bytes(), - )?; - buffer.write_all( + ); + buffer.extend_from_slice( format!( "Handbook: {}\n", color::url("https://moonrepo.dev/docs/guides/javascript/node-handbook") ) .as_bytes(), - )?; - buffer.write_all( + ); + buffer.extend_from_slice( format!( "Config: {}\n\n", color::url("https://moonrepo.dev/docs/config/toolchain#node") ) .as_bytes(), - )?; - - Ok(()) + ); })?; } diff --git a/crates/cli/src/commands/init/rust.rs b/crates/cli/src/commands/init/rust.rs index 640ae6dfdaf..8b7a4821049 100644 --- a/crates/cli/src/commands/init/rust.rs +++ b/crates/cli/src/commands/init/rust.rs @@ -8,7 +8,6 @@ use moon_console::Console; use moon_rust_lang::toolchain_toml::ToolchainTomlCache; use starbase::AppResult; use starbase_styles::color; -use std::io::Write; use std::path::Path; use tera::{Context, Tera}; @@ -46,29 +45,27 @@ pub async fn init_rust( console.out.print_header("Rust")?; console.out.write_raw(|buffer| { - buffer.write_all( + buffer.extend_from_slice( format!( "Toolchain: {}\n", color::url("https://moonrepo.dev/docs/concepts/toolchain") ) .as_bytes(), - )?; - buffer.write_all( + ); + buffer.extend_from_slice( format!( "Handbook: {}\n", color::url("https://moonrepo.dev/docs/guides/rust/handbook") ) .as_bytes(), - )?; - buffer.write_all( + ); + buffer.extend_from_slice( format!( "Config: {}\n\n", color::url("https://moonrepo.dev/docs/config/toolchain#rust") ) .as_bytes(), - )?; - - Ok(()) + ); })?; } diff --git a/crates/cli/src/commands/init/typescript.rs b/crates/cli/src/commands/init/typescript.rs index 0be26c16616..428e3216208 100644 --- a/crates/cli/src/commands/init/typescript.rs +++ b/crates/cli/src/commands/init/typescript.rs @@ -7,7 +7,6 @@ use moon_console::Console; use moon_typescript_lang::TsConfigJson; use starbase::AppResult; use starbase_styles::color; -use std::io::Write; use std::path::Path; use tera::{Context, Tera}; @@ -25,14 +24,14 @@ pub async fn init_typescript( console.out.print_header("TypeScript")?; console.out.write_raw(|buffer| { - buffer.write_all( + buffer.extend_from_slice( format!( "Toolchain: {}\n", color::url("https://moonrepo.dev/docs/concepts/toolchain") ) .as_bytes(), - )?; - buffer.write_all( + ); + buffer.extend_from_slice( format!( "Handbook: {}\n", color::url( @@ -40,16 +39,14 @@ pub async fn init_typescript( ) ) .as_bytes(), - )?; - buffer.write_all( + ); + buffer.extend_from_slice( format!( "Config: {}\n\n", color::url("https://moonrepo.dev/docs/config/toolchain#typescript") ) .as_bytes(), - )?; - - Ok(()) + ); })?; } diff --git a/crates/cli/src/systems/execute.rs b/crates/cli/src/systems/execute.rs index b2dc4be8c81..59b2c79203b 100644 --- a/crates/cli/src/systems/execute.rs +++ b/crates/cli/src/systems/execute.rs @@ -1,6 +1,6 @@ use moon_api::Launchpad; use moon_app_components::{AppConsole, MoonEnv}; -use moon_common::{color, is_test_env, is_unformatted_stdout}; +use moon_common::{color, is_formatted_output, is_test_env}; use moon_console::Checkpoint; use moon_workspace::Workspace; use starbase::system; @@ -12,7 +12,7 @@ pub async fn check_for_new_version( workspace: ResourceRef, console: ResourceRef, ) { - if is_test_env() || !is_unformatted_stdout() || !moon::is_telemetry_enabled() { + if is_test_env() || is_formatted_output() || !moon::is_telemetry_enabled() { return Ok(()); } diff --git a/nextgen/common/src/env.rs b/nextgen/common/src/env.rs index 9d39d1bf03d..41c8b5ae962 100644 --- a/nextgen/common/src/env.rs +++ b/nextgen/common/src/env.rs @@ -20,6 +20,6 @@ pub fn is_test_env() -> bool { } #[inline] -pub fn is_unformatted_stdout() -> bool { - !env::args().any(|arg| arg == "--json" || arg == "--dot") +pub fn is_formatted_output() -> bool { + env::args().any(|arg| arg == "--json" || arg == "--dot") } diff --git a/nextgen/console/src/console.rs b/nextgen/console/src/console.rs index 88c285ac8a6..dc0d5610a6e 100644 --- a/nextgen/console/src/console.rs +++ b/nextgen/console/src/console.rs @@ -1,6 +1,9 @@ use miette::IntoDiagnostic; +use moon_common::is_formatted_output; use parking_lot::Mutex; -use std::io::{self, BufWriter, IsTerminal, Write}; +use std::io::{self, IsTerminal, Write}; +use std::mem; +use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::mpsc::{self, Receiver, Sender, TryRecvError}; use std::sync::Arc; use std::thread::{sleep, spawn, JoinHandle}; @@ -13,19 +16,19 @@ pub enum ConsoleStream { } pub struct ConsoleBuffer { - buffer: Arc>>>, + buffer: Arc>>, closed: bool, channel: Option>, handle: Option>, stream: ConsoleStream, - pub quiet: bool, - pub test_mode: bool, + quiet: Option>, + test_mode: bool, } impl ConsoleBuffer { fn internal_new(stream: ConsoleStream, with_handle: bool) -> Self { - let buffer = Arc::new(Mutex::new(BufWriter::new(Vec::new()))); + let buffer = Arc::new(Mutex::new(Vec::new())); let buffer_clone = Arc::clone(&buffer); let (tx, rx) = mpsc::channel(); @@ -42,15 +45,13 @@ impl ConsoleBuffer { channel: Some(tx), handle, stream, - quiet: false, + quiet: None, test_mode: false, } } - pub fn new(stream: ConsoleStream, quiet: bool) -> Self { - let mut console = Self::internal_new(stream, true); - console.quiet = quiet; - console + pub fn new(stream: ConsoleStream) -> Self { + Self::internal_new(stream, true) } pub fn new_testing(stream: ConsoleStream) -> Self { @@ -66,6 +67,12 @@ impl ConsoleBuffer { } } + pub fn is_quiet(&self) -> bool { + self.quiet + .as_ref() + .is_some_and(|quiet| quiet.load(Ordering::Relaxed)) + } + pub fn close(&mut self) -> miette::Result<()> { self.flush()?; @@ -94,33 +101,29 @@ impl ConsoleBuffer { Ok(()) } - pub fn write_raw>) -> io::Result<()>>( - &self, - mut op: F, - ) -> miette::Result<()> { + pub fn write_raw)>(&self, mut op: F) -> miette::Result<()> { if self.closed { return Ok(()); } // When testing just flush immediately if self.test_mode { - let mut buffer = BufWriter::new(Vec::new()); - - op(&mut buffer).into_diagnostic()?; + let mut buffer = Vec::new(); - drain(&mut buffer, self.stream).into_diagnostic()?; + op(&mut buffer); - return Ok(()); + flush(&mut buffer, self.stream).into_diagnostic()?; } + // Otherwise just write to the buffer and flush + // when its length grows too large + else { + let mut buffer = self.buffer.lock(); - // Otherwise just write to the buffer - let mut buffer = self.buffer.lock(); - - op(&mut buffer).into_diagnostic()?; + op(&mut buffer); - // Buffer has written its data to the inner vec, so drain it - if !buffer.get_ref().is_empty() { - drain(&mut buffer, self.stream).into_diagnostic()?; + if buffer.len() >= 1024 { + flush(&mut buffer, self.stream).into_diagnostic()?; + } } Ok(()) @@ -133,7 +136,7 @@ impl ConsoleBuffer { return Ok(()); } - self.write_raw(|buffer| buffer.write_all(data)) + self.write_raw(|buffer| buffer.extend_from_slice(data)) } pub fn write_line>(&self, data: T) -> miette::Result<()> { @@ -144,9 +147,8 @@ impl ConsoleBuffer { } self.write_raw(|buffer| { - buffer.write_all(data)?; - buffer.write_all(b"\n")?; - Ok(()) + buffer.extend_from_slice(data); + buffer.push(b'\n'); }) } @@ -167,7 +169,7 @@ impl Clone for ConsoleBuffer { buffer: Arc::clone(&self.buffer), closed: self.closed, stream: self.stream, - quiet: self.quiet, + quiet: self.quiet.clone(), test_mode: self.test_mode, // Ignore for clones channel: None, @@ -180,24 +182,31 @@ impl Clone for ConsoleBuffer { pub struct Console { pub err: ConsoleBuffer, pub out: ConsoleBuffer, + + quiet: Arc, } impl Console { pub fn new(quiet: bool) -> Self { - Self { - err: ConsoleBuffer::new(ConsoleStream::Stderr, quiet), - out: ConsoleBuffer::new(ConsoleStream::Stdout, quiet), - } + let quiet = Arc::new(AtomicBool::new(quiet || is_formatted_output())); + + let mut err = ConsoleBuffer::new(ConsoleStream::Stderr); + err.quiet = Some(Arc::clone(&quiet)); + + let mut out = ConsoleBuffer::new(ConsoleStream::Stdout); + out.quiet = Some(Arc::clone(&quiet)); + + Self { err, out, quiet } } pub fn new_testing() -> Self { Self { err: ConsoleBuffer::new_testing(ConsoleStream::Stderr), out: ConsoleBuffer::new_testing(ConsoleStream::Stdout), + quiet: Arc::new(AtomicBool::new(false)), } } - // This should be safe since there will only be one console instance pub fn close(&mut self) -> miette::Result<()> { self.err.close()?; self.out.close()?; @@ -205,6 +214,10 @@ impl Console { Ok(()) } + pub fn quiet(&self) { + self.quiet.store(true, Ordering::Release); + } + pub fn stderr(&self) -> &ConsoleBuffer { &self.err } @@ -220,8 +233,12 @@ impl Drop for Console { } } -fn drain(buffer: &mut BufWriter>, stream: ConsoleStream) -> io::Result<()> { - let data = buffer.get_mut().drain(0..).collect::>(); +fn flush(buffer: &mut Vec, stream: ConsoleStream) -> io::Result<()> { + if buffer.is_empty() { + return Ok(()); + } + + let data = mem::take(buffer); match stream { ConsoleStream::Stderr => io::stderr().lock().write_all(&data), @@ -229,21 +246,7 @@ fn drain(buffer: &mut BufWriter>, stream: ConsoleStream) -> io::Result<( } } -fn flush(buffer: &mut BufWriter>, stream: ConsoleStream) -> io::Result<()> { - buffer.flush()?; - - if buffer.get_ref().is_empty() { - return Ok(()); - } - - drain(buffer, stream) -} - -fn flush_on_loop( - buffer: Arc>>>, - stream: ConsoleStream, - receiver: Receiver, -) { +fn flush_on_loop(buffer: Arc>>, stream: ConsoleStream, receiver: Receiver) { loop { sleep(Duration::from_millis(100)); diff --git a/nextgen/console/src/printer.rs b/nextgen/console/src/printer.rs index 3a7bb807457..92e844a61ce 100644 --- a/nextgen/console/src/printer.rs +++ b/nextgen/console/src/printer.rs @@ -1,5 +1,4 @@ use crate::console::ConsoleBuffer; -use moon_common::is_unformatted_stdout; use starbase_styles::color::owo::{OwoColorize, XtermColors}; use starbase_styles::color::{self, no_color, Color, OwoStyle}; @@ -88,7 +87,7 @@ impl ConsoleBuffer { message: M, comments: C, ) -> miette::Result<()> { - if !self.quiet && is_unformatted_stdout() { + if !self.is_quiet() { self.write_line(self.format_checkpoint(checkpoint, message, comments))?; }