diff --git a/rust-tests/src/lib.rs b/rust-tests/src/lib.rs index 7b74a5881..890f3ca96 100644 --- a/rust-tests/src/lib.rs +++ b/rust-tests/src/lib.rs @@ -81,7 +81,7 @@ mod tests { let stdout = String::from_utf8(output.stdout.clone()) .expect("Failed to convert output to UTF-8"); - println!( + tracing::info!( "Running: {} {}", command, args_vec @@ -91,7 +91,7 @@ mod tests { .join(" ") ); - println!("{}", stdout); + tracing::info!("{}", stdout); output } diff --git a/src/console_utils.rs b/src/console_utils.rs index 5e531311a..123903712 100644 --- a/src/console_utils.rs +++ b/src/console_utils.rs @@ -1,64 +1,29 @@ use console::style; -use indicatif::MultiProgress; +use indicatif::{HumanBytes, HumanDuration, MultiProgress, ProgressState, ProgressStyle}; use std::{ - io::{self, Write}, + collections::HashMap, + io, sync::{Arc, Mutex}, + time::Instant, }; use tracing::field; use tracing_core::{span::Id, Event, Field, Subscriber}; use tracing_subscriber::{ fmt::{ format::{self, Format}, - FmtContext, FormatEvent, FormatFields, MakeWriter, + FmtContext, FormatEvent, FormatFields, }, layer::Context, registry::LookupSpan, Layer, }; -#[derive(Clone)] -pub struct IndicatifWriter { - progress_bars: MultiProgress, -} - -impl IndicatifWriter { - pub(crate) fn new(pb: MultiProgress) -> Self { - Self { progress_bars: pb } - } -} - -impl io::Write for IndicatifWriter { - fn write(&mut self, buf: &[u8]) -> io::Result { - self.progress_bars.suspend(|| io::stderr().write(buf)) - } - - fn flush(&mut self) -> io::Result<()> { - self.progress_bars.suspend(|| io::stderr().flush()) - } -} - -impl<'a> MakeWriter<'a> for IndicatifWriter { - type Writer = IndicatifWriter; - - fn make_writer(&'a self) -> Self::Writer { - self.clone() - } -} - +#[derive(Default)] struct TracingFormatterInner { indentation_level: usize, current_section: String, } -impl Default for TracingFormatterInner { - fn default() -> Self { - Self { - indentation_level: 0, - current_section: String::new(), - } - } -} - impl Default for TracingFormatter { fn default() -> Self { Self { @@ -83,10 +48,6 @@ where event: &Event<'_>, ) -> std::fmt::Result { let metadata = event.metadata(); - // println!("metadata: {:?}", metadata); - // println!("full event: {:?}", event); - println!("span: {:?}", ctx.current_span()); - fn indent_levels(indent: usize) -> String { let mut s = String::new(); for _ in 0..indent { @@ -139,28 +100,17 @@ where } } -pub struct CustomLayer { - shared_state: Arc>, - writer: IndicatifWriter, -} - -impl CustomLayer { - pub fn new(writer: IndicatifWriter) -> Self { - Self { - shared_state: Arc::new(Mutex::new(SharedState::new())), - writer, - } - } -} - +#[derive(Debug)] struct SharedState { indentation_level: usize, + timestamps: HashMap, } impl SharedState { fn new() -> Self { Self { indentation_level: 0, + timestamps: HashMap::new(), } } } @@ -215,7 +165,7 @@ fn chunk_string_without_ansi(input: &str, max_chunk_length: usize) -> Vec String { format!("{}", style(s).cyan()) } -impl Layer for CustomLayer +impl Layer for LoggingOutputHandler where S: Subscriber + for<'a> LookupSpan<'a>, { fn on_enter(&self, id: &Id, ctx: Context<'_, S>) { - let mut state = self.shared_state.lock().unwrap(); - + let mut state = self.state.lock().unwrap(); + state.timestamps.insert(id.clone(), Instant::now()); let span = ctx.span(id); let ind = indent_levels(state.indentation_level); @@ -265,43 +215,177 @@ where state.indentation_level += 1; } - fn on_exit(&self, _id: &Id, _ctx: Context<'_, S>) { - let mut state = self.shared_state.lock().unwrap(); + fn on_exit(&self, id: &Id, _ctx: Context<'_, S>) { + let mut state = self.state.lock().unwrap(); + + let prev_ind = indent_levels(state.indentation_level); if state.indentation_level > 0 { state.indentation_level -= 1; } let ind = indent_levels(state.indentation_level); - println!("{ind} {}", style("╰───────────────────").cyan()); + + let elapsed_time = state + .timestamps + .remove(id) + .map(|t| t.elapsed()) + .unwrap_or_default(); + + let human_duration = HumanDuration(elapsed_time); + + println!( + "{prev_ind}\n{ind} {} (took {})\n{ind}", + style("╰─────────────────── ").cyan(), + human_duration + ); } fn on_event(&self, event: &Event<'_>, _ctx: Context<'_, S>) { - let state = self.shared_state.lock().unwrap(); - + let state = self.state.lock().unwrap(); let indent_str = indent_levels(state.indentation_level); let mut s = Vec::new(); let mut w = io::Cursor::new(&mut s); event.record(&mut CustomVisitor::new(&mut w)); - let s = String::from_utf8_lossy(&w.get_ref()); + let s = String::from_utf8_lossy(w.get_ref()); let width: usize = terminal_size::terminal_size() .map(|(w, _)| w.0) .unwrap_or(80) as usize; let max_width = width - (state.indentation_level * 2) - 1; - let mut writer = self.writer.make_writer(); - for line in s.lines() { - // split line into max_width chunks - if line.len() <= max_width { - writeln!(writer, "{} {}", indent_str, line).unwrap(); - } else { - chunk_string_without_ansi(line, max_width) - .iter() - .for_each(|chunk| { - writeln!(writer, "{} {}", indent_str, chunk).unwrap(); - }); + + self.progress_bars.suspend(|| { + for line in s.lines() { + // split line into max_width chunks + if line.len() <= max_width { + println!("{} {}", indent_str, line); + } else { + chunk_string_without_ansi(line, max_width) + .iter() + .for_each(|chunk| { + println!("{} {}", indent_str, chunk); + }); + } } + }); + } +} + +#[derive(Debug)] +pub struct LoggingOutputHandler { + state: Arc>, + progress_bars: MultiProgress, +} + +impl Clone for LoggingOutputHandler { + fn clone(&self) -> Self { + Self { + state: self.state.clone(), + progress_bars: self.progress_bars.clone(), + } + } +} + +impl LoggingOutputHandler { + pub fn new(pb: MultiProgress) -> Self { + Self { + state: Arc::new(Mutex::new(SharedState::new())), + progress_bars: pb, } } + + fn with_indent_levels(&self, template: &str) -> String { + let state = self.state.lock().unwrap(); + let indent_str = indent_levels(state.indentation_level); + format!("{} {}", indent_str, template) + } + + /// Returns the style to use for a progressbar that is currently in progress. + pub fn default_bytes_style(&self) -> indicatif::ProgressStyle { + let template_str = self.with_indent_levels( + "{spinner:.green} {prefix:20!} [{elapsed_precise}] [{bar:40!.bright.yellow/dim.white}] {bytes:>8} @ {smoothed_bytes_per_sec:8}" + ); + + indicatif::ProgressStyle::default_bar() + .template(&template_str) + .unwrap() + .progress_chars("━━╾─") + .with_key( + "smoothed_bytes_per_sec", + |s: &ProgressState, w: &mut dyn std::fmt::Write| match ( + s.pos(), + s.elapsed().as_millis(), + ) { + (pos, elapsed_ms) if elapsed_ms > 0 => { + // TODO: log with tracing? + _ = write!( + w, + "{}/s", + HumanBytes((pos as f64 * 1000_f64 / elapsed_ms as f64) as u64) + ); + } + _ => { + _ = write!(w, "-"); + } + }, + ) + } + + /// Returns the style to use for a progressbar that is currently in progress. + pub fn default_progress_style(&self) -> indicatif::ProgressStyle { + let template_str = self.with_indent_levels( + "{spinner:.green} {prefix:20!} [{elapsed_precise}] [{bar:40!.bright.yellow/dim.white}] {pos:>7}/{len:7}" + ); + indicatif::ProgressStyle::default_bar() + .template(&template_str) + .unwrap() + .progress_chars("━━╾─") + } + + /// Returns the style to use for a progressbar that is in Deserializing state. + pub fn deserializing_progress_style(&self) -> indicatif::ProgressStyle { + let template_str = + self.with_indent_levels("{spinner:.green} {prefix:20!} [{elapsed_precise}] {wide_msg}"); + indicatif::ProgressStyle::default_bar() + .template(&template_str) + .unwrap() + .progress_chars("━━╾─") + } + + /// Returns the style to use for a progressbar that is finished. + pub fn finished_progress_style(&self) -> indicatif::ProgressStyle { + let template_str = self.with_indent_levels(&format!( + "{} {{spinner:.green}} {{prefix:20!}} [{{elapsed_precise}}] {{msg:.bold.green}}", + console::style(console::Emoji("✔", " ")).green() + )); + + indicatif::ProgressStyle::default_bar() + .template(&template_str) + .unwrap() + .progress_chars("━━╾─") + } + + /// Returns the style to use for a progressbar that is in error state. + pub fn errored_progress_style(&self) -> indicatif::ProgressStyle { + let template_str = self.with_indent_levels(&format!( + "{} {{prefix:20!}} [{{elapsed_precise}}] {{msg:.bold.red}}", + console::style(console::Emoji("×", " ")).red() + )); + + indicatif::ProgressStyle::default_bar() + .template(&template_str) + .unwrap() + .progress_chars("━━╾─") + } + + /// Returns the style to use for a progressbar that is indeterminate and simply shows a spinner. + pub fn long_running_progress_style(&self) -> indicatif::ProgressStyle { + let template_str = self.with_indent_levels("{spinner:.green} {msg}"); + ProgressStyle::with_template(&template_str).unwrap() + } + + pub fn add_progress_bar(&self, progress_bar: indicatif::ProgressBar) -> indicatif::ProgressBar { + self.progress_bars.add(progress_bar) + } } diff --git a/src/lib.rs b/src/lib.rs index b5a459018..fa067c127 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,8 +1,9 @@ -#![deny(missing_docs)] +// #![deny(missing_docs)] //! The library pieces of rattler-build pub mod build; +pub mod console_utils; pub mod metadata; pub mod package_test; pub mod packaging; diff --git a/src/main.rs b/src/main.rs index 071cb22b8..28b0c79a3 100644 --- a/src/main.rs +++ b/src/main.rs @@ -3,7 +3,6 @@ use clap::{arg, crate_version, CommandFactory, Parser}; use clap_verbosity_flag::{InfoLevel, Verbosity}; -use console_utils::CustomLayer; use dunce::canonicalize; use fs_err as fs; use indicatif::MultiProgress; @@ -25,6 +24,7 @@ use url::Url; use rattler_build::{ build::run_build, + console_utils::LoggingOutputHandler, hash::HashInfo, metadata::{BuildConfiguration, Directories, PackageIdentifier, PackagingSettings}, package_test::{self, TestConfiguration}, @@ -39,12 +39,9 @@ use rattler_build::{ variant_config::{ParseErrors, VariantConfig}, }; -mod console_utils; mod rebuild; mod upload; -use crate::console_utils::IndicatifWriter; - #[derive(Parser)] enum SubCommands { /// Build a package @@ -419,14 +416,15 @@ pub struct CondaForgeOpts { async fn main() -> miette::Result<()> { let args = App::parse(); - let multi_progress = MultiProgress::new(); + let fancy_log_handler = LoggingOutputHandler::new(MultiProgress::new()); // Setup tracing subscriber tracing_subscriber::registry() .with(get_default_env_filter(args.verbose.log_level_filter()).into_diagnostic()?) - .with(CustomLayer::new(IndicatifWriter::new( - multi_progress.clone(), - ))) + .with(fancy_log_handler.clone()) + // .with(CustomLayer::new(IndicatifWriter::new( + // multi_progress.clone(), + // ))) // .with( // fmt::layer() // .with_writer(IndicatifWriter::new(multi_progress.clone())) @@ -451,7 +449,7 @@ async fn main() -> miette::Result<()> { print_completions(shell, &mut cmd); Ok(()) } - Some(SubCommands::Build(args)) => run_build_from_args(args, multi_progress).await, + Some(SubCommands::Build(args)) => run_build_from_args(args, fancy_log_handler).await, Some(SubCommands::Test(args)) => run_test_from_args(args).await, Some(SubCommands::Rebuild(args)) => rebuild_from_args(args).await, Some(SubCommands::Upload(args)) => upload_from_args(args).await, @@ -479,7 +477,7 @@ async fn run_test_from_args(args: TestOpts) -> miette::Result<()> { .unwrap_or_else(|| vec!["conda-forge".to_string()]), tool_configuration: tool_configuration::Configuration { client, - multi_progress_indicator: MultiProgress::new(), + fancy_log_handler: LoggingOutputHandler::new(MultiProgress::new()), // duplicate from `keep_test_prefix`? no_clean: false, ..Default::default() @@ -493,7 +491,10 @@ async fn run_test_from_args(args: TestOpts) -> miette::Result<()> { Ok(()) } -async fn run_build_from_args(args: BuildOpts, multi_progress: MultiProgress) -> miette::Result<()> { +async fn run_build_from_args( + args: BuildOpts, + fancy_log_handler: LoggingOutputHandler, +) -> miette::Result<()> { let recipe_path = canonicalize(&args.recipe); if let Err(e) = &recipe_path { match e.kind() { @@ -598,7 +599,7 @@ async fn run_build_from_args(args: BuildOpts, multi_progress: MultiProgress) -> let tool_config = tool_configuration::Configuration { client, - multi_progress_indicator: multi_progress, + fancy_log_handler: fancy_log_handler.clone(), no_clean: args.keep_build, no_test: args.no_test, use_zstd: args.common.use_zstd, @@ -743,7 +744,7 @@ async fn rebuild_from_args(args: RebuildOpts) -> miette::Result<()> { let tool_config = tool_configuration::Configuration { client, - multi_progress_indicator: MultiProgress::new(), + fancy_log_handler: LoggingOutputHandler::new(MultiProgress::new()), no_clean: true, no_test: args.no_test, use_zstd: args.common.use_zstd, diff --git a/src/package_test/run_test.rs b/src/package_test/run_test.rs index e39292f96..e7e4a8edc 100644 --- a/src/package_test/run_test.rs +++ b/src/package_test/run_test.rs @@ -243,10 +243,10 @@ async fn legacy_tests_from_folder(pkg: &Path) -> Result<(PathBuf, Vec), T continue; }; if file_name.eq("run_test.sh") || file_name.eq("run_test.bat") { - println!("test {}", file_name.to_string_lossy()); + tracing::info!("test {}", file_name.to_string_lossy()); tests.push(Tests::Commands(path)); } else if file_name.eq("run_test.py") { - println!("test {}", file_name.to_string_lossy()); + tracing::info!("test {}", file_name.to_string_lossy()); tests.push(Tests::Python(path)); } } @@ -414,7 +414,7 @@ pub async fn run_test(package_file: &Path, config: &TestConfiguration) -> Result // for each enumerated test, we load and run it while let Some(entry) = read_dir.next_entry().await? { - println!("test {:?}", entry.path()); + tracing::info!("test {:?}", entry.path()); run_individual_test(&pkg, &entry.path(), &prefix, &config).await?; } @@ -576,7 +576,7 @@ async fn run_individual_test( // no test found } - println!( + tracing::info!( "{} test passed!", console::style(console::Emoji("✔", "")).green() ); diff --git a/src/packaging.rs b/src/packaging.rs index ead881942..7b40fd98f 100644 --- a/src/packaging.rs +++ b/src/packaging.rs @@ -20,7 +20,6 @@ pub use metadata::create_prefix_placeholder; use crate::metadata::Output; use crate::package_test::write_test_files; -use crate::render::solver::default_bytes_style; use crate::{post_process, tool_configuration}; #[allow(missing_docs)] @@ -248,10 +247,10 @@ pub fn package_conda( tracing::info!("Compressing archive..."); - let progress_bar = tool_configuration.multi_progress_indicator.add( + let progress_bar = tool_configuration.fancy_log_handler.add_progress_bar( indicatif::ProgressBar::new(0) .with_prefix("Compressing ") - .with_style(default_bytes_style().unwrap()), + .with_style(tool_configuration.fancy_log_handler.default_bytes_style()), ); match packaging_settings.archive_type { diff --git a/src/render/solver.rs b/src/render/solver.rs index 8fdaca921..05a13c00f 100644 --- a/src/render/solver.rs +++ b/src/render/solver.rs @@ -2,7 +2,7 @@ use anyhow::Context; use comfy_table::Table; use futures::{stream, stream::FuturesUnordered, FutureExt, StreamExt, TryFutureExt, TryStreamExt}; -use indicatif::{style::TemplateError, HumanBytes, ProgressBar, ProgressState, ProgressStyle}; +use indicatif::{style::TemplateError, HumanBytes, ProgressBar}; use rattler::{ install::{link_package, InstallDriver, InstallOptions, Transaction, TransactionOperation}, package_cache::PackageCache, @@ -20,7 +20,6 @@ use reqwest_middleware::ClientWithMiddleware; use std::{ borrow::Cow, - fmt::Write, future::ready, io::ErrorKind, path::{Path, PathBuf}, @@ -28,7 +27,7 @@ use std::{ }; use tokio::task::JoinHandle; -use crate::tool_configuration; +use crate::{console_utils::LoggingOutputHandler, tool_configuration}; fn print_as_table(packages: &Vec) { let mut table = Table::new(); @@ -134,7 +133,7 @@ pub async fn create_environment( platform, &repodata_cache, download_client.clone(), - tool_configuration.multi_progress_indicator.clone(), + tool_configuration.fancy_log_handler.clone(), platform != Platform::NoArch, ) .await @@ -150,21 +149,27 @@ pub async fn create_environment( // Get the package names from the matchspecs so we can only load the package records that we need. let package_names = specs.iter().filter_map(|spec| spec.name.clone()); - let repodatas = wrap_in_progress("parsing repodata", move || { - SparseRepoData::load_records_recursive(&sparse_repo_datas, package_names, None) - })??; + let repodatas = wrap_in_progress( + "parsing repodata", + &tool_configuration.fancy_log_handler, + move || SparseRepoData::load_records_recursive(&sparse_repo_datas, package_names, None), + )??; // Determine virtual packages of the system. These packages define the capabilities of the // system. Some packages depend on these virtual packages to indicate compatibility with the // hardware of the system. - let virtual_packages = wrap_in_progress("determining virtual packages", move || { - rattler_virtual_packages::VirtualPackage::current().map(|vpkgs| { - vpkgs - .iter() - .map(|vpkg| GenericVirtualPackage::from(vpkg.clone())) - .collect::>() - }) - })??; + let virtual_packages = wrap_in_progress( + "determining virtual packages", + &tool_configuration.fancy_log_handler, + move || { + rattler_virtual_packages::VirtualPackage::current().map(|vpkgs| { + vpkgs + .iter() + .map(|vpkg| GenericVirtualPackage::from(vpkg.clone())) + .collect::>() + }) + }, + )??; // Now that we parsed and downloaded all information, construct the packaging problem that we // need to solve. We do this by constructing a `SolverProblem`. This encapsulates all the @@ -183,7 +188,11 @@ pub async fn create_environment( // Next, use a solver to solve this specific problem. This provides us with all the operations // we need to apply to our environment to bring it up to date. - let required_packages = wrap_in_progress("solving", move || Solver.solve(solver_task))??; + let required_packages = wrap_in_progress( + "solving", + &tool_configuration.fancy_log_handler, + move || Solver.solve(solver_task), + )??; install_packages( &required_packages, @@ -221,7 +230,7 @@ pub async fn install_packages( target_prefix, cache_dir, tool_configuration.client.clone(), - tool_configuration.multi_progress_indicator.clone(), + tool_configuration.fancy_log_handler.clone(), ) .await?; tracing::info!( @@ -244,7 +253,7 @@ async fn execute_transaction( target_prefix: &Path, cache_dir: &Path, download_client: ClientWithMiddleware, - multi_progress: indicatif::MultiProgress, + fancy_log_handler: LoggingOutputHandler, ) -> anyhow::Result<()> { // Open the package cache let package_cache = PackageCache::new(cache_dir.join("pkgs")); @@ -266,9 +275,9 @@ async fn execute_transaction( .filter(|op| op.record_to_install().is_some()) .count(); let download_pb = if total_packages_to_download > 0 { - let pb = multi_progress.add( + let pb = fancy_log_handler.add_progress_bar( indicatif::ProgressBar::new(total_packages_to_download as u64) - .with_style(default_progress_style()?) + .with_style(fancy_log_handler.default_progress_style()) .with_finish(indicatif::ProgressFinish::WithMessage("Done!".into())) .with_prefix("downloading"), ); @@ -280,9 +289,9 @@ async fn execute_transaction( // Create a progress bar to track all operations. let total_operations = transaction.operations.len(); - let link_pb = multi_progress.add( + let link_pb = fancy_log_handler.add_progress_bar( indicatif::ProgressBar::new(total_operations as u64) - .with_style(default_progress_style()?) + .with_style(fancy_log_handler.default_progress_style()) .with_finish(indicatif::ProgressFinish::WithMessage("Done!".into())) .with_prefix("linking"), ); @@ -298,6 +307,7 @@ async fn execute_transaction( let download_pb = download_pb.as_ref(); let link_pb = &link_pb; let install_options = &install_options; + let fancy_log_handler = fancy_log_handler.clone(); async move { execute_operation( target_prefix, @@ -308,6 +318,7 @@ async fn execute_transaction( link_pb, op, install_options, + fancy_log_handler, ) .await } @@ -331,6 +342,7 @@ async fn execute_operation( link_pb: &ProgressBar, op: TransactionOperation, install_options: &InstallOptions, + fancy_log_handler: LoggingOutputHandler, ) -> anyhow::Result<()> { // Determine the package to install let install_record = op.record_to_install(); @@ -361,7 +373,7 @@ async fn execute_operation( if let Some(pb) = download_pb { pb.inc(1); if pb.length() == Some(pb.position()) { - pb.set_style(finished_progress_style()?); + pb.set_style(fancy_log_handler.finished_progress_style()); } } @@ -390,7 +402,7 @@ async fn execute_operation( // Increment the link progress bar since we finished a step! link_pb.inc(1); if link_pb.length() == Some(link_pb.position()) { - link_pb.set_style(finished_progress_style()?); + link_pb.set_style(fancy_log_handler.finished_progress_style()); } Ok(()) @@ -501,11 +513,12 @@ async fn remove_package_from_environment( /// Displays a spinner with the given message while running the specified function to completion. fn wrap_in_progress T>( msg: impl Into>, + fancy_log_handler: &LoggingOutputHandler, func: F, ) -> Result { let pb = ProgressBar::new_spinner(); pb.enable_steady_tick(Duration::from_millis(100)); - pb.set_style(long_running_progress_style()?); + pb.set_style(fancy_log_handler.long_running_progress_style()); pb.set_message(msg); let result = func(); pb.finish_and_clear(); @@ -519,15 +532,15 @@ async fn fetch_repo_data_records_with_progress( platform: Platform, repodata_cache: &Path, client: ClientWithMiddleware, - multi_progress: indicatif::MultiProgress, + fancy_log_handler: LoggingOutputHandler, allow_not_found: bool, ) -> anyhow::Result> { // Create a progress bar - let progress_bar = multi_progress.add( + let progress_bar = fancy_log_handler.add_progress_bar( indicatif::ProgressBar::new(1) .with_finish(indicatif::ProgressFinish::AndLeave) .with_prefix(format!("{}/{platform}", friendly_channel_name(&channel))) - .with_style(default_bytes_style()?), + .with_style(fancy_log_handler.default_bytes_style()), ); progress_bar.enable_steady_tick(Duration::from_millis(100)); @@ -551,11 +564,11 @@ async fn fetch_repo_data_records_with_progress( let result = match result { Err(e) => { if matches!(e, FetchRepoDataError::NotFound(_)) && allow_not_found { - progress_bar.set_style(errored_progress_style()?); + progress_bar.set_style(fancy_log_handler.errored_progress_style()); progress_bar.finish_with_message("Not Found"); return Ok(None); } - progress_bar.set_style(errored_progress_style()?); + progress_bar.set_style(fancy_log_handler.errored_progress_style()); progress_bar.finish_with_message("404 not found"); return Err(e.into()); } @@ -563,7 +576,7 @@ async fn fetch_repo_data_records_with_progress( }; // Notify that we are deserializing - progress_bar.set_style(deserializing_progress_style()?); + progress_bar.set_style(fancy_log_handler.deserializing_progress_style()); progress_bar.set_message("Deserializing.."); // Deserialize the data. This is a hefty blocking operation so we spawn it as a tokio blocking @@ -579,12 +592,12 @@ async fn fetch_repo_data_records_with_progress( result.cache_result, CacheResult::CacheHit | CacheResult::CacheHitAfterFetch ); - progress_bar.set_style(finished_progress_style()?); + progress_bar.set_style(fancy_log_handler.finished_progress_style()); progress_bar.finish_with_message(if is_cache_hit { "Using cache" } else { "Done" }); Ok(Some(repodata)) } Ok(Err(err)) => { - progress_bar.set_style(errored_progress_style()?); + progress_bar.set_style(fancy_log_handler.errored_progress_style()); progress_bar.finish_with_message(format!("Error: {:?}", err)); Err(err.into()) } @@ -593,7 +606,7 @@ async fn fetch_repo_data_records_with_progress( std::panic::resume_unwind(panic); } Err(_) => { - progress_bar.set_style(errored_progress_style()?); + progress_bar.set_style(fancy_log_handler.errored_progress_style()); progress_bar.finish_with_message("Canceled..."); // Since the task was cancelled most likely the whole async stack is being cancelled. Err(anyhow::anyhow!("canceled")) @@ -611,64 +624,6 @@ fn friendly_channel_name(channel: &Channel) -> String { .unwrap_or_else(|| channel.canonical_name()) } -/// Returns the style to use for a progressbar that is currently in progress. -pub(crate) fn default_bytes_style() -> Result { - Ok(indicatif::ProgressStyle::default_bar() - .template("{spinner:.green} {prefix:20!} [{elapsed_precise}] [{bar:40!.bright.yellow/dim.white}] {bytes:>8} @ {smoothed_bytes_per_sec:8}")? - .progress_chars("━━╾─") - .with_key( - "smoothed_bytes_per_sec", - |s: &ProgressState, w: &mut dyn Write| match (s.pos(), s.elapsed().as_millis()) { - (pos, elapsed_ms) if elapsed_ms > 0 => { - // TODO: log with tracing? - _ = write!(w, "{}/s", HumanBytes((pos as f64 * 1000_f64 / elapsed_ms as f64) as u64)); - } - _ => { - _ = write!(w, "-"); - }, - }, - )) -} - -/// Returns the style to use for a progressbar that is currently in progress. -fn default_progress_style() -> Result { - Ok(indicatif::ProgressStyle::default_bar() - .template("{spinner:.green} {prefix:20!} [{elapsed_precise}] [{bar:40!.bright.yellow/dim.white}] {pos:>7}/{len:7}")? - .progress_chars("━━╾─")) -} - -/// Returns the style to use for a progressbar that is in Deserializing state. -fn deserializing_progress_style() -> Result { - Ok(indicatif::ProgressStyle::default_bar() - .template("{spinner:.green} {prefix:20!} [{elapsed_precise}] {wide_msg}")? - .progress_chars("━━╾─")) -} - -/// Returns the style to use for a progressbar that is finished. -fn finished_progress_style() -> Result { - Ok(indicatif::ProgressStyle::default_bar() - .template(&format!( - "{} {{prefix:20!}} [{{elapsed_precise}}] {{msg:.bold}}", - console::style(console::Emoji("✔", " ")).green() - ))? - .progress_chars("━━╾─")) -} - -/// Returns the style to use for a progressbar that is in error state. -fn errored_progress_style() -> Result { - Ok(indicatif::ProgressStyle::default_bar() - .template(&format!( - "{} {{prefix:20!}} [{{elapsed_precise}}] {{msg:.bold.red}}", - console::style(console::Emoji("×", " ")).red() - ))? - .progress_chars("━━╾─")) -} - -/// Returns the style to use for a progressbar that is indeterminate and simply shows a spinner. -fn long_running_progress_style() -> Result { - ProgressStyle::with_template("{spinner:.green} {msg}") -} - /// Scans the conda-meta directory of an environment and returns all the [`PrefixRecord`]s found in /// there. async fn find_installed_packages( diff --git a/src/source/git_source.rs b/src/source/git_source.rs index 72f38eef6..358558ac5 100644 --- a/src/source/git_source.rs +++ b/src/source/git_source.rs @@ -16,7 +16,7 @@ use super::SourceError; /// Fetch the given repository using the host `git` executable. pub fn fetch_repo(repo_path: &Path, url: &Url, rev: &str) -> Result<(), SourceError> { - println!("Fetching repository from {} at {}", url, rev); + tracing::info!("Fetching repository from {} at {}", url, rev); let mut command = git_command("fetch"); let output = command .args([url.to_string().as_str(), rev]) diff --git a/src/source/mod.rs b/src/source/mod.rs index 27377afec..de87888ef 100644 --- a/src/source/mod.rs +++ b/src/source/mod.rs @@ -6,9 +6,9 @@ use std::{ }; use crate::{ + console_utils::LoggingOutputHandler, metadata::Directories, recipe::parser::{GitRev, GitSource, Source}, - render::solver::default_bytes_style, tool_configuration, }; @@ -154,11 +154,7 @@ pub async fn fetch_sources( .to_string_lossy() .contains(".tar") { - extract_tar( - &res, - &dest_dir, - tool_configuration.multi_progress_indicator.clone(), - )?; + extract_tar(&res, &dest_dir, &tool_configuration.fancy_log_handler)?; tracing::info!("Extracted to {:?}", dest_dir); } else if res .file_name() @@ -166,11 +162,7 @@ pub async fn fetch_sources( .to_string_lossy() .ends_with(".zip") { - extract_zip( - &res, - &dest_dir, - tool_configuration.multi_progress_indicator.clone(), - )?; + extract_zip(&res, &dest_dir, &tool_configuration.fancy_log_handler)?; tracing::info!("Extracted zip to {:?}", dest_dir); } else { if let Some(file_name) = src.file_name() { @@ -309,18 +301,16 @@ fn move_extracted_dir(src: &Path, dest: &Path) -> Result<(), SourceError> { fn extract_tar( archive: impl AsRef, target_directory: impl AsRef, - multi_progress_indicator: indicatif::MultiProgress, + log_handler: &LoggingOutputHandler, ) -> Result<(), SourceError> { let archive = archive.as_ref(); let target_directory = target_directory.as_ref(); let len = archive.metadata().map(|m| m.len()).unwrap_or(1); - let progress_bar = multi_progress_indicator.add( + let progress_bar = log_handler.add_progress_bar( indicatif::ProgressBar::new(len) .with_prefix("Extracting tar") - .with_style(default_bytes_style().map_err(|_| { - SourceError::UnknownError("Failed to get progress bar style".to_string()) - })?), + .with_style(log_handler.default_bytes_style()), ); let mut archive = tar::Archive::new(progress_bar.wrap_read(ext_to_compression( @@ -348,19 +338,17 @@ fn extract_tar( fn extract_zip( archive: impl AsRef, target_directory: impl AsRef, - multi_progress_indicator: indicatif::MultiProgress, + log_handler: &LoggingOutputHandler, ) -> Result<(), SourceError> { let archive = archive.as_ref(); let target_directory = target_directory.as_ref(); let len = archive.metadata().map(|m| m.len()).unwrap_or(1); - let progress_bar = multi_progress_indicator.add( + let progress_bar = log_handler.add_progress_bar( indicatif::ProgressBar::new(len) .with_finish(indicatif::ProgressFinish::AndLeave) .with_prefix("Extracting zip") - .with_style(default_bytes_style().map_err(|_| { - SourceError::UnknownError("Failed to get progress bar style".to_string()) - })?), + .with_style(log_handler.default_bytes_style()), ); let mut archive = zip::ZipArchive::new(progress_bar.wrap_read( diff --git a/src/source/url_source.rs b/src/source/url_source.rs index c0a58da4a..00a66ede4 100644 --- a/src/source/url_source.rs +++ b/src/source/url_source.rs @@ -7,7 +7,6 @@ use std::{ use crate::{ recipe::parser::{Checksum, UrlSource}, - render::solver::default_bytes_style, tool_configuration, }; use rattler_digest::{compute_file_digest, Md5}; @@ -139,12 +138,10 @@ pub(crate) async fn url_src( } }; - let progress_bar = tool_configuration.multi_progress_indicator.add( + let progress_bar = tool_configuration.fancy_log_handler.add_progress_bar( indicatif::ProgressBar::new(download_size) .with_prefix("Downloading") - .with_style(default_bytes_style().map_err(|_| { - SourceError::UnknownError("Failed to get progress bar style".to_string()) - })?), + .with_style(tool_configuration.fancy_log_handler.default_bytes_style()), ); progress_bar.set_message( source diff --git a/src/tool_configuration.rs b/src/tool_configuration.rs index cefa6439d..b24e19d08 100644 --- a/src/tool_configuration.rs +++ b/src/tool_configuration.rs @@ -3,14 +3,14 @@ use std::{path::PathBuf, sync::Arc}; +use crate::console_utils::LoggingOutputHandler; use rattler_networking::{authentication_storage, AuthenticationMiddleware, AuthenticationStorage}; use reqwest_middleware::ClientWithMiddleware; - /// Global configuration for the build #[derive(Clone, Debug)] pub struct Configuration { /// If set to a value, a progress bar will be shown - pub multi_progress_indicator: indicatif::MultiProgress, + pub fancy_log_handler: LoggingOutputHandler, /// The authenticated reqwest download client to use pub client: ClientWithMiddleware, @@ -59,7 +59,7 @@ impl Default for Configuration { fn default() -> Self { let auth_storage = AuthenticationStorage::default(); Self { - multi_progress_indicator: indicatif::MultiProgress::new(), + fancy_log_handler: LoggingOutputHandler::new(indicatif::MultiProgress::new()), client: reqwest_middleware::ClientBuilder::new( reqwest::Client::builder() .no_gzip()