#![allow(dead_code)]
use std::sync::atomic::{AtomicBool, AtomicU64, Ordering};
use std::sync::Arc;
use std::time::{Duration, Instant};
use tokio::sync::Notify;
fn format_duration(duration: Duration) -> String {
let total_secs = duration.as_secs();
if total_secs < 60 {
format!("{total_secs}s")
} else if total_secs < 3600 {
let mins = total_secs / 60;
let secs = total_secs % 60;
if secs == 0 {
format!("{mins}m")
} else {
format!("{mins}m {secs}s")
}
} else {
let hrs = total_secs / 3600;
let mins = (total_secs % 3600) / 60;
if mins == 0 {
format!("{hrs}h")
} else {
format!("{hrs}h {mins}m")
}
}
}
pub struct ElapsedTime {
start_time: Instant,
is_running: bool,
update_interval_ms: u64,
paused_ms: Duration,
end_time: Option<Instant>,
}
impl ElapsedTime {
pub fn new(
start_time: Instant,
is_running: bool,
update_interval_ms: Option<u64>,
paused_ms: Duration,
end_time: Option<Instant>,
) -> Self {
Self {
start_time,
is_running,
update_interval_ms: update_interval_ms.unwrap_or(1000),
paused_ms,
end_time,
}
}
pub fn from_unix_ms(
start_time_ms: u64,
is_running: bool,
update_interval_ms: Option<u64>,
paused_ms: u64,
end_time_ms: Option<u64>,
) -> Self {
let now = std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.unwrap()
.as_millis() as u64;
let elapsed_since_start = now.saturating_sub(start_time_ms);
let start =
Instant::now().checked_sub(Duration::from_millis(elapsed_since_start)).unwrap_or(Instant::now());
let end = end_time_ms.map(|ms| {
let duration = ms.saturating_sub(start_time_ms);
start.checked_add(Duration::from_millis(duration)).unwrap_or(Instant::now())
});
Self {
start_time: start,
is_running,
update_interval_ms: update_interval_ms.unwrap_or(1000),
paused_ms: Duration::from_millis(paused_ms),
end_time: end,
}
}
pub fn get(&self) -> String {
let end = self.end_time.unwrap_or(Instant::now());
let elapsed = end
.duration_since(self.start_time)
.saturating_sub(self.paused_ms);
format_duration(elapsed)
}
pub fn subscribe(&self) -> Option<Arc<Notify>> {
if !self.is_running {
return None;
}
let notify = Arc::new(Notify::new());
let interval_ms = self.update_interval_ms;
let notify_clone = Arc::clone(¬ify);
tokio::spawn(async move {
let mut interval = tokio::time::interval(Duration::from_millis(interval_ms));
loop {
interval.tick().await;
notify_clone.notify_waiters();
}
});
Some(notify)
}
pub fn elapsed_duration(&self) -> Duration {
let end = self.end_time.unwrap_or(Instant::now());
end.duration_since(self.start_time)
.saturating_sub(self.paused_ms)
}
}