1use anyhow::{Context, Result};
4use std::future::Future;
5use std::time::Duration;
6
7pub 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
19pub 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}