use std::future::Future;
use crate::policy::Policy;
#[derive(Clone)]
pub struct Wrap<O, I> {
pub outer: O,
pub inner: I,
}
impl<O, I> Wrap<O, I> {
pub fn new(outer: O, inner: I) -> Self {
Self { outer, inner }
}
}
#[async_trait::async_trait]
impl<O, I, E> Policy<E> for Wrap<O, I>
where
O: Policy<E>,
I: Policy<E>,
E: Send + Sync,
{
async fn execute<F, Fut, T>(&self, f: F) -> Result<T, E>
where
F: Fn() -> Fut + Send + Sync,
Fut: Future<Output = Result<T, E>> + Send,
T: Send,
{
self.outer.execute(|| self.inner.execute(&f)).await
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::retry::RetryPolicy;
use crate::timeout::TimeoutPolicy;
use crate::error::DoOverError;
use std::time::Duration;
use std::sync::atomic::{AtomicUsize, Ordering};
use std::sync::Arc;
#[tokio::test]
async fn test_wrap_retry_and_timeout() {
let attempts = Arc::new(AtomicUsize::new(0));
let attempts_clone = Arc::clone(&attempts);
let retry = RetryPolicy::fixed(2, Duration::from_millis(10));
let timeout = TimeoutPolicy::new(Duration::from_secs(1));
let wrapped = Wrap::new(retry, timeout);
let result: Result<String, DoOverError<std::io::Error>> = wrapped
.execute(|| {
let a = Arc::clone(&attempts_clone);
async move {
let count = a.fetch_add(1, Ordering::SeqCst);
if count < 1 {
Err(DoOverError::Inner(std::io::Error::new(
std::io::ErrorKind::Other,
"temporary failure",
)))
} else {
Ok("success".to_string())
}
}
})
.await;
assert!(result.is_ok());
assert_eq!(result.unwrap(), "success");
assert_eq!(attempts.load(Ordering::SeqCst), 2);
}
#[tokio::test]
async fn test_wrap_composition() {
let timeout = TimeoutPolicy::new(Duration::from_millis(100));
let retry = RetryPolicy::fixed(1, Duration::from_millis(10));
let wrapped = Wrap::new(timeout, retry);
let result: Result<(), DoOverError<()>> = wrapped
.execute(|| async {
tokio::time::sleep(Duration::from_millis(200)).await;
Ok(())
})
.await;
assert!(matches!(result, Err(DoOverError::Timeout)));
}
}