Skip to main content

rs_zero/resil/
timeout.rs

1use std::{future::Future, time::Duration};
2
3use thiserror::Error;
4
5/// Error returned when a future exceeds the configured timeout.
6#[derive(Debug, Error, PartialEq, Eq)]
7#[error("operation timed out after {duration:?}")]
8pub struct TimeoutError {
9    /// Configured timeout duration.
10    pub duration: Duration,
11}
12
13/// Runs a future with a timeout.
14pub async fn run_with_timeout<F, T>(duration: Duration, future: F) -> Result<T, TimeoutError>
15where
16    F: Future<Output = T>,
17{
18    tokio::time::timeout(duration, future)
19        .await
20        .map_err(|_| TimeoutError { duration })
21}
22
23#[cfg(test)]
24mod tests {
25    use super::run_with_timeout;
26    use std::time::Duration;
27
28    #[tokio::test]
29    async fn returns_value_before_timeout() {
30        let value = run_with_timeout(Duration::from_millis(50), async { 7 })
31            .await
32            .expect("value");
33
34        assert_eq!(value, 7);
35    }
36
37    #[tokio::test]
38    async fn returns_timeout_error() {
39        let result = run_with_timeout(Duration::from_millis(5), async {
40            tokio::time::sleep(Duration::from_millis(50)).await;
41        })
42        .await;
43
44        assert!(result.is_err());
45    }
46}