use std::time::Duration; use libremetaverse_types::compat::{CancellationToken, Task}; use crate::Error; pub struct Repeat; impl Repeat { pub async fn interval_with_time_span_action_cancellation_token_boolean( poll_interval: Duration, action: Box, token: CancellationToken, immediately: Option, ) -> Result<(), Error> { if poll_interval.is_zero() { return Err(Error::Argument); } if immediately.unwrap_or(false) { action(); } loop { tokio::select! { () = tokio::time::sleep(poll_interval) => action(), () = token.cancelled() => return Ok(()), } } } pub async fn interval_with_time_span_func_cancellation_token_boolean( poll_interval: Duration, async_action: Box Task<()> + Send + Sync>, token: CancellationToken, immediately: Option, ) -> Result<(), Error> { if poll_interval.is_zero() { return Err(Error::Argument); } if immediately.unwrap_or(false) { async_action().await?; } loop { tokio::select! { () = tokio::time::sleep(poll_interval) => async_action().await?, () = token.cancelled() => return Ok(()), } } } } #[cfg(test)] mod tests { use std::sync::Arc; use std::sync::atomic::{AtomicUsize, Ordering}; use libremetaverse_types::compat::CancellationTokenSource; use super::*; #[tokio::test] async fn repeats_immediately_and_stops_on_cancellation() { let source = CancellationTokenSource::new(); let token = source.token(); let count = Arc::new(AtomicUsize::new(0)); let action_count = Arc::clone(&count); let cancel = source.clone(); Repeat::interval_with_time_span_action_cancellation_token_boolean( Duration::from_millis(1), Box::new(move || { if action_count.fetch_add(1, Ordering::Relaxed) >= 2 { cancel.cancel(); } }), token, Some(true), ) .await .unwrap(); assert_eq!(count.load(Ordering::Relaxed), 3); } }