use std::time::{Duration, Instant}; #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum WaitOutcome { Satisfied, Timeout, DisabledUnknown, } /// Wait for a predicate with a deadline. Unknown disabled reasons do not spin /// forever — they surface as DisabledUnknown so the caller can re-observe. pub fn wait_until(deadline: Duration, mut ready: F) -> WaitOutcome where F: FnMut() -> Result, { let start = Instant::now(); loop { match ready() { Ok(true) => return WaitOutcome::Satisfied, Ok(false) if start.elapsed() >= deadline => return WaitOutcome::Timeout, Ok(false) => std::thread::sleep(Duration::from_millis(5)), Err(_) => return WaitOutcome::DisabledUnknown, } } } #[cfg(test)] mod tests { use super::*; #[test] fn deadline_stops_an_unready_condition() { let outcome = wait_until(Duration::from_millis(20), || Ok(false)); assert_eq!(outcome, WaitOutcome::Timeout); } #[test] fn unknown_disabled_does_not_spin() { let outcome = wait_until(Duration::from_secs(5), || Err("unknown")); assert_eq!(outcome, WaitOutcome::DisabledUnknown); } }