use std::future::Future;
use std::time::Duration;
use rocketmq_error::RocketMQError;
use rocketmq_error::RocketMQResult;
use tokio::time::timeout;
pub async fn with_timeout<F, T>(duration: Duration, future: F) -> RocketMQResult<T>
where
F: Future<Output = RocketMQResult<T>>,
{
timeout(duration, future).await.map_err(|_| RocketMQError::Timeout {
operation: "async operation",
timeout_ms: duration.as_millis() as u64,
})?
}
pub async fn with_timeout_named<F, T>(operation: &'static str, duration: Duration, future: F) -> RocketMQResult<T>
where
F: Future<Output = RocketMQResult<T>>,
{
timeout(duration, future).await.map_err(|_| RocketMQError::Timeout {
operation,
timeout_ms: duration.as_millis() as u64,
})?
}
pub async fn with_timeout_millis<F, T>(operation: &'static str, timeout_ms: u64, future: F) -> RocketMQResult<T>
where
F: Future<Output = RocketMQResult<T>>,
{
with_timeout_named(operation, Duration::from_millis(timeout_ms), future).await
}
pub async fn with_timeout_all<F, T>(duration: Duration, futures: Vec<F>) -> RocketMQResult<Vec<T>>
where
F: Future<Output = RocketMQResult<T>> + Send + 'static,
T: Send + 'static,
{
let joined = async move {
let mut results = Vec::with_capacity(futures.len());
for future in futures {
results.push(future.await?);
}
RocketMQResult::Ok(results)
};
with_timeout(duration, joined).await
}
#[cfg(test)]
mod tests {
use super::*;
use std::time::Duration;
#[tokio::test]
async fn test_with_timeout_success() {
async fn quick_operation() -> RocketMQResult<String> {
tokio::time::sleep(Duration::from_millis(10)).await;
Ok("Success".to_string())
}
let result = with_timeout(Duration::from_millis(100), quick_operation()).await;
assert!(result.is_ok());
assert_eq!(result.unwrap(), "Success");
}
#[tokio::test]
async fn test_with_timeout_failure() {
async fn slow_operation() -> RocketMQResult<String> {
tokio::time::sleep(Duration::from_millis(200)).await;
Ok("Success".to_string())
}
let result = with_timeout(Duration::from_millis(50), slow_operation()).await;
assert!(result.is_err());
match result.unwrap_err() {
RocketMQError::Timeout { operation, timeout_ms } => {
assert_eq!(operation, "async operation");
assert_eq!(timeout_ms, 50);
}
_ => panic!("Expected timeout error"),
}
}
#[tokio::test]
async fn test_with_timeout_named() {
async fn slow_operation() -> RocketMQResult<String> {
tokio::time::sleep(Duration::from_millis(200)).await;
Ok("Success".to_string())
}
let result = with_timeout_named("test_operation", Duration::from_millis(50), slow_operation()).await;
assert!(result.is_err());
match result.unwrap_err() {
RocketMQError::Timeout { operation, timeout_ms } => {
assert_eq!(operation, "test_operation");
assert_eq!(timeout_ms, 50);
}
_ => panic!("Expected timeout error"),
}
}
#[tokio::test]
async fn test_with_timeout_millis() {
async fn quick_operation() -> RocketMQResult<String> {
tokio::time::sleep(Duration::from_millis(10)).await;
Ok("Success".to_string())
}
let result = with_timeout_millis("test", 100, quick_operation()).await;
assert!(result.is_ok());
assert_eq!(result.unwrap(), "Success");
}
#[tokio::test]
async fn test_with_timeout_all_success() {
async fn operation(id: usize) -> RocketMQResult<usize> {
tokio::time::sleep(Duration::from_millis(10)).await;
Ok(id)
}
let futures = vec![operation(1), operation(2), operation(3)];
let result = with_timeout_all(Duration::from_millis(100), futures).await;
assert!(result.is_ok());
assert_eq!(result.unwrap(), vec![1, 2, 3]);
}
#[tokio::test]
async fn test_with_timeout_all_timeout() {
async fn slow_operation(id: usize) -> RocketMQResult<usize> {
tokio::time::sleep(Duration::from_millis(100)).await;
Ok(id)
}
let futures = vec![slow_operation(1), slow_operation(2), slow_operation(3)];
let result = with_timeout_all(Duration::from_millis(50), futures).await;
assert!(result.is_err());
match result.unwrap_err() {
RocketMQError::Timeout { .. } => {}
_ => panic!("Expected timeout error"),
}
}
#[tokio::test]
async fn test_with_timeout_propagates_error() {
async fn failing_operation() -> RocketMQResult<String> {
Err(RocketMQError::Internal("Test error".to_string()))
}
let result = with_timeout(Duration::from_millis(100), failing_operation()).await;
assert!(result.is_err());
match result.unwrap_err() {
RocketMQError::Internal(msg) => {
assert_eq!(msg, "Test error");
}
_ => panic!("Expected internal error"),
}
}
}