use std::time::{Duration, Instant};
use crate::error::{OlError, OL_4228_DEADLINE_EXCEEDED};
#[derive(Debug, Clone, Copy)]
pub struct Deadline {
started: Instant,
budget: Duration,
}
impl Deadline {
pub fn from_budget_ms(budget_ms: u64) -> Self {
Self {
started: Instant::now(),
budget: Duration::from_millis(budget_ms),
}
}
pub fn from_started(started: Instant, budget: Duration) -> Self {
Self { started, budget }
}
pub fn remaining(&self) -> Result<Duration, OlError> {
let elapsed = self.started.elapsed();
if elapsed >= self.budget {
Err(OlError::new(
OL_4228_DEADLINE_EXCEEDED,
format!(
"deadline exceeded: budget {} ms, elapsed {} ms",
self.budget.as_millis(),
elapsed.as_millis()
),
))
} else {
Ok(self.budget - elapsed)
}
}
pub fn budget(&self) -> Duration {
self.budget
}
pub fn elapsed(&self) -> Duration {
self.started.elapsed()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn fresh_deadline_has_full_budget_remaining() {
let d = Deadline::from_budget_ms(200);
let r = d.remaining().unwrap();
assert!(r > Duration::from_millis(150));
}
#[test]
fn expired_deadline_returns_4228() {
let d = Deadline::from_started(
Instant::now() - Duration::from_secs(1),
Duration::from_millis(100),
);
let err = d.remaining().unwrap_err();
assert_eq!(err.code.code, "OL-4228");
}
#[test]
fn zero_budget_is_immediately_expired() {
let d = Deadline::from_budget_ms(0);
assert!(d.remaining().is_err());
}
}