use crate::errors::BntoError;
use super::PipelineContext;
#[derive(Debug, Clone, Copy)]
pub(super) struct TimeLimit {
secs: u64,
deadline_ms: u64,
}
impl TimeLimit {
pub(super) fn starting_at(started_ms: u64, secs: Option<u64>) -> Option<Self> {
secs.map(|secs| Self {
secs,
deadline_ms: started_ms.saturating_add(secs.saturating_mul(1000)),
})
}
pub(super) fn check(&self, now_ms: u64) -> Result<(), BntoError> {
if now_ms >= self.deadline_ms {
return Err(BntoError::TimedOut { secs: self.secs });
}
Ok(())
}
}
impl<F: Fn() -> u64 + Copy> PipelineContext<'_, F> {
pub(super) fn check_time_limit(&self) -> Result<(), BntoError> {
match self.time_limit {
Some(limit) => limit.check((self.now_ms)()),
None => Ok(()),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_the_limit_passes_at_its_deadline() {
let limit = TimeLimit::starting_at(1_000, Some(2)).unwrap();
assert!(limit.check(2_999).is_ok());
let err = limit.check(3_000).unwrap_err();
assert_eq!(err.to_string(), "Run timed out after 2s");
}
#[test]
fn test_no_limit_without_seconds() {
assert!(TimeLimit::starting_at(1_000, None).is_none());
}
#[test]
fn test_a_huge_limit_never_overflows() {
let limit = TimeLimit::starting_at(u64::MAX - 5, Some(u64::MAX)).unwrap();
assert!(limit.check(u64::MAX - 1).is_ok());
}
}