use std::future::Future;
use std::time::Duration;
use tokio::time::{timeout, sleep};
pub async fn retry_with_exponential_backoff<F, Fut, T, E>(
mut operation: F,
max_retries: usize,
initial_delay: Duration,
max_delay: Duration,
) -> Result<T, E>
where
F: FnMut() -> Fut,
Fut: Future<Output = Result<T, E>>,
{
let mut delay = initial_delay;
for attempt in 0..=max_retries {
match operation().await {
Ok(result) => return Ok(result),
Err(e) => {
if attempt == max_retries {
return Err(e);
}
sleep(delay).await;
delay = std::cmp::min(delay * 2, max_delay);
}
}
}
unreachable!()
}
pub async fn with_timeout<F, T>(
operation: F,
duration: Duration,
) -> Result<T, TimeoutError>
where
F: Future<Output = T>,
{
timeout(duration, operation)
.await
.map_err(|_| TimeoutError)
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct TimeoutError;
impl std::fmt::Display for TimeoutError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "Operation timed out")
}
}
impl std::error::Error for TimeoutError {}
pub struct Debouncer<T> {
delay: Duration,
last_value: Option<T>,
task_handle: Option<tokio::task::JoinHandle<()>>,
}
impl<T: Clone + Send + 'static> Debouncer<T> {
pub fn new(delay: Duration) -> Self {
Self {
delay,
last_value: None,
task_handle: None,
}
}
pub async fn trigger<F, Fut>(&mut self, value: T, operation: F)
where
F: FnOnce(T) -> Fut + Send + 'static,
Fut: Future<Output = ()> + Send,
{
self.last_value = Some(value.clone());
if let Some(handle) = self.task_handle.take() {
handle.abort();
}
let delay = self.delay;
self.task_handle = Some(tokio::spawn(async move {
sleep(delay).await;
operation(value).await;
}));
}
}
pub struct Throttler {
last_execution: Option<std::time::Instant>,
min_interval: Duration,
}
impl Throttler {
pub fn new(min_interval: Duration) -> Self {
Self {
last_execution: None,
min_interval,
}
}
pub async fn execute<F, Fut, T>(&mut self, operation: F) -> Option<T>
where
F: FnOnce() -> Fut,
Fut: Future<Output = T>,
{
let now = std::time::Instant::now();
if let Some(last) = self.last_execution {
if now.duration_since(last) < self.min_interval {
return None;
}
}
self.last_execution = Some(now);
Some(operation().await)
}
pub async fn execute_with_wait<F, Fut, T>(&mut self, operation: F) -> T
where
F: FnOnce() -> Fut,
Fut: Future<Output = T>,
{
let now = std::time::Instant::now();
if let Some(last) = self.last_execution {
let elapsed = now.duration_since(last);
if elapsed < self.min_interval {
sleep(self.min_interval - elapsed).await;
}
}
self.last_execution = Some(std::time::Instant::now());
operation().await
}
}
pub struct BatchProcessor<T> {
batch_size: usize,
flush_interval: Duration,
items: Vec<T>,
last_flush: std::time::Instant,
}
impl<T: Send + 'static> BatchProcessor<T> {
pub fn new(batch_size: usize, flush_interval: Duration) -> Self {
Self {
batch_size,
flush_interval,
items: Vec::new(),
last_flush: std::time::Instant::now(),
}
}
pub async fn add<F, Fut>(&mut self, item: T, processor: F) -> bool
where
F: FnOnce(Vec<T>) -> Fut,
Fut: Future<Output = ()>,
{
self.items.push(item);
let should_flush = self.items.len() >= self.batch_size ||
self.last_flush.elapsed() >= self.flush_interval;
if should_flush {
self.flush(processor).await;
true
} else {
false
}
}
pub async fn flush<F, Fut>(&mut self, processor: F)
where
F: FnOnce(Vec<T>) -> Fut,
Fut: Future<Output = ()>,
{
if !self.items.is_empty() {
let items = std::mem::take(&mut self.items);
processor(items).await;
self.last_flush = std::time::Instant::now();
}
}
}
pub struct AsyncSemaphore {
permits: tokio::sync::Semaphore,
}
impl AsyncSemaphore {
pub fn new(permits: usize) -> Self {
Self {
permits: tokio::sync::Semaphore::new(permits),
}
}
pub async fn execute<F, Fut, T>(&self, operation: F) -> Result<T, tokio::sync::AcquireError>
where
F: FnOnce() -> Fut,
Fut: Future<Output = T>,
{
let _permit = self.permits.acquire().await?;
Ok(operation().await)
}
pub fn try_execute<F, Fut, T>(&self, operation: F) -> Option<impl Future<Output = T>>
where
F: FnOnce() -> Fut,
Fut: Future<Output = T>,
{
if let Ok(_permit) = self.permits.try_acquire() {
Some(async move {
operation().await
})
} else {
None
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use std::sync::atomic::{AtomicUsize, Ordering};
use std::sync::Arc;
#[tokio::test]
async fn test_retry_with_exponential_backoff() {
let counter = Arc::new(AtomicUsize::new(0));
let counter_clone = counter.clone();
let result = retry_with_exponential_backoff(
move || {
let count = counter_clone.fetch_add(1, Ordering::SeqCst);
async move {
if count < 2 {
Err("not yet")
} else {
Ok("success")
}
}
},
5,
Duration::from_millis(1),
Duration::from_millis(100),
).await;
assert_eq!(result, Ok("success"));
assert_eq!(counter.load(Ordering::SeqCst), 3);
}
#[tokio::test]
async fn test_with_timeout() {
let result = with_timeout(
async { "success" },
Duration::from_millis(100),
).await;
assert_eq!(result, Ok("success"));
let result = with_timeout(
async {
sleep(Duration::from_millis(200)).await;
"too late"
},
Duration::from_millis(100),
).await;
assert!(result.is_err());
}
#[tokio::test]
async fn test_throttler() {
let mut throttler = Throttler::new(Duration::from_millis(50));
let result1 = throttler.execute(|| async { "first" }).await;
assert_eq!(result1, Some("first"));
let result2 = throttler.execute(|| async { "second" }).await;
assert_eq!(result2, None);
sleep(Duration::from_millis(60)).await;
let result3 = throttler.execute(|| async { "third" }).await;
assert_eq!(result3, Some("third"));
}
#[tokio::test]
async fn test_batch_processor() {
let processed = Arc::new(AtomicUsize::new(0));
let processed_clone = processed.clone();
let mut processor = BatchProcessor::new(3, Duration::from_millis(100));
let flushed1 = processor.add("item1", |items| {
let processed = processed_clone.clone();
async move {
processed.fetch_add(items.len(), Ordering::SeqCst);
}
}).await;
assert!(!flushed1);
let flushed2 = processor.add("item2", |items| {
let processed = processed_clone.clone();
async move {
processed.fetch_add(items.len(), Ordering::SeqCst);
}
}).await;
assert!(!flushed2);
let flushed3 = processor.add("item3", |items| {
let processed = processed_clone.clone();
async move {
processed.fetch_add(items.len(), Ordering::SeqCst);
}
}).await;
assert!(flushed3);
assert_eq!(processed.load(Ordering::SeqCst), 3);
}
#[tokio::test]
async fn test_async_semaphore() {
let semaphore = Arc::new(AsyncSemaphore::new(2));
let counter = Arc::new(AtomicUsize::new(0));
let mut handles = Vec::new();
for _ in 0..5 {
let semaphore = semaphore.clone();
let counter = counter.clone();
let handle = tokio::spawn(async move {
semaphore.execute(|| async {
counter.fetch_add(1, Ordering::SeqCst);
sleep(Duration::from_millis(10)).await;
counter.fetch_sub(1, Ordering::SeqCst);
}).await.unwrap();
});
handles.push(handle);
}
sleep(Duration::from_millis(5)).await;
assert!(counter.load(Ordering::SeqCst) <= 2);
for handle in handles {
handle.await.unwrap();
}
assert_eq!(counter.load(Ordering::SeqCst), 0);
}
}