use std::time::{Duration, Instant};
pub struct TickBudget {
start: Instant,
limit: Duration,
}
impl TickBudget {
pub fn new(limit: Duration) -> Self {
Self {
start: Instant::now(),
limit,
}
}
pub fn unlimited() -> Self {
Self {
start: Instant::now(),
limit: Duration::MAX,
}
}
pub fn remaining(&self) -> Duration {
self.limit.saturating_sub(self.start.elapsed())
}
pub fn is_expired(&self) -> bool {
self.start.elapsed() >= self.limit
}
pub fn elapsed(&self) -> Duration {
self.start.elapsed()
}
pub fn limit(&self) -> Duration {
self.limit
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn unlimited_budget_never_expires() {
let budget = TickBudget::unlimited();
assert!(!budget.is_expired());
assert!(budget.remaining() > Duration::from_secs(1000));
assert_eq!(budget.limit(), Duration::MAX);
}
#[test]
fn new_budget_starts_not_expired() {
let budget = TickBudget::new(Duration::from_millis(100));
assert!(!budget.is_expired());
assert!(budget.remaining() > Duration::ZERO);
assert_eq!(budget.limit(), Duration::from_millis(100));
}
#[test]
fn zero_budget_expires_immediately() {
let budget = TickBudget::new(Duration::ZERO);
assert!(budget.is_expired());
assert_eq!(budget.remaining(), Duration::ZERO);
}
#[test]
fn elapsed_increases() {
let budget = TickBudget::new(Duration::from_secs(10));
let e1 = budget.elapsed();
std::hint::spin_loop();
let e2 = budget.elapsed();
assert!(e2 >= e1);
}
}