#![doc = include_str!("../README.md")]
use log::{Level, debug, error, info, log, trace, warn};
use num_format::{Locale, ToFormattedString};
use pluralizer::pluralize;
use std::fmt::{Arguments, Display, Formatter, Result};
use std::sync::{Arc, Mutex};
use std::time::{Duration, Instant};
use sysinfo::{MemoryRefreshKind, Pid, ProcessRefreshKind, ProcessesToUpdate, RefreshKind, System};
mod utils;
pub use utils::*;
pub trait ProgressLog {
type Concurrent: ConcurrentProgressLog;
fn log(&mut self, now: Instant);
fn log_if(&mut self, now: Instant);
fn display_memory(&mut self, display_memory: bool) -> &mut Self;
fn item_name(&mut self, item_name: impl AsRef<str>) -> &mut Self;
fn log_interval(&mut self, log_interval: Duration) -> &mut Self;
fn expected_updates(&mut self, expected_updates: impl Into<Option<usize>>) -> &mut Self;
fn time_unit(&mut self, time_unit: impl Into<Option<TimeUnit>>) -> &mut Self;
fn local_speed(&mut self, local_speed: bool) -> &mut Self;
fn log_target(&mut self, target: impl AsRef<str>) -> &mut Self;
fn log_level(&mut self, log_level: Level) -> &mut Self;
fn add_to_count(&mut self, count: usize);
fn start(&mut self, msg: impl AsRef<str>);
fn update(&mut self);
fn update_with_count(&mut self, count: usize) {
self.update_with_count_and_time(count, Instant::now());
}
fn update_with_count_and_time(&mut self, count: usize, now: Instant);
fn light_update(&mut self);
fn update_and_display(&mut self);
fn stop(&mut self);
fn done(&mut self);
fn done_with_count(&mut self, count: usize);
fn elapsed(&self) -> Option<Duration>;
fn count(&self) -> usize;
fn refresh(&mut self);
fn trace(&self, args: Arguments<'_>);
fn debug(&self, args: Arguments<'_>);
fn info(&self, args: Arguments<'_>);
fn warn(&self, args: Arguments<'_>);
fn error(&self, args: Arguments<'_>);
fn concurrent(&self) -> Self::Concurrent;
}
pub trait ConcurrentProgressLog: ProgressLog + Sync + Send + Clone {
type Duplicated: ConcurrentProgressLog;
fn dup(&self) -> Self::Duplicated;
}
impl<P: ProgressLog> ProgressLog for &mut P {
type Concurrent = P::Concurrent;
fn log(&mut self, now: Instant) {
(**self).log(now);
}
fn log_if(&mut self, now: Instant) {
(**self).log_if(now);
}
fn add_to_count(&mut self, count: usize) {
(**self).add_to_count(count);
}
fn display_memory(&mut self, display_memory: bool) -> &mut Self {
(**self).display_memory(display_memory);
self
}
fn item_name(&mut self, item_name: impl AsRef<str>) -> &mut Self {
(**self).item_name(item_name);
self
}
fn log_interval(&mut self, log_interval: Duration) -> &mut Self {
(**self).log_interval(log_interval);
self
}
fn expected_updates(&mut self, expected_updates: impl Into<Option<usize>>) -> &mut Self {
(**self).expected_updates(expected_updates.into());
self
}
fn time_unit(&mut self, time_unit: impl Into<Option<TimeUnit>>) -> &mut Self {
(**self).time_unit(time_unit.into());
self
}
fn local_speed(&mut self, local_speed: bool) -> &mut Self {
(**self).local_speed(local_speed);
self
}
fn log_target(&mut self, target: impl AsRef<str>) -> &mut Self {
(**self).log_target(target);
self
}
fn log_level(&mut self, log_level: Level) -> &mut Self {
(**self).log_level(log_level);
self
}
fn start(&mut self, msg: impl AsRef<str>) {
(**self).start(msg);
}
fn update(&mut self) {
(**self).update();
}
fn update_with_count_and_time(&mut self, count: usize, now: Instant) {
(**self).update_with_count_and_time(count, now);
}
fn light_update(&mut self) {
(**self).light_update();
}
fn update_and_display(&mut self) {
(**self).update_and_display();
}
fn stop(&mut self) {
(**self).stop();
}
fn done(&mut self) {
(**self).done();
}
fn done_with_count(&mut self, count: usize) {
(**self).done_with_count(count);
}
fn elapsed(&self) -> Option<Duration> {
(**self).elapsed()
}
fn count(&self) -> usize {
(**self).count()
}
fn refresh(&mut self) {
(**self).refresh();
}
fn trace(&self, args: Arguments<'_>) {
(**self).trace(args);
}
fn debug(&self, args: Arguments<'_>) {
(**self).debug(args);
}
fn info(&self, args: Arguments<'_>) {
(**self).info(args);
}
fn warn(&self, args: Arguments<'_>) {
(**self).warn(args);
}
fn error(&self, args: Arguments<'_>) {
(**self).error(args);
}
fn concurrent(&self) -> Self::Concurrent {
(**self).concurrent()
}
}
impl<P: ProgressLog> ProgressLog for Option<P> {
type Concurrent = Option<P::Concurrent>;
fn log(&mut self, now: Instant) {
if let Some(pl) = self {
pl.log(now);
}
}
fn log_if(&mut self, now: Instant) {
if let Some(pl) = self {
pl.log_if(now);
}
}
fn add_to_count(&mut self, count: usize) {
if let Some(pl) = self {
pl.add_to_count(count);
}
}
fn display_memory(&mut self, display_memory: bool) -> &mut Self {
if let Some(pl) = self {
pl.display_memory(display_memory);
}
self
}
fn item_name(&mut self, item_name: impl AsRef<str>) -> &mut Self {
if let Some(pl) = self {
pl.item_name(item_name);
}
self
}
fn log_interval(&mut self, log_interval: Duration) -> &mut Self {
if let Some(pl) = self {
pl.log_interval(log_interval);
}
self
}
fn expected_updates(&mut self, expected_updates: impl Into<Option<usize>>) -> &mut Self {
if let Some(pl) = self {
pl.expected_updates(expected_updates.into());
}
self
}
fn time_unit(&mut self, time_unit: impl Into<Option<TimeUnit>>) -> &mut Self {
if let Some(pl) = self {
pl.time_unit(time_unit.into());
}
self
}
fn local_speed(&mut self, local_speed: bool) -> &mut Self {
if let Some(pl) = self {
pl.local_speed(local_speed);
}
self
}
fn log_target(&mut self, target: impl AsRef<str>) -> &mut Self {
if let Some(pl) = self {
pl.log_target(target);
}
self
}
fn log_level(&mut self, log_level: Level) -> &mut Self {
if let Some(pl) = self {
pl.log_level(log_level);
}
self
}
fn start(&mut self, msg: impl AsRef<str>) {
if let Some(pl) = self {
pl.start(msg);
}
}
fn update(&mut self) {
if let Some(pl) = self {
pl.update();
}
}
fn update_with_count_and_time(&mut self, count: usize, now: Instant) {
if let Some(pl) = self {
pl.update_with_count_and_time(count, now);
}
}
fn light_update(&mut self) {
if let Some(pl) = self {
pl.light_update();
}
}
fn update_and_display(&mut self) {
if let Some(pl) = self {
pl.update_and_display();
}
}
fn stop(&mut self) {
if let Some(pl) = self {
pl.stop();
}
}
fn done(&mut self) {
if let Some(pl) = self {
pl.done();
}
}
fn done_with_count(&mut self, count: usize) {
if let Some(pl) = self {
pl.done_with_count(count);
}
}
fn elapsed(&self) -> Option<Duration> {
self.as_ref().and_then(|pl| pl.elapsed())
}
fn count(&self) -> usize {
self.as_ref().map(|pl| pl.count()).unwrap_or(0)
}
fn refresh(&mut self) {
if let Some(pl) = self {
pl.refresh();
}
}
fn trace(&self, args: Arguments<'_>) {
if let Some(pl) = self {
pl.trace(args);
}
}
fn debug(&self, args: Arguments<'_>) {
if let Some(pl) = self {
pl.debug(args);
}
}
fn info(&self, args: Arguments<'_>) {
if let Some(pl) = self {
pl.info(args);
}
}
fn warn(&self, args: Arguments<'_>) {
if let Some(pl) = self {
pl.warn(args);
}
}
fn error(&self, args: Arguments<'_>) {
if let Some(pl) = self {
pl.error(args);
}
}
fn concurrent(&self) -> Self::Concurrent {
self.as_ref().map(|pl| pl.concurrent())
}
}
impl<P: ConcurrentProgressLog> ConcurrentProgressLog for Option<P> {
type Duplicated = Option<P::Duplicated>;
fn dup(&self) -> Self::Duplicated {
self.as_ref().map(|pl| pl.dup())
}
}
pub struct ProgressLogger {
item_name: String,
items_name: String,
log_interval: Duration,
expected_updates: Option<usize>,
time_unit: Option<TimeUnit>,
local_speed: bool,
log_target: String,
log_level: Level,
start_time: Option<Instant>,
last_log_time: Instant,
next_log_time: Instant,
stop_time: Option<Instant>,
count: usize,
last_count: usize,
system: Option<System>,
pid: Pid,
}
impl Default for ProgressLogger {
fn default() -> Self {
Self {
item_name: "item".into(),
items_name: "items".into(),
log_interval: Duration::from_secs(10),
expected_updates: None,
time_unit: None,
local_speed: false,
log_target: std::env::current_exe()
.ok()
.and_then(|path| {
path.file_name()
.and_then(|s| s.to_owned().into_string().ok())
})
.unwrap_or_else(|| "main".to_string()),
log_level: Level::Info,
start_time: None,
last_log_time: Instant::now(),
next_log_time: Instant::now(),
stop_time: None,
count: 0,
last_count: 0,
system: None,
pid: Pid::from(std::process::id() as usize),
}
}
}
impl Clone for ProgressLogger {
#[allow(clippy::manual_map)]
fn clone(&self) -> Self {
Self {
item_name: self.item_name.clone(),
items_name: self.items_name.clone(),
log_interval: self.log_interval,
time_unit: self.time_unit,
local_speed: self.local_speed,
log_level: self.log_level,
log_target: self.log_target.clone(),
system: match self.system {
Some(_) => Some(System::new_with_specifics(
RefreshKind::nothing().with_memory(MemoryRefreshKind::nothing().with_ram()),
)),
None => None,
},
..ProgressLogger::default()
}
}
}
#[macro_export]
macro_rules! progress_logger {
($($method:ident = $arg:expr),* $(,)?) => {
{
let mut pl = $crate::ProgressLogger::default();
$crate::ProgressLog::log_target(&mut pl, ::std::module_path!());
$(
$crate::ProgressLog::$method(&mut pl, $arg);
)*
pl
}
}
}
impl ProgressLogger {
pub const LIGHT_UPDATE_MASK: usize = (1 << 20) - 1;
fn fmt_timing_speed(&self, f: &mut Formatter<'_>, seconds_per_item: f64) -> Result {
let items_per_second = 1.0 / seconds_per_item;
let time_unit_timing = self
.time_unit
.unwrap_or_else(|| TimeUnit::nice_time_unit(seconds_per_item));
let time_unit_speed = self
.time_unit
.unwrap_or_else(|| TimeUnit::nice_speed_unit(seconds_per_item));
f.write_fmt(format_args!(
"{:.2} {}/{}, {:.2} {}/{}",
items_per_second * time_unit_speed.as_seconds(),
self.items_name,
time_unit_speed.label(),
seconds_per_item / time_unit_timing.as_seconds(),
time_unit_timing.label(),
self.item_name
))?;
Ok(())
}
}
impl ProgressLog for ProgressLogger {
type Concurrent = ConcurrentWrapper<Self>;
fn log(&mut self, now: Instant) {
self.refresh();
log!(target: &self.log_target, self.log_level, "{}", self);
self.last_count = self.count;
self.last_log_time = now;
self.next_log_time = now + self.log_interval;
}
fn log_if(&mut self, now: Instant) {
if self.next_log_time <= now {
self.log(now);
}
}
fn add_to_count(&mut self, count: usize) {
self.count += count;
}
fn display_memory(&mut self, display_memory: bool) -> &mut Self {
match (display_memory, &self.system) {
(true, None) => {
self.system = Some(System::new_with_specifics(
RefreshKind::nothing().with_memory(MemoryRefreshKind::nothing().with_ram()),
));
}
(false, Some(_)) => {
self.system = None;
}
_ => (),
}
self
}
fn item_name(&mut self, item_name: impl AsRef<str>) -> &mut Self {
self.item_name = item_name.as_ref().into();
self.items_name = pluralize(item_name.as_ref(), 2, false);
self
}
fn log_interval(&mut self, log_interval: Duration) -> &mut Self {
self.log_interval = log_interval;
self
}
fn expected_updates(&mut self, expected_updates: impl Into<Option<usize>>) -> &mut Self {
self.expected_updates = expected_updates.into();
self
}
fn time_unit(&mut self, time_unit: impl Into<Option<TimeUnit>>) -> &mut Self {
self.time_unit = time_unit.into();
self
}
fn local_speed(&mut self, local_speed: bool) -> &mut Self {
self.local_speed = local_speed;
self
}
fn log_target(&mut self, target: impl AsRef<str>) -> &mut Self {
self.log_target = target.as_ref().into();
self
}
fn log_level(&mut self, log_level: Level) -> &mut Self {
self.log_level = log_level;
self
}
fn start(&mut self, msg: impl AsRef<str>) {
let now = Instant::now();
self.start_time = Some(now);
self.stop_time = None;
self.count = 0;
self.last_count = 0;
self.last_log_time = now;
self.next_log_time = now + self.log_interval;
if !msg.as_ref().is_empty() {
log!(target: &self.log_target, self.log_level, "{}", msg.as_ref());
}
}
fn refresh(&mut self) {
if let Some(system) = &mut self.system {
system.refresh_processes_specifics(
ProcessesToUpdate::Some(&[self.pid]),
false,
ProcessRefreshKind::nothing().with_memory(),
);
}
}
fn update(&mut self) {
self.count += 1;
self.log_if(Instant::now());
}
fn update_with_count_and_time(&mut self, count: usize, now: Instant) {
self.count += count;
self.log_if(now);
}
#[inline(always)]
fn light_update(&mut self) {
self.count += 1;
if (self.count & Self::LIGHT_UPDATE_MASK) == 0 {
self.log_if(Instant::now());
}
}
fn update_and_display(&mut self) {
self.count += 1;
self.log(Instant::now());
}
fn stop(&mut self) {
self.stop_time = Some(Instant::now());
self.expected_updates = None;
}
fn done(&mut self) {
self.stop();
log!(target: &self.log_target, self.log_level, "Completed.");
self.refresh();
log!(target: &self.log_target, self.log_level, "{}", self);
}
fn done_with_count(&mut self, count: usize) {
self.count = count;
self.done();
}
fn elapsed(&self) -> Option<Duration> {
let start_time = self.start_time?;
Some(self.stop_time.unwrap_or_else(Instant::now) - start_time)
}
fn count(&self) -> usize {
self.count
}
fn trace(&self, args: Arguments<'_>) {
trace!(target: &self.log_target, "{}", std::fmt::format(args));
}
fn debug(&self, args: Arguments<'_>) {
debug!(target: &self.log_target, "{}", std::fmt::format(args));
}
fn info(&self, args: Arguments<'_>) {
info!(target: &self.log_target, "{}", std::fmt::format(args));
}
fn warn(&self, args: Arguments<'_>) {
warn!(target: &self.log_target, "{}", std::fmt::format(args));
}
fn error(&self, args: Arguments<'_>) {
error!(target: &self.log_target, "{}", std::fmt::format(args));
}
fn concurrent(&self) -> Self::Concurrent {
ConcurrentWrapper::wrap(self.clone())
}
}
impl Display for ProgressLogger {
fn fmt(&self, f: &mut Formatter<'_>) -> Result {
if let Some(start_time) = self.start_time {
let count_fmtd = if self.time_unit.is_none() {
self.count.to_formatted_string(&Locale::en)
} else {
self.count.to_string()
};
if let Some(stop_time) = self.stop_time {
let elapsed = stop_time - start_time;
let seconds_per_item = elapsed.as_secs_f64() / self.count as f64;
f.write_fmt(format_args!(
"Elapsed: {}",
TimeUnit::pretty_print(elapsed.as_millis())
))?;
if self.count != 0 {
f.write_fmt(format_args!(
" [{} {}, ",
count_fmtd,
if self.count == 1 {
&self.item_name
} else {
&self.items_name
}
))?;
self.fmt_timing_speed(f, seconds_per_item)?;
f.write_fmt(format_args!("]"))?
}
} else {
let now = Instant::now();
let elapsed = now - start_time;
f.write_fmt(format_args!(
"{} {}, {}, ",
count_fmtd,
if self.count == 1 {
&self.item_name
} else {
&self.items_name
},
TimeUnit::pretty_print(elapsed.as_millis()),
))?;
let seconds_per_item = elapsed.as_secs_f64() / self.count as f64;
self.fmt_timing_speed(f, seconds_per_item)?;
if let Some(expected_updates) = self.expected_updates {
let millis_to_end: u128 = (expected_updates.saturating_sub(self.count) as u128
* elapsed.as_millis())
/ (self.count as u128 + 1);
f.write_fmt(format_args!(
"; {:.2}% done, {} to end",
100.0 * self.count as f64 / expected_updates as f64,
TimeUnit::pretty_print(millis_to_end)
))?;
}
if self.local_speed && self.stop_time.is_none() {
f.write_fmt(format_args!(" ["))?;
let elapsed = now - self.last_log_time;
let seconds_per_item =
elapsed.as_secs_f64() / (self.count - self.last_count) as f64;
self.fmt_timing_speed(f, seconds_per_item)?;
f.write_fmt(format_args!("]"))?;
}
}
if let Some(system) = &self.system {
f.write_fmt(format_args!(
"; res/vir/avail/free/total mem {}/{}/{}B/{}B/{}B",
system
.process(self.pid)
.map(|process| humanize(process.memory() as _) + "B")
.unwrap_or("N/A".to_string()),
system
.process(self.pid)
.map(|process| humanize(process.virtual_memory() as _) + "B")
.unwrap_or("N/A".to_string()),
humanize(system.available_memory() as _),
humanize(system.free_memory() as _),
humanize(system.total_memory() as _)
))?;
}
Ok(())
} else {
write!(f, "ProgressLogger not started")
}
}
}
pub struct ConcurrentWrapper<P: ProgressLog = ProgressLogger> {
inner: Arc<Mutex<P>>,
local_count: u32,
threshold: u32,
}
impl Default for ConcurrentWrapper {
fn default() -> Self {
Self {
inner: Arc::new(Mutex::new(ProgressLogger::default())),
local_count: 0,
threshold: Self::DEFAULT_THRESHOLD,
}
}
}
impl<P: ProgressLog + Clone> Clone for ConcurrentWrapper<P> {
fn clone(&self) -> Self {
Self {
inner: self.inner.clone(),
local_count: 0,
threshold: self.threshold,
}
}
}
#[macro_export]
macro_rules! concurrent_progress_logger {
($($method:ident = $arg:expr),* $(,)?) => {
{
let mut cpl = $crate::ConcurrentWrapper::default();
$crate::ProgressLog::log_target(&mut cpl, ::std::module_path!());
$(
$crate::ProgressLog::$method(&mut cpl, $arg);
)*
cpl
}
}
}
impl ConcurrentWrapper {
pub fn new() -> Self {
Self::with_threshold(Self::DEFAULT_THRESHOLD)
}
pub fn with_threshold(threshold: u32) -> Self {
Self {
inner: Arc::new(Mutex::new(ProgressLogger::default())),
local_count: 0,
threshold,
}
}
}
impl<P: ProgressLog> ConcurrentWrapper<P> {
pub const DEFAULT_THRESHOLD: u32 = 1 << 15;
pub const LIGHT_UPDATE_MASK: u32 = (1 << 10) - 1;
pub fn threshold(&mut self, threshold: u32) -> &mut Self {
self.threshold = threshold;
self
}
pub fn wrap(inner: P) -> Self {
Self {
inner: Arc::new(Mutex::new(inner)),
local_count: 0,
threshold: Self::DEFAULT_THRESHOLD,
}
}
pub fn wrap_with_threshold(inner: P, threshold: u32) -> Self {
Self {
inner: Arc::new(Mutex::new(inner)),
local_count: 0,
threshold,
}
}
pub fn flush(&mut self) {
self.inner
.lock()
.unwrap()
.update_with_count(self.local_count as _);
self.local_count = 0;
}
}
impl<P: ProgressLog + Clone> ConcurrentWrapper<P> {
pub fn dup(&self) -> Self {
Self {
inner: Arc::new(Mutex::new(self.inner.lock().unwrap().clone())),
local_count: 0,
threshold: self.threshold,
}
}
}
impl<P: ProgressLog + Clone + Send> ConcurrentProgressLog for ConcurrentWrapper<P> {
type Duplicated = ConcurrentWrapper<P>;
fn dup(&self) -> Self {
Self {
inner: Arc::new(Mutex::new(self.inner.lock().unwrap().clone())),
local_count: 0,
threshold: self.threshold,
}
}
}
impl<P: ProgressLog + Clone + Send> ProgressLog for ConcurrentWrapper<P> {
type Concurrent = Self;
fn log(&mut self, now: Instant) {
self.inner.lock().unwrap().log(now);
}
fn log_if(&mut self, now: Instant) {
self.inner.lock().unwrap().log_if(now);
}
fn add_to_count(&mut self, count: usize) {
self.inner.lock().unwrap().add_to_count(count);
}
fn display_memory(&mut self, display_memory: bool) -> &mut Self {
self.inner.lock().unwrap().display_memory(display_memory);
self
}
fn item_name(&mut self, item_name: impl AsRef<str>) -> &mut Self {
self.inner.lock().unwrap().item_name(item_name);
self
}
fn log_interval(&mut self, log_interval: Duration) -> &mut Self {
self.inner.lock().unwrap().log_interval(log_interval);
self
}
fn expected_updates(&mut self, expected_updates: impl Into<Option<usize>>) -> &mut Self {
self.inner
.lock()
.unwrap()
.expected_updates(expected_updates.into());
self
}
fn time_unit(&mut self, time_unit: impl Into<Option<TimeUnit>>) -> &mut Self {
self.inner.lock().unwrap().time_unit(time_unit.into());
self
}
fn local_speed(&mut self, local_speed: bool) -> &mut Self {
self.inner.lock().unwrap().local_speed(local_speed);
self
}
fn log_target(&mut self, target: impl AsRef<str>) -> &mut Self {
self.inner.lock().unwrap().log_target(target);
self
}
fn log_level(&mut self, log_level: Level) -> &mut Self {
self.inner.lock().unwrap().log_level(log_level);
self
}
fn start(&mut self, msg: impl AsRef<str>) {
self.inner.lock().unwrap().start(msg);
self.local_count = 0;
}
#[inline]
fn update(&mut self) {
self.update_with_count(1)
}
#[inline]
fn update_with_count_and_time(&mut self, count: usize, _now: Instant) {
self.update_with_count(count);
}
#[inline]
fn update_with_count(&mut self, count: usize) {
match (self.local_count as usize).checked_add(count) {
None => {
{
let now = Instant::now();
let mut inner = self.inner.lock().unwrap();
inner.update_with_count_and_time(self.local_count as _, now);
inner.update_with_count_and_time(count, now);
}
self.local_count = 0;
}
Some(total_count) => {
if total_count >= self.threshold as usize {
self.local_count = 0;
let now = Instant::now();
self.inner
.lock()
.unwrap()
.update_with_count_and_time(total_count, now);
} else {
self.local_count = total_count as u32;
}
}
}
}
#[inline]
fn light_update(&mut self) {
self.local_count += 1;
if (self.local_count & Self::LIGHT_UPDATE_MASK) == 0 && self.local_count >= self.threshold {
let local_count = self.local_count as usize;
self.local_count = 0;
let now = Instant::now();
self.inner
.lock()
.unwrap()
.update_with_count_and_time(local_count, now);
}
}
fn update_and_display(&mut self) {
{
let mut inner = self.inner.lock().unwrap();
inner.add_to_count(self.local_count as _);
inner.update_and_display();
}
self.local_count = 0;
}
fn stop(&mut self) {
self.inner.lock().unwrap().stop();
}
fn done(&mut self) {
self.inner.lock().unwrap().done();
}
fn done_with_count(&mut self, count: usize) {
self.inner.lock().unwrap().done_with_count(count);
}
fn elapsed(&self) -> Option<Duration> {
self.inner.lock().unwrap().elapsed()
}
fn count(&self) -> usize {
self.inner.lock().unwrap().count()
}
fn refresh(&mut self) {
self.inner.lock().unwrap().refresh();
}
fn trace(&self, args: Arguments<'_>) {
self.inner.lock().unwrap().trace(args);
}
fn debug(&self, args: Arguments<'_>) {
self.inner.lock().unwrap().debug(args);
}
fn info(&self, args: Arguments<'_>) {
self.inner.lock().unwrap().info(args);
}
fn warn(&self, args: Arguments<'_>) {
self.inner.lock().unwrap().warn(args);
}
fn error(&self, args: Arguments<'_>) {
self.inner.lock().unwrap().error(args);
}
fn concurrent(&self) -> Self::Concurrent {
self.dup()
}
}
impl<P: ProgressLog> Drop for ConcurrentWrapper<P> {
fn drop(&mut self) {
self.flush();
}
}
impl Display for ConcurrentWrapper {
fn fmt(&self, f: &mut Formatter<'_>) -> Result {
self.inner.lock().unwrap().fmt(f)
}
}
#[macro_export]
macro_rules! no_logging {
() => {
&mut Option::<$crate::ConcurrentWrapper<$crate::ProgressLogger>>::None
};
}
pub mod prelude {
pub use log::Level;
pub use super::{
ConcurrentProgressLog, ConcurrentWrapper, ProgressLog, ProgressLogger, TimeUnit,
concurrent_progress_logger, no_logging, progress_logger,
};
}