#![warn(clippy::all, clippy::pedantic)]
#![allow(clippy::new_without_default, clippy::new_without_default)]
#![allow(clippy::useless_attribute, clippy::missing_docs_in_private_items)]
#![allow(clippy::use_self)]
pub use log;
pub use log::Level;
use std::collections::BTreeSet;
use std::sync::{Mutex, MutexGuard, PoisonError};
#[doc(hidden)]
pub struct __MessagesSet {
inner: Mutex<BTreeSet<String>>,
}
impl __MessagesSet {
#[must_use]
pub fn new() -> Self {
Self {
inner: Mutex::new(BTreeSet::new()),
}
}
pub fn lock(
&self,
) -> Result<MutexGuard<BTreeSet<String>>, PoisonError<MutexGuard<BTreeSet<String>>>> {
self.inner.lock()
}
}
#[macro_export]
macro_rules! log_once {
(@CREATE STATIC) => ({
use ::std::sync::Once;
static mut __SEEN_MESSAGES: *const $crate::__MessagesSet = 0 as *const _;
static ONCE: Once = Once::new();
unsafe {
ONCE.call_once(|| {
let singleton = $crate::__MessagesSet::new();
__SEEN_MESSAGES = ::std::mem::transmute(Box::new(singleton));
});
&(*__SEEN_MESSAGES)
}
});
(target: $target:expr, $lvl:expr, $($arg:tt)+) => ({
let message = format!($($arg)+);
#[allow(non_snake_case)]
let __SEEN_MESSAGES = $crate::log_once!(@CREATE STATIC);
let mut seen_messages = __SEEN_MESSAGES.lock().expect("Mutex was poisonned");
let event = String::from(stringify!($target)) + stringify!($lvl) + message.as_ref();
if seen_messages.insert(event) {
$crate::log::log!(target: $target, $lvl, "{}", message);
}
});
($lvl:expr, $($arg:tt)+) => ($crate::log_once!(target: module_path!(), $lvl, $($arg)+));
}
#[macro_export]
macro_rules! error_once {
(target: $target:expr, $($arg:tt)*) => (
$crate::log_once!(target: $target, $crate::Level::Error, $($arg)*);
);
($($arg:tt)*) => (
$crate::log_once!($crate::Level::Error, $($arg)*);
)
}
#[macro_export]
macro_rules! warn_once {
(target: $target:expr, $($arg:tt)*) => (
$crate::log_once!(target: $target, $crate::Level::Warn, $($arg)*);
);
($($arg:tt)*) => (
$crate::log_once!($crate::Level::Warn, $($arg)*);
)
}
#[macro_export]
macro_rules! info_once {
(target: $target:expr, $($arg:tt)*) => (
$crate::log_once!(target: $target, $crate::Level::Info, $($arg)*);
);
($($arg:tt)*) => (
$crate::log_once!($crate::Level::Info, $($arg)*);
)
}
#[macro_export]
macro_rules! debug_once {
(target: $target:expr, $($arg:tt)*) => (
$crate::log_once!(target: $target, $crate::Level::Debug, $($arg)*);
);
($($arg:tt)*) => (
$crate::log_once!($crate::Level::Debug, $($arg)*);
)
}
#[macro_export]
macro_rules! trace_once {
(target: $target:expr, $($arg:tt)*) => (
$crate::log_once!(target: $target, $crate::Level::Trace, $($arg)*);
);
($($arg:tt)*) => (
$crate::log_once!($crate::Level::Trace, $($arg)*);
)
}
#[cfg(test)]
mod tests {
use log::{LevelFilter, Log, Metadata, Record};
use std::cell::Cell;
use std::sync::Once;
struct SimpleLogger;
impl Log for SimpleLogger {
fn enabled(&self, _: &Metadata) -> bool {
true
}
fn log(&self, _: &Record) {}
fn flush(&self) {}
}
static LOGGER: SimpleLogger = SimpleLogger;
#[test]
fn called_once() {
static START: Once = Once::new();
START.call_once(|| {
log::set_logger(&LOGGER).expect("Could not set the logger");
log::set_max_level(LevelFilter::Trace);
});
let counter = Cell::new(0);
let function = || {
counter.set(counter.get() + 1);
counter.get()
};
info_once!("Counter is: {}", function());
assert_eq!(counter.get(), 1);
}
}