mielin_cli/
timeout.rs

1//! Timeout utilities for command execution
2
3use anyhow::{Context, Result};
4use std::future::Future;
5use std::time::Duration;
6
7/// Execute a future with a timeout
8pub async fn with_timeout<F, T>(future: F, timeout_secs: u64) -> Result<T>
9where
10    F: Future<Output = Result<T>>,
11{
12    let timeout_duration = Duration::from_secs(timeout_secs);
13
14    tokio::time::timeout(timeout_duration, future)
15        .await
16        .context("Operation timed out")?
17}
18
19/// Execute a future with a configurable timeout from config
20pub async fn with_config_timeout<F, T>(future: F) -> Result<T>
21where
22    F: Future<Output = Result<T>>,
23{
24    let config = crate::config::Config::load()?;
25    with_timeout(future, config.cli.command_timeout_secs).await
26}
27
28#[cfg(test)]
29mod tests {
30    use super::*;
31    use std::time::Duration;
32
33    #[tokio::test]
34    async fn test_timeout_succeeds() {
35        let result = with_timeout(
36            async {
37                tokio::time::sleep(Duration::from_millis(10)).await;
38                Ok(42)
39            },
40            1,
41        )
42        .await;
43
44        assert!(result.is_ok());
45        assert_eq!(result.unwrap(), 42);
46    }
47
48    #[tokio::test]
49    async fn test_timeout_fails() {
50        let result = with_timeout(
51            async {
52                tokio::time::sleep(Duration::from_secs(5)).await;
53                Ok::<_, anyhow::Error>(42)
54            },
55            1,
56        )
57        .await;
58
59        assert!(result.is_err());
60        let err_msg = result.unwrap_err().to_string();
61        assert!(err_msg.contains("timed out"));
62    }
63}