commit ec575484da02cf9aa3f9c07eacf6e3b2102d64bb
parent 9c9a3c80e25da686f32079ed6bd05177ad8b14fd
Author: triesap <triesap@radroots.dev>
Date: Mon, 19 Jan 2026 07:09:25 +0000
app-utils: add async iteration helper
- add exe_iter async helper with delay
- provide wasm and native sleep support
- export exe_iter from utils crate
- add unit test for callback counting
Diffstat:
5 files changed, 85 insertions(+), 0 deletions(-)
diff --git a/Cargo.lock b/Cargo.lock
@@ -715,6 +715,16 @@ dependencies = [
]
[[package]]
+name = "gloo-timers"
+version = "0.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bbb143cf96099802033e0d4f4963b19fd2e0b728bcf076cd9cf7f6634f092994"
+dependencies = [
+ "js-sys",
+ "wasm-bindgen",
+]
+
+[[package]]
name = "gloo-utils"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1565,7 +1575,9 @@ dependencies = [
name = "radroots-app-utils"
version = "0.1.0"
dependencies = [
+ "futures",
"getrandom 0.2.17",
+ "gloo-timers",
"js-sys",
"radroots-types",
"serde_json",
diff --git a/Cargo.toml b/Cargo.toml
@@ -21,8 +21,10 @@ leptos = { version = "0.8.5", default-features = false }
wasm-bindgen = "=0.2.100"
serde = { version = "1", features = ["derive"] }
serde_json = "1"
+futures = "0.3"
getrandom = "0.2"
js-sys = "0.3.77"
+gloo-timers = "0.3"
web-sys = { version = "0.3.77", features = [
"Crypto",
"CryptoKey",
diff --git a/crates/utils/Cargo.toml b/crates/utils/Cargo.toml
@@ -15,3 +15,9 @@ getrandom = { workspace = true }
js-sys = { workspace = true }
web-sys = { workspace = true }
serde_json = { workspace = true }
+
+[target.'cfg(target_arch = "wasm32")'.dependencies]
+gloo-timers = { workspace = true }
+
+[dev-dependencies]
+futures = { workspace = true }
diff --git a/crates/utils/src/async/mod.rs b/crates/utils/src/async/mod.rs
@@ -0,0 +1,63 @@
+#![forbid(unsafe_code)]
+
+use crate::error::RadrootsAppUtilsError;
+use std::future::Future;
+use std::time::Duration;
+
+pub async fn exe_iter<F, Fut>(
+ callback: F,
+ num: usize,
+ delay_ms: u64,
+) -> Result<(), RadrootsAppUtilsError>
+where
+ F: Fn() -> Fut,
+ Fut: Future<Output = ()>,
+{
+ if num == 0 {
+ return Ok(());
+ }
+ for index in 0..num {
+ callback().await;
+ if index + 1 < num {
+ sleep_ms(delay_ms).await?;
+ }
+ }
+ Ok(())
+}
+
+#[cfg(target_arch = "wasm32")]
+async fn sleep_ms(delay_ms: u64) -> Result<(), RadrootsAppUtilsError> {
+ gloo_timers::future::TimeoutFuture::new(delay_ms as u32).await;
+ Ok(())
+}
+
+#[cfg(not(target_arch = "wasm32"))]
+async fn sleep_ms(delay_ms: u64) -> Result<(), RadrootsAppUtilsError> {
+ std::thread::sleep(Duration::from_millis(delay_ms));
+ Ok(())
+}
+
+#[cfg(test)]
+mod tests {
+ use super::exe_iter;
+ use std::sync::{Arc, Mutex};
+
+ #[test]
+ fn exe_iter_runs_callback() {
+ let counter = Arc::new(Mutex::new(0usize));
+ let counter_ref = Arc::clone(&counter);
+ let task = exe_iter(
+ move || {
+ let counter_ref = Arc::clone(&counter_ref);
+ async move {
+ let mut guard = counter_ref.lock().expect("lock");
+ *guard += 1;
+ }
+ },
+ 3,
+ 0,
+ );
+ futures::executor::block_on(task).expect("exe_iter");
+ assert_eq!(*counter.lock().expect("lock"), 3);
+ }
+}
diff --git a/crates/utils/src/lib.rs b/crates/utils/src/lib.rs
@@ -2,6 +2,7 @@
pub mod error;
pub mod errors;
+pub mod r#async;
pub mod binary;
pub mod numbers;
pub mod object;
@@ -10,6 +11,7 @@ pub mod text;
pub mod time;
pub mod types;
+pub use r#async::exe_iter;
pub use binary::{as_array_buffer, RadrootsAppArrayBuffer};
pub use errors::{err_msg, handle_err, throw_err, ERR_PREFIX_APP, ERR_PREFIX_UTILS};
pub use numbers::{num_interval_range, num_str, parse_float, parse_int};