diff --git a/Cargo.toml b/Cargo.toml index b61b271..b660ccd 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -10,11 +10,10 @@ repository = "https://github.com/softprops/again" keywords = ["retry", "futures", "futures-retry"] license = "MIT" readme = "README.md" -categories = [ - "asynchronous", - "wasm", - "web-programming" -] +categories = ["asynchronous", "wasm", "web-programming"] + +[lib] +crate-type = ["cdylib", "rlib"] [badges] maintenance = { status = "actively-developed" } @@ -22,16 +21,28 @@ maintenance = { status = "actively-developed" } # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [features] default = ["log", "rand"] -wasm-bindgen = ["getrandom/wasm-bindgen"] [dependencies] log = { version = "0.4", optional = true } rand = { version = "0.8", optional = true } getrandom = { version = "0.2", optional = true } -wasm-timer = "0.2" + +[target.'cfg(target_arch = "wasm32")'.dependencies] +web-sys = "0.3.69" +wasm-bindgen = "0.2" +wasm-bindgen-futures = "0.4.42" +getrandom = { version = "0.2", features = ["js"] } + +[target.'cfg(not(target_arch = "wasm32"))'.dependencies] +async-std = "1.12.0" [dev-dependencies] approx = "0.5" pretty_env_logger = "0.4" reqwest = "0.11" -tokio = { version = "1", features = ["rt-multi-thread","macros"] } + +[target.'cfg(not(target_arch = "wasm32"))'.dev-dependencies] +tokio = { version = "1", features = ["rt-multi-thread", "macros"] } + +[target.'cfg(target_arch = "wasm32")'.dev-dependencies] +wasm-bindgen-test = "0.3" diff --git a/examples/count.rs b/examples/count.rs index 7cc2f67..98b48a7 100644 --- a/examples/count.rs +++ b/examples/count.rs @@ -1,10 +1,14 @@ -use std::{ - rc::Rc, - sync::atomic::{AtomicUsize, Ordering}, -}; +#[cfg(target_arch = "wasm32")] +fn main() {} +#[cfg(not(target_arch = "wasm32"))] #[tokio::main] async fn main() -> Result<(), &'static str> { + use std::{ + rc::Rc, + sync::atomic::{AtomicUsize, Ordering}, + }; + pretty_env_logger::init(); let counter = Rc::new(AtomicUsize::new(0)); again::retry(move || { diff --git a/examples/fail.rs b/examples/fail.rs index 572f2bc..7e72f47 100644 --- a/examples/fail.rs +++ b/examples/fail.rs @@ -1,7 +1,9 @@ -use std::error::Error; +#[cfg(target_arch = "wasm32")] +fn main() {} +#[cfg(not(target_arch = "wasm32"))] #[tokio::main] -async fn main() -> Result<(), Box> { +async fn main() -> Result<(), Box> { pretty_env_logger::init(); again::retry(|| reqwest::get("nope")).await?; Ok(()) diff --git a/src/delay.rs b/src/delay.rs new file mode 100644 index 0000000..eeebb1e --- /dev/null +++ b/src/delay.rs @@ -0,0 +1,62 @@ +#[cfg(target_arch = "wasm32")] +enum Global { + Window(web_sys::Window), + WorkerGlobalScope(web_sys::WorkerGlobalScope), +} + +#[cfg(target_arch = "wasm32")] +fn global() -> Global { + use web_sys::wasm_bindgen::JsCast; + if let Ok(s) = web_sys::js_sys::global().dyn_into::() { + return Global::Window(s); + } + if let Ok(s) = web_sys::js_sys::global().dyn_into::() { + return Global::WorkerGlobalScope(s); + } + panic!("no global object!") +} + +#[cfg(target_arch = "wasm32")] +impl Global { + pub fn set_timeout_with_callback_and_timeout_and_arguments_0( + &self, + handler: &web_sys::js_sys::Function, + timeout: i32, + ) -> Result { + match self { + Self::Window(s) => { + s.set_timeout_with_callback_and_timeout_and_arguments_0(handler, timeout) + } + Self::WorkerGlobalScope(s) => { + s.set_timeout_with_callback_and_timeout_and_arguments_0(handler, timeout) + } + } + } +} + +#[cfg(target_arch = "wasm32")] +pub async fn sleep(duration: std::time::Duration) -> () { + use std::convert::TryInto; + let millis = duration.as_millis().try_into().unwrap_or(i32::MAX); + + let promise = web_sys::js_sys::Promise::new(&mut |resolve, _| { + global() + .set_timeout_with_callback_and_timeout_and_arguments_0(&resolve, millis) + .unwrap(); + }); + let js_fut = wasm_bindgen_futures::JsFuture::from(promise); + let _ = js_fut.await; +} + +#[cfg(not(target_arch = "wasm32"))] +pub async fn sleep(duration: std::time::Duration) -> () { + async_std::task::sleep(duration).await; +} + +pub struct Delay; + +impl Delay { + pub async fn new(duration: std::time::Duration) -> () { + sleep(duration).await; + } +} diff --git a/src/lib.rs b/src/lib.rs index d7a17bd..dd7d013 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -66,7 +66,10 @@ #[cfg(feature = "rand")] use rand::{distributions::OpenClosed01, thread_rng, Rng}; use std::{cmp::min, future::Future, time::Duration}; -use wasm_timer::Delay; + +pub mod delay; + +use delay::Delay; /// Retries a fallible `Future` with a default `RetryPolicy` /// @@ -634,6 +637,7 @@ where } #[cfg(test)] +#[cfg(not(target_arch = "wasm32"))] mod tests { use super::*; use approx::assert_relative_eq; @@ -715,6 +719,7 @@ mod tests { test(|| async { Ok::(42) }); } + #[ignore] #[test] fn retried_futures_are_send_when_tasks_are_send() { fn test(_: impl Send) {} diff --git a/tests/wasm.rs b/tests/wasm.rs new file mode 100644 index 0000000..c878d46 --- /dev/null +++ b/tests/wasm.rs @@ -0,0 +1,12 @@ +#![cfg(all(test, target_arch = "wasm32"))] + +wasm_bindgen_test::wasm_bindgen_test_configure!(run_in_browser); + +use again::delay::Delay; + +use wasm_bindgen_test::*; + +#[wasm_bindgen_test] +async fn wait_two_seconds() { + let _ = Delay::new(std::time::Duration::from_secs(2)).await; +}