Include logging utilities form cargo-flash/embed into the cli utils as they are the same
diff --git a/probe-rs-cli-util/Cargo.toml b/probe-rs-cli-util/Cargo.toml index 8382f5d..2bf1d38 100644 --- a/probe-rs-cli-util/Cargo.toml +++ b/probe-rs-cli-util/Cargo.toml
@@ -17,7 +17,11 @@ [dependencies] structopt = "0.3.16" anyhow = "1.0" +indicatif = "0.15.0" +env_logger = "0.8.1" log = "0.4.0" +lazy_static = "1.4.0" +colored = "2.0.0" probe-rs = { version = "0.9.0", path = "../probe-rs" } cargo_toml = "0.8.1" serde = { version = "1.0.115", features = [ "derive" ] }
diff --git a/probe-rs-cli-util/src/lib.rs b/probe-rs-cli-util/src/lib.rs index 43a2296..6ff4559 100644 --- a/probe-rs-cli-util/src/lib.rs +++ b/probe-rs-cli-util/src/lib.rs
@@ -1,3 +1,5 @@ +mod logging; + use anyhow::{anyhow, Context, Result}; use cargo_toml::Manifest; use serde::Deserialize;
diff --git a/probe-rs-cli-util/src/logging.rs b/probe-rs-cli-util/src/logging.rs new file mode 100644 index 0000000..5a50632 --- /dev/null +++ b/probe-rs-cli-util/src/logging.rs
@@ -0,0 +1,138 @@ +use colored::*; +use env_logger::Builder; +use indicatif::ProgressBar; +use log::{Level, LevelFilter}; +use std::{ + fmt, + sync::{ + atomic::{AtomicUsize, Ordering}, + Arc, RwLock, + }, +}; + +static MAX_WINDOW_WIDTH: AtomicUsize = AtomicUsize::new(0); + +lazy_static::lazy_static! { + /// Stores the progress bar for the logging facility. + static ref PROGRESS_BAR: RwLock<Option<Arc<ProgressBar>>> = RwLock::new(None); +} + +struct Padded<T> { + value: T, + width: usize, +} + +impl<T: fmt::Display> fmt::Display for Padded<T> { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "{: <width$}", self.value, width = self.width) + } +} + +fn max_target_width(target: &str) -> usize { + let max_width = MAX_WINDOW_WIDTH.load(Ordering::Relaxed); + if max_width < target.len() { + MAX_WINDOW_WIDTH.store(target.len(), Ordering::Relaxed); + target.len() + } else { + max_width + } +} + +fn colored_level(level: Level) -> ColoredString { + match level { + Level::Trace => "TRACE".magenta().bold(), + Level::Debug => "DEBUG".blue().bold(), + Level::Info => " INFO".green().bold(), + Level::Warn => " WARN".yellow().bold(), + Level::Error => "ERROR".red().bold(), + } +} + +/// Initialize the logger. +/// +/// There are two sources of log level configuration: +/// +/// - The config file can define the default log level through `general.log_level`. +/// - The user can set the `RUST_LOG` env var, which overrides the log level from the config. +/// +/// The config file only accepts a log level, while the `RUST_LOG` variable +/// supports the full `env_logger` syntax, including filtering by crate and +/// module. +pub fn init(level: Option<Level>) { + let mut builder = Builder::new(); + + // First, apply log level from the config + if let Some(level) = level { + builder.filter_level(level.to_level_filter()); + } else { + builder.filter_level(LevelFilter::Warn); + } + + // Then override that with the `RUST_LOG` env var, if set + if let Ok(s) = ::std::env::var("RUST_LOG") { + builder.parse_filters(&s); + } + + // Custom log format + builder.format(move |f, record| { + let target = record.target(); + let max_width = max_target_width(target); + + let level = colored_level(record.level()); + + let mut style = f.style(); + let target = style.set_bold(true).value(Padded { + value: target, + width: max_width, + }); + + let guard = PROGRESS_BAR.write().unwrap(); + if let Some(pb) = &*guard { + pb.println(format!(" {} {} > {}", level, target, record.args())); + } else { + println!(" {} {} > {}", level, target, record.args()); + } + + Ok(()) + }); + + builder.init(); +} + +/// Sets the current progress bar in store for the logging facility. +pub fn set_progress_bar(progress: Arc<ProgressBar>) { + let mut guard = PROGRESS_BAR.write().unwrap(); + *guard = Some(progress); +} + +/// Clears current progress bar in store for the logging facility. +pub fn clear_progress_bar() { + let mut guard = PROGRESS_BAR.write().unwrap(); + *guard = None; +} + +/// Writes an error to the log. +/// This can be used for unwraps/eprintlns/etc. +pub fn eprintln(message: impl AsRef<str>) { + let guard = PROGRESS_BAR.write().unwrap(); + + match guard.as_ref() { + Some(pb) if !pb.is_finished() => { + pb.println(message.as_ref()); + } + _ => { + eprintln!("{}", message.as_ref()); + } + } +} + +/// Writes an error to the log. +/// This can be used for unwraps/eprintlns/etc. +pub fn println(message: impl AsRef<str>) { + let guard = PROGRESS_BAR.write().unwrap(); + if let Some(pb) = &*guard { + pb.println(message.as_ref()); + } else { + println!("{}", message.as_ref()); + } +}