#[macro_export]
macro_rules! info_debounce {
($duration:expr, $($arg:tt)*) => {{
use std::sync::LazyLock;
use std::sync::Mutex;
use std::time::Instant;
static LAST: LazyLock<Mutex<Option<Instant>>> =
LazyLock::new(|| Mutex::new(None));
if let Ok(mut last) = LAST.lock() {
let now = Instant::now();
let should_log = last
.map(|l| now.duration_since(l) >= $duration)
.unwrap_or(true);
if should_log {
*last = Some(now);
log::info!($($arg)*);
}
}
}};
}
#[macro_export]
macro_rules! info_once {
($($arg:tt)*) => {
$crate::info_debounce!(std::time::Duration::MAX, $($arg)*)
};
}
#[macro_export]
macro_rules! warn_debounce {
($duration:expr, $($arg:tt)*) => {{
use std::sync::LazyLock;
use std::sync::Mutex;
use std::time::Instant;
static LAST: LazyLock<Mutex<Option<Instant>>> =
LazyLock::new(|| Mutex::new(None));
if let Ok(mut last) = LAST.lock() {
let now = Instant::now();
let should_log = last
.map(|l| now.duration_since(l) >= $duration)
.unwrap_or(true);
if should_log {
*last = Some(now);
log::warn!($($arg)*);
}
}
}};
}
#[macro_export]
macro_rules! warn_once {
($($arg:tt)*) => {
$crate::warn_debounce!(std::time::Duration::MAX, $($arg)*)
};
}
#[macro_export]
macro_rules! error_debounce {
($duration:expr, $($arg:tt)*) => {{
use std::sync::LazyLock;
use std::sync::Mutex;
use std::time::Instant;
static LAST: LazyLock<Mutex<Option<Instant>>> =
LazyLock::new(|| Mutex::new(None));
if let Ok(mut last) = LAST.lock() {
let now = Instant::now();
let should_log = last
.map(|l| now.duration_since(l) >= $duration)
.unwrap_or(true);
if should_log {
*last = Some(now);
log::error!($($arg)*);
}
}
}};
}
#[macro_export]
macro_rules! error_once {
($($arg:tt)*) => {
$crate::error_debounce!(std::time::Duration::MAX, $($arg)*)
};
}
#[macro_export]
macro_rules! debug_debounce {
($duration:expr, $($arg:tt)*) => {{
use std::sync::LazyLock;
use std::sync::Mutex;
use std::time::Instant;
static LAST: LazyLock<Mutex<Option<Instant>>> =
LazyLock::new(|| Mutex::new(None));
if let Ok(mut last) = LAST.lock() {
let now = Instant::now();
let should_log = last
.map(|l| now.duration_since(l) >= $duration)
.unwrap_or(true);
if should_log {
*last = Some(now);
log::debug!($($arg)*);
}
}
}};
}
#[macro_export]
macro_rules! debug_once {
($($arg:tt)*) => {
$crate::debug_debounce!(std::time::Duration::MAX, $($arg)*)
};
}
#[macro_export]
macro_rules! trace_debounce {
($duration:expr, $($arg:tt)*) => {{
use std::sync::LazyLock;
use std::sync::Mutex;
use std::time::Instant;
static LAST: LazyLock<Mutex<Option<Instant>>> =
LazyLock::new(|| Mutex::new(None));
if let Ok(mut last) = LAST.lock() {
let now = Instant::now();
let should_log = last
.map(|l| now.duration_since(l) >= $duration)
.unwrap_or(true);
if should_log {
*last = Some(now);
log::trace!($($arg)*);
}
}
}};
}
#[macro_export]
macro_rules! trace_once {
($($arg:tt)*) => {
$crate::trace_debounce!(std::time::Duration::MAX, $($arg)*)
};
}
#[cfg(test)]
mod tests {
use super::*;
use std::time::Duration;
#[test]
fn test_debounce_fires_first_time() {
info_debounce!(Duration::from_secs(1), "test message");
}
#[test]
fn test_once_macro() {
info_once!("only once");
info_once!("only once");
}
}