use std::collections::HashMap;
use std::sync::Once;
#[cfg(not(target_arch = "wasm32"))]
use std::time::{Duration, Instant};
#[cfg(all(target_arch = "wasm32", not(feature = "webworker")))]
use wasm_bindgen::prelude::*;
#[cfg(all(target_arch = "wasm32", not(feature = "webworker")))]
use web_sys::{window, Performance};
pub struct Timer {
#[cfg(not(target_arch = "wasm32"))]
timers: HashMap<String, Instant>,
#[cfg(target_arch = "wasm32")]
timers: HashMap<String, f64>,
#[cfg(all(target_arch = "wasm32", not(feature = "webworker")))]
performance: Performance,
}
impl Timer {
pub fn new() -> Self {
#[cfg(not(target_arch = "wasm32"))]
return Timer {
timers: HashMap::new(),
};
#[cfg(all(target_arch = "wasm32", not(feature = "webworker")))]
return Timer {
timers: HashMap::new(),
performance: window().unwrap().performance().unwrap(),
};
#[cfg(all(target_arch = "wasm32", feature = "webworker"))]
return Timer {
timers: HashMap::new(),
};
}
pub fn time(&mut self, label: &str) {
#[cfg(not(target_arch = "wasm32"))]
self.timers.insert(label.to_string(), Instant::now());
#[cfg(all(target_arch = "wasm32", not(feature = "webworker")))]
self.timers
.insert(label.to_string(), self.performance.now());
}
pub fn time_log(&self, label: &str, silent: bool) -> f64 {
#[cfg(not(target_arch = "wasm32"))]
if let Some(start_time) = self.timers.get(label) {
let duration = start_time.elapsed();
let ms = Self::duration_to_ms(duration);
if !silent {
println!("{}: {:.3}ms", label, ms);
}
ms
} else {
eprintln!("Timer '{}' does not exist", label);
0.0
}
#[cfg(all(target_arch = "wasm32", not(feature = "webworker")))]
if let Some(start_time) = self.timers.get(label) {
let ms = self.performance.now() - start_time;
if !silent {
web_sys::console::log_1(&format!("{}: {:.3}ms", label, ms).into());
}
ms
} else {
web_sys::console::error_1(&format!("Timer '{}' does not exist", label).into());
0.0
}
#[cfg(all(target_arch = "wasm32", feature = "webworker"))]
0.0
}
pub fn time_end(&mut self, label: &str, silent: bool) -> f64 {
#[cfg(not(target_arch = "wasm32"))]
if let Some(start_time) = self.timers.remove(label) {
let duration = start_time.elapsed();
let ms = Self::duration_to_ms(duration);
if !silent {
println!("{}: {:.3}ms", label, ms);
}
ms
} else {
eprintln!("Timer '{}' does not exist", label);
0.0
}
#[cfg(all(target_arch = "wasm32", not(feature = "webworker")))]
if let Some(start_time) = self.timers.remove(label) {
let ms = self.performance.now() - start_time;
if !silent {
web_sys::console::log_1(&format!("{}: {:.3}ms", label, ms).into());
}
ms
} else {
web_sys::console::error_1(&format!("Timer '{}' does not exist", label).into());
0.0
}
#[cfg(all(target_arch = "wasm32", feature = "webworker"))]
0.0
}
pub fn single_instance() -> &'static mut Timer {
static ONCE: Once = Once::new();
static mut SINGLETON: Option<Timer> = None;
unsafe {
ONCE.call_once(|| {
SINGLETON = Some(self::Timer::new());
});
SINGLETON.as_mut().unwrap()
}
}
#[cfg(not(target_arch = "wasm32"))]
fn duration_to_ms(duration: Duration) -> f64 {
(duration.as_secs() as f64) * 1000.0 + (duration.subsec_nanos() as f64) / 1_000_000.0
}
}
impl Default for Timer {
fn default() -> Self {
Self::new()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[cfg(not(target_arch = "wasm32"))]
use std::thread::sleep;
#[cfg(not(target_arch = "wasm32"))]
use std::time::Duration;
#[test]
fn test_timer_new() {
let timer = Timer::default();
assert!(timer.timers.is_empty());
}
#[test]
fn test_timer_time() {
let mut timer = Timer::new();
timer.time("test");
assert!(timer.timers.contains_key("test"));
}
#[test]
#[cfg(not(target_arch = "wasm32"))]
fn test_timer_time_log() {
let mut timer = Timer::new();
timer.time("test_time_log");
sleep(Duration::from_millis(10));
let ms = timer.time_log("test_time_log", false);
assert!(ms > 10.0 && ms < 15.0);
}
#[test]
#[cfg(not(target_arch = "wasm32"))]
fn test_timer_time_end() {
let mut timer = Timer::new();
timer.time("test_time_end");
sleep(Duration::from_millis(10));
timer.time_end("test_time_end", false);
assert!(!timer.timers.contains_key("test"));
}
#[test]
#[cfg(not(target_arch = "wasm32"))]
fn test_duration_to_ms() {
let duration = Duration::from_millis(1234);
assert_eq!(Timer::duration_to_ms(duration), 1234.0);
}
}