From fd479b558adaafebea15a07ff9ea11f89e4ad4e2 Mon Sep 17 00:00:00 2001 From: One <43485962+c-git@users.noreply.github.com> Date: Tue, 14 Jan 2025 10:31:06 -0500 Subject: [PATCH 01/12] chore: dev version bump --- Cargo.lock | 2 +- Cargo.toml | 7 +++++-- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 6636091..46b5bd4 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1383,7 +1383,7 @@ dependencies = [ [[package]] name = "reqwest-cross" -version = "0.5.0" +version = "0.5.1-dev" dependencies = [ "anyhow", "document-features", diff --git a/Cargo.toml b/Cargo.toml index 8b8a386..b1da82d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "reqwest-cross" -version = "0.5.0" +version = "0.5.1-dev" authors = ["One "] categories = ["web-programming::http-client", "wasm"] documentation = "https://docs.rs/reqwest-cross" @@ -39,7 +39,10 @@ web-sys = { version = "0.3.69", optional = true } wasm-bindgen-test = "0.3.34" [target.'cfg(not(target_arch = "wasm32"))'.dev-dependencies] -tokio = { version = "1.27.0", default-features = false, features = ["macros", "rt-multi-thread"] } +tokio = { version = "1.27.0", default-features = false, features = [ + "macros", + "rt-multi-thread", +] } [[example]] name = "loop_yield_custom" From ead2e89940c18ef11a6b8f522556e1c9146dc427 Mon Sep 17 00:00:00 2001 From: One <43485962+c-git@users.noreply.github.com> Date: Tue, 14 Jan 2025 10:32:12 -0500 Subject: [PATCH 02/12] feat: stub out a wrapper to do retries --- src/data_state_retry.rs | 62 +++++++++++++++++++++++++++++++++++++++++ src/lib.rs | 6 +++- 2 files changed, 67 insertions(+), 1 deletion(-) create mode 100644 src/data_state_retry.rs diff --git a/src/data_state_retry.rs b/src/data_state_retry.rs new file mode 100644 index 0000000..b3262a2 --- /dev/null +++ b/src/data_state_retry.rs @@ -0,0 +1,62 @@ +use crate::{DataState, ErrorBounds}; +use std::ops::Range; +use std::time::Instant; + +/// Automatically retries with a delay on failure until attempts are exhausted +#[derive(Debug)] +pub struct DataStateRetry { + /// The wrapped [`DataState`] + pub inner: DataState, + /// Number of attempts that the retries get reset to + pub max_attempts: u8, + /// The range of milliseconds to select a random value from to set the delay + /// to retry + pub retry_delay_ms: Range, + attempts_left: u8, + last_attempt: Option, +} + +impl DataStateRetry { + /// Creates a new instance of [DataStateRetry] + pub fn new(max_attempts: u8, retry_delay_ms: Range) -> Self { + Self { + max_attempts, + retry_delay_ms, + ..Default::default() + } + } + + /// The number times left to retry before stopping trying + pub fn attempts_left(&self) -> u8 { + self.attempts_left + } + + /// If an attempt was made the instant that it happened at + pub fn last_attempt(&self) -> Option { + self.last_attempt + } +} + +impl Default for DataStateRetry { + fn default() -> Self { + Self { + inner: Default::default(), + max_attempts: 3, + retry_delay_ms: 1000..5000, + attempts_left: 3, + last_attempt: Default::default(), + } + } +} + +impl AsRef> for DataStateRetry { + fn as_ref(&self) -> &DataStateRetry { + self + } +} + +impl AsMut> for DataStateRetry { + fn as_mut(&mut self) -> &mut DataStateRetry { + self + } +} diff --git a/src/lib.rs b/src/lib.rs index c22367a..c7b3d5d 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -18,7 +18,9 @@ //! [examples][#examples]. I would say if you're writing a larger application //! then [DataState] can abstract away a lot of the boiler plate. In addition I //! would prefer [fetch_plus] over [fetch] unless you don't need the UI -//! callback and [fetch_plus] ends up as the one with more boiler plate. +//! callback and [fetch_plus] ends up as the one with more boiler plate. If +//! automated retires are desired see [DataStateRetry] which exposes similar +//! methods but with retry built in. //! //! NOTE: At least 1 [feature flag](#feature-flags) for //! native MUST be set to choose which runtime to use. Currently only Tokio is @@ -72,12 +74,14 @@ //! [examples_folder]: https://github.com/c-git/reqwest-cross/tree/main/examples mod data_state; +mod data_state_retry; mod platform; mod traits; #[cfg(feature = "yield_now")] mod yield_; pub use data_state::{Awaiting, DataState, DataStateError, ErrorBounds}; +pub use data_state_retry::DataStateRetry; pub use platform::{fetch, fetch_plus, spawn}; pub use traits::{BoundedFuture, DoneHandler, ResponseHandler, UiCallBack, ValidReturn}; #[cfg(feature = "yield_now")] From 0422fb49a839060312399e5c621a245cadbc03f4 Mon Sep 17 00:00:00 2001 From: One <43485962+c-git@users.noreply.github.com> Date: Tue, 14 Jan 2025 10:43:05 -0500 Subject: [PATCH 03/12] chore: include egui in bacon checks --- bacon.toml | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/bacon.toml b/bacon.toml index 5fccbfc..87d55bc 100644 --- a/bacon.toml +++ b/bacon.toml @@ -1,3 +1,11 @@ +[jobs.check] +command = ["cargo", "check", "--features=yield_now,egui"] +need_stdout = true + +[jobs.clippy] +command = ["cargo", "clippy", "--features=yield_now,egui"] +need_stdout = true + [jobs.test_examples] command = ["cargo", "test", "--features=yield_now,egui", "--examples"] need_stdout = true From 3ddaf13c4e2085d2d3102518766b37dd10037ff5 Mon Sep 17 00:00:00 2001 From: One <43485962+c-git@users.noreply.github.com> Date: Tue, 14 Jan 2025 10:49:36 -0500 Subject: [PATCH 04/12] chore: remove deny warnings it's pretty annoying --- src/lib.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/lib.rs b/src/lib.rs index c7b3d5d..587ea68 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -2,7 +2,6 @@ #![deny(missing_debug_implementations)] #![cfg_attr(docsrs, feature(doc_cfg))] #![forbid(unsafe_code)] -#![cfg_attr(test, deny(warnings))] // dox - used as documentation for duplicate wasm functions (Uncertain if this will cause problems // but seen this in Reqwest) From fa460cb92a3b5a2b0fadf40bdbf9cf2993196d0b Mon Sep 17 00:00:00 2001 From: One <43485962+c-git@users.noreply.github.com> Date: Tue, 14 Jan 2025 13:12:23 -0500 Subject: [PATCH 05/12] feat: remove debug asset --- src/data_state.rs | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/src/data_state.rs b/src/data_state.rs index feb8cba..84343e6 100644 --- a/src/data_state.rs +++ b/src/data_state.rs @@ -79,11 +79,7 @@ impl DataState { match self { DataState::None => { ui.spinner(); - let is_able_to_make_progress = self.get(fetch_fn).is_able_to_make_progress(); - debug_assert!( - is_able_to_make_progress, - "Should only be calling get if there is a point" - ); + self.get(fetch_fn) } DataState::AwaitingResponse(rx) => { if let Some(new_state) = Self::await_data(rx) { @@ -91,19 +87,20 @@ impl DataState { } else { ui.spinner(); } + CanMakeProgress::AbleToMakeProgress } DataState::Present(_data) => { // Does nothing as data is already present - return CanMakeProgress::UnableToMakeProgress; + CanMakeProgress::UnableToMakeProgress } DataState::Failed(e) => { ui.colored_label(ui.visuals().error_fg_color, e.to_string()); if ui.button(retry_msg.unwrap_or("Retry Request")).clicked() { *self = DataState::default(); } + CanMakeProgress::AbleToMakeProgress } } - CanMakeProgress::AbleToMakeProgress } /// Attempts to load the data and returns if it is able to make progress. @@ -140,7 +137,7 @@ impl DataState { } /// Checks to see if the data is ready and if it is returns a new [`Self`] - /// otherwise None + /// otherwise None. pub fn await_data(rx: &mut Awaiting) -> Option { Some(match rx.0.try_recv() { Ok(recv_opt) => match recv_opt { From ae66515ac5c4d14e1a5c38b90e288aed36315043 Mon Sep 17 00:00:00 2001 From: One <43485962+c-git@users.noreply.github.com> Date: Tue, 14 Jan 2025 13:29:52 -0500 Subject: [PATCH 06/12] refactor: remove duplicated code --- src/data_state.rs | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/src/data_state.rs b/src/data_state.rs index 84343e6..641f8dd 100644 --- a/src/data_state.rs +++ b/src/data_state.rs @@ -1,9 +1,8 @@ //! Helpers for handling pending data. -use std::fmt::{Debug, Display}; - use anyhow::anyhow; use futures::channel::oneshot; +use std::fmt::{Debug, Display}; use thiserror::Error; use tracing::{error, warn}; @@ -81,13 +80,9 @@ impl DataState { ui.spinner(); self.get(fetch_fn) } - DataState::AwaitingResponse(rx) => { - if let Some(new_state) = Self::await_data(rx) { - *self = new_state; - } else { - ui.spinner(); - } - CanMakeProgress::AbleToMakeProgress + DataState::AwaitingResponse(_) => { + ui.spinner(); + self.get(fetch_fn) } DataState::Present(_data) => { // Does nothing as data is already present From 75d5d8f5014c01867edb09347ca0d235136ee7fb Mon Sep 17 00:00:00 2001 From: One <43485962+c-git@users.noreply.github.com> Date: Tue, 14 Jan 2025 13:30:29 -0500 Subject: [PATCH 07/12] feat: add retry functionality --- Cargo.lock | 1 + Cargo.toml | 1 + src/data_state_retry.rs | 189 +++++++++++++++++++++++++++++++++++++--- 3 files changed, 178 insertions(+), 13 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 46b5bd4..7a6fa73 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1390,6 +1390,7 @@ dependencies = [ "egui", "futures", "js-sys", + "rand", "reqwest", "thiserror 2.0.9", "tokio", diff --git a/Cargo.toml b/Cargo.toml index b1da82d..4af0a56 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -21,6 +21,7 @@ anyhow = "1.0.95" document-features = "0.2.10" egui = { version = "0.30.0", default-features = false, optional = true } futures = "0.3.28" +rand = "0.8.5" reqwest = { version = "0.12.12", default-features = false } thiserror = "2.0.9" tracing = "0.1.41" diff --git a/src/data_state_retry.rs b/src/data_state_retry.rs index b3262a2..c573243 100644 --- a/src/data_state_retry.rs +++ b/src/data_state_retry.rs @@ -1,27 +1,29 @@ -use crate::{DataState, ErrorBounds}; +use tracing::{error, warn}; + +use crate::{data_state::CanMakeProgress, Awaiting, DataState, ErrorBounds}; +use std::fmt::Debug; use std::ops::Range; -use std::time::Instant; +use std::time::{Duration, Instant}; /// Automatically retries with a delay on failure until attempts are exhausted #[derive(Debug)] pub struct DataStateRetry { - /// The wrapped [`DataState`] - pub inner: DataState, /// Number of attempts that the retries get reset to pub max_attempts: u8, /// The range of milliseconds to select a random value from to set the delay /// to retry - pub retry_delay_ms: Range, + pub retry_delay_millis: Range, attempts_left: u8, - last_attempt: Option, + inner: DataState, // Not public to ensure resets happen as they should + next_allowed_attempt: Instant, } impl DataStateRetry { /// Creates a new instance of [DataStateRetry] - pub fn new(max_attempts: u8, retry_delay_ms: Range) -> Self { + pub fn new(max_attempts: u8, retry_delay_millis: Range) -> Self { Self { max_attempts, - retry_delay_ms, + retry_delay_millis, ..Default::default() } } @@ -31,9 +33,170 @@ impl DataStateRetry { self.attempts_left } - /// If an attempt was made the instant that it happened at - pub fn last_attempt(&self) -> Option { - self.last_attempt + /// The instant that needs to be waited for before another attempt is + /// allowed + pub fn next_allowed_attempt(&self) -> Instant { + self.next_allowed_attempt + } + + /// Provides access to the inner [`DataState`] + pub fn inner(&self) -> &DataState { + &self.inner + } + + /// Consumes self and returns the unwrapped inner + pub fn into_inner(self) -> DataState { + self.inner + } + + /// Provides access to the stored data if available (returns Some if + /// self.inner is `Data::Present(_)`) + pub fn present(&self) -> Option<&T> { + if let DataState::Present(data) = self.inner.as_ref() { + Some(data) + } else { + None + } + } + + /// Provides mutable access to the stored data if available (returns Some if + /// self.inner is `Data::Present(_)`) + pub fn present_mut(&mut self) -> Option<&mut T> { + if let DataState::Present(data) = self.inner.as_mut() { + Some(data) + } else { + None + } + } + + #[cfg(feature = "egui")] + /// Attempts to load the data and displays appropriate UI if applicable. + /// + /// Note see [`DataState::egui_get`] for more info. + #[must_use] + pub fn egui_get( + &mut self, + ui: &mut egui::Ui, + retry_msg: Option<&str>, + fetch_fn: F, + ) -> CanMakeProgress + where + F: FnOnce() -> Awaiting, + { + match self.inner.as_ref() { + DataState::None | DataState::AwaitingResponse(_) => { + self.ui_spinner_with_attempt_count(ui); + self.get(fetch_fn) + } + DataState::Present(_data) => { + // Does nothing as data is already present + CanMakeProgress::UnableToMakeProgress + } + DataState::Failed(e) => { + ui.colored_label( + ui.visuals().error_fg_color, + format!("{} attempts exhausted. {e}", self.max_attempts), + ); + if ui.button(retry_msg.unwrap_or("Restart Requests")).clicked() { + self.reset_attempts(); + self.inner = DataState::default(); + } + CanMakeProgress::AbleToMakeProgress + } + } + } + + /// Attempts to load the data and returns if it is able to make progress. + /// + /// See [`DataState::get`] for more info. + #[must_use] + pub fn get(&mut self, fetch_fn: F) -> CanMakeProgress + where + F: FnOnce() -> Awaiting, + { + match self.inner.as_mut() { + DataState::None => { + // Going to make an attempt, set when the next attempt is allowed + use rand::Rng; + let wait_time_in_millis = rand::thread_rng() + .gen_range(self.retry_delay_millis.clone()) + .into(); + self.next_allowed_attempt = Instant::now() + .checked_add(Duration::from_millis(wait_time_in_millis)) + .expect("failed to get random delay, value was out of range"); + + self.inner.get(fetch_fn) + } + DataState::AwaitingResponse(rx) => { + if let Some(new_state) = DataState::await_data(rx) { + // TODO 4: Add some tests to ensure await_data work as this function assumes + self.inner = match new_state.as_ref() { + DataState::None => { + error!("Unexpected new state received of DataState::None"); + unreachable!("Only expect Failed or Present variants to be returned but got None") + } + DataState::AwaitingResponse(_) => { + error!("Unexpected new state received of AwaitingResponse"); + unreachable!("Only expect Failed or Present variants to be returned bug got AwaitingResponse") + } + DataState::Present(_) => { + // Data was successfully received + self.reset_attempts(); + new_state + } + DataState::Failed(_) => new_state, + }; + } + CanMakeProgress::AbleToMakeProgress + } + DataState::Present(_) => self.inner.get(fetch_fn), + DataState::Failed(err_msg) => { + if self.attempts_left == 0 { + self.inner.get(fetch_fn) + } else { + let wait_duration_left = self + .next_allowed_attempt + .saturating_duration_since(Instant::now()); + if wait_duration_left.is_zero() { + warn!(?err_msg, ?self.attempts_left, "retrying request"); + self.attempts_left -= 1; + self.inner = DataState::None; + } + CanMakeProgress::AbleToMakeProgress + } + } + } + } + + /// Resets the attempts taken + pub fn reset_attempts(&mut self) { + self.attempts_left = self.max_attempts; + self.next_allowed_attempt = Instant::now(); + } + + /// Clear stored data + pub fn clear(&mut self) { + self.inner = DataState::default(); + } + + /// Returns `true` if the internal data state is [`DataState::Present`]. + #[must_use] + pub fn is_present(&self) -> bool { + self.inner.is_present() + } + + /// Returns `true` if the internal data state is [`DataState::None`]. + #[must_use] + pub fn is_none(&self) -> bool { + self.inner.is_none() + } + + fn ui_spinner_with_attempt_count(&self, ui: &mut egui::Ui) { + ui.horizontal(|ui| { + ui.spinner(); + ui.separator(); + ui.label(format!("{} attempts left", self.attempts_left)) + }); } } @@ -42,9 +205,9 @@ impl Default for DataStateRetry { Self { inner: Default::default(), max_attempts: 3, - retry_delay_ms: 1000..5000, + retry_delay_millis: 1000..5000, attempts_left: 3, - last_attempt: Default::default(), + next_allowed_attempt: Instant::now(), } } } From 15c99d8d305f5c03b53d4c505c9cf68d01ae755d Mon Sep 17 00:00:00 2001 From: One <43485962+c-git@users.noreply.github.com> Date: Tue, 14 Jan 2025 13:38:39 -0500 Subject: [PATCH 08/12] fix: add js feature random on WASM --- .vscode/settings.json | 1 + Cargo.lock | 1 + Cargo.toml | 1 + src/data_state_retry.rs | 1 + 4 files changed, 4 insertions(+) diff --git a/.vscode/settings.json b/.vscode/settings.json index f8f20ed..ed224e1 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,6 +1,7 @@ { "rust-analyzer.showUnlinkedFileNotification": false, "cSpell.words": [ + "getrandom", "jsvalue" ], // "rust-analyzer.cargo.target": "wasm32-unknown-unknown" // Uncomment to use rust-analyzer on wasm code instead diff --git a/Cargo.lock b/Cargo.lock index 7a6fa73..d715c73 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1389,6 +1389,7 @@ dependencies = [ "document-features", "egui", "futures", + "getrandom", "js-sys", "rand", "reqwest", diff --git a/Cargo.toml b/Cargo.toml index 4af0a56..1e35d51 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -35,6 +35,7 @@ tokio = { version = "1", default-features = false, optional = true } wasm-bindgen-futures = "0.4.34" js-sys = { version = "0.3.69", optional = true } web-sys = { version = "0.3.69", optional = true } +getrandom = { version = "0.2.15", features = ["js"] } [dev-dependencies] wasm-bindgen-test = "0.3.34" diff --git a/src/data_state_retry.rs b/src/data_state_retry.rs index c573243..6c15a1d 100644 --- a/src/data_state_retry.rs +++ b/src/data_state_retry.rs @@ -191,6 +191,7 @@ impl DataStateRetry { self.inner.is_none() } + #[cfg(feature = "egui")] fn ui_spinner_with_attempt_count(&self, ui: &mut egui::Ui) { ui.horizontal(|ui| { ui.spinner(); From ec2d6e86c8edb71af24ae096ef5b4220edafb256 Mon Sep 17 00:00:00 2001 From: One <43485962+c-git@users.noreply.github.com> Date: Tue, 14 Jan 2025 13:45:25 -0500 Subject: [PATCH 09/12] chore: update deps in lock file --- Cargo.lock | 123 +++++++++++++++++++++++++++-------------------------- 1 file changed, 63 insertions(+), 60 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index d715c73..a6ef4a6 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -128,9 +128,9 @@ checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" [[package]] name = "bitflags" -version = "2.6.0" +version = "2.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" +checksum = "1be3f42a67d6d345ecd59f675f3f012d6974981560836e938c22b424b85ce1be" [[package]] name = "brotli" @@ -173,9 +173,9 @@ checksum = "325918d6fe32f23b19878fe4b34794ae41fc19ddbe53b10571a4874d44ffd39b" [[package]] name = "cc" -version = "1.2.6" +version = "1.2.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8d6dbb628b8f8555f86d0323c2eb39e3ec81901f4b83e091db8a6a76d316a333" +checksum = "c8293772165d9345bdaaa39b45b2109591e63fe5e6fbc23c6ff930a048aa310b" dependencies = [ "jobserver", "libc", @@ -907,9 +907,9 @@ dependencies = [ [[package]] name = "js-sys" -version = "0.3.76" +version = "0.3.77" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6717b6b5b077764fb5966237269cb3c64edddde4b14ce42647430a78ced9e7b7" +checksum = "1cfaf33c695fc6e08064efbc1f72ec937429614f25eef83af942d0e227c3a28f" dependencies = [ "once_cell", "wasm-bindgen", @@ -929,9 +929,9 @@ checksum = "0717cef1bc8b636c6e1c1bbdefc09e6322da8a9321966e8928ef80d20f7f770f" [[package]] name = "linux-raw-sys" -version = "0.4.14" +version = "0.4.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89" +checksum = "d26c52dbd32dccf2d10cac7725f8eae5296885fb5703b261f7d0a0739ec807ab" [[package]] name = "litemap" @@ -957,9 +957,9 @@ dependencies = [ [[package]] name = "log" -version = "0.4.22" +version = "0.4.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24" +checksum = "04cbf5b083de1c7e0222a7a51dbfdba1cbe1c6ab0b15e29fff3f6c077fd9cd9f" [[package]] name = "lru-cache" @@ -1010,9 +1010,9 @@ dependencies = [ [[package]] name = "miniz_oxide" -version = "0.8.2" +version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ffbe83022cedc1d264172192511ae958937694cd57ce297164951b8b3568394" +checksum = "b8402cab7aefae129c6977bb0ff1b8fd9a04eb5b51efc50a70bea51cda0c7924" dependencies = [ "adler2", ] @@ -1156,9 +1156,9 @@ checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" [[package]] name = "pin-project-lite" -version = "0.2.15" +version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "915a1e146535de9163f3987b8944ed8cf49a18bb0056bcebcdcece385cece4ff" +checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b" [[package]] name = "pin-utils" @@ -1189,9 +1189,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.92" +version = "1.0.93" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "37d3544b3f2748c54e147655edb5025752e2303145b5aefb3c3ea2c78b973bb0" +checksum = "60946a68e5f9d28b0dc1c21bb8a97ee7d018a8b322fa57838ba31cc878e22d99" dependencies = [ "unicode-ident", ] @@ -1237,7 +1237,7 @@ dependencies = [ "rustc-hash", "rustls", "socket2", - "thiserror 2.0.9", + "thiserror 2.0.11", "tokio", "tracing", ] @@ -1256,7 +1256,7 @@ dependencies = [ "rustls", "rustls-pki-types", "slab", - "thiserror 2.0.9", + "thiserror 2.0.11", "tinyvec", "tracing", "web-time", @@ -1273,7 +1273,7 @@ dependencies = [ "once_cell", "socket2", "tracing", - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] @@ -1393,7 +1393,7 @@ dependencies = [ "js-sys", "rand", "reqwest", - "thiserror 2.0.9", + "thiserror 2.0.11", "tokio", "tracing", "wasm-bindgen-futures", @@ -1440,9 +1440,9 @@ checksum = "c7fb8039b3032c191086b10f11f319a6e99e1e82889c5cc6046f515c9db1d497" [[package]] name = "rustix" -version = "0.38.42" +version = "0.38.43" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f93dc38ecbab2eb790ff964bb77fa94faf256fd3e73285fd7ba0903b76bedb85" +checksum = "a78891ee6bf2340288408954ac787aa063d8e8817e9f53abb37c695c6d834ef6" dependencies = [ "bitflags", "errno", @@ -1453,9 +1453,9 @@ dependencies = [ [[package]] name = "rustls" -version = "0.23.20" +version = "0.23.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5065c3f250cbd332cd894be57c40fa52387247659b14a2d6041d121547903b1b" +checksum = "8f287924602bf649d949c63dc8ac8b235fa5387d394020705b80c4eb597ce5b8" dependencies = [ "once_cell", "ring", @@ -1494,6 +1494,12 @@ dependencies = [ "untrusted", ] +[[package]] +name = "rustversion" +version = "1.0.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f7c45b9784283f1b2e7fb61b42047c2fd678ef0960d4f6f1eba131594cc369d4" + [[package]] name = "ryu" version = "1.0.18" @@ -1518,12 +1524,6 @@ dependencies = [ "windows-sys 0.59.0", ] -[[package]] -name = "scoped-tls" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e1cf6437eb19a8f4a6cc0f7dca544973b0b78843adbfeb3683d1a94a0024a294" - [[package]] name = "scopeguard" version = "1.2.0" @@ -1648,9 +1648,9 @@ checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" [[package]] name = "syn" -version = "2.0.93" +version = "2.0.96" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c786062daee0d6db1132800e623df74274a0a87322d8e183338e01b3d98d058" +checksum = "d5d0adab1ae378d7f53bdebc67a39f1f151407ef230f0ce2883572f5d8985c80" dependencies = [ "proc-macro2", "quote", @@ -1723,11 +1723,11 @@ dependencies = [ [[package]] name = "thiserror" -version = "2.0.9" +version = "2.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f072643fd0190df67a8bab670c20ef5d8737177d6ac6b2e9a236cb096206b2cc" +checksum = "d452f284b73e6d76dd36758a0c8684b1d5be31f92b89d07fd5822175732206fc" dependencies = [ - "thiserror-impl 2.0.9", + "thiserror-impl 2.0.11", ] [[package]] @@ -1743,9 +1743,9 @@ dependencies = [ [[package]] name = "thiserror-impl" -version = "2.0.9" +version = "2.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b50fa271071aae2e6ee85f842e2e28ba8cd2c5fb67f11fcb1fd70b276f9e7d4" +checksum = "26afc1baea8a989337eeb52b6e72a039780ce45c3edfcc9c5b9d112feeb173c2" dependencies = [ "proc-macro2", "quote", @@ -1810,9 +1810,9 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tokio" -version = "1.42.0" +version = "1.43.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5cec9b21b0450273377fc97bd4c33a8acffc8c996c987a7c5b319a0083707551" +checksum = "3d61fa4ffa3de412bfea335c6ecff681de2b609ba3c77ef3e00e521813a9ed9e" dependencies = [ "backtrace", "bytes", @@ -1826,9 +1826,9 @@ dependencies = [ [[package]] name = "tokio-macros" -version = "2.4.0" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "693d596312e88961bc67d7f1f97af8a70227d9f90c31bba5806eec004978d752" +checksum = "6e06d43f1345a3bcd39f6a56dbb7dcab2ba47e68e8ac134855e7e2bdbaf8cab8" dependencies = [ "proc-macro2", "quote", @@ -2030,20 +2030,21 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] name = "wasm-bindgen" -version = "0.2.99" +version = "0.2.100" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a474f6281d1d70c17ae7aa6a613c87fce69a127e2624002df63dcb39d6cf6396" +checksum = "1edc8929d7499fc4e8f0be2262a241556cfc54a0bea223790e71446f2aab1ef5" dependencies = [ "cfg-if", "once_cell", + "rustversion", "wasm-bindgen-macro", ] [[package]] name = "wasm-bindgen-backend" -version = "0.2.99" +version = "0.2.100" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f89bb38646b4f81674e8f5c3fb81b562be1fd936d84320f3264486418519c79" +checksum = "2f0a0651a5c2bc21487bde11ee802ccaf4c51935d0d3d42a6101f98161700bc6" dependencies = [ "bumpalo", "log", @@ -2055,9 +2056,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-futures" -version = "0.4.49" +version = "0.4.50" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38176d9b44ea84e9184eff0bc34cc167ed044f816accfe5922e54d84cf48eca2" +checksum = "555d470ec0bc3bb57890405e5d4322cc9ea83cebb085523ced7be4144dac1e61" dependencies = [ "cfg-if", "js-sys", @@ -2068,9 +2069,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.99" +version = "0.2.100" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2cc6181fd9a7492eef6fef1f33961e3695e4579b9872a6f7c83aee556666d4fe" +checksum = "7fe63fc6d09ed3792bd0897b314f53de8e16568c2b3f7982f468c0bf9bd0b407" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -2078,9 +2079,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.99" +version = "0.2.100" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30d7a95b763d3c45903ed6c81f156801839e5ee968bb07e534c44df0fcd330c2" +checksum = "8ae87ea40c9f689fc23f209965b6fb8a99ad69aeeb0231408be24920604395de" dependencies = [ "proc-macro2", "quote", @@ -2091,19 +2092,21 @@ dependencies = [ [[package]] name = "wasm-bindgen-shared" -version = "0.2.99" +version = "0.2.100" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "943aab3fdaaa029a6e0271b35ea10b72b943135afe9bffca82384098ad0e06a6" +checksum = "1a05d73b933a847d6cccdda8f838a22ff101ad9bf93e33684f39c1f5f0eece3d" +dependencies = [ + "unicode-ident", +] [[package]] name = "wasm-bindgen-test" -version = "0.3.49" +version = "0.3.50" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c61d44563646eb934577f2772656c7ad5e9c90fac78aa8013d776fcdaf24625d" +checksum = "66c8d5e33ca3b6d9fa3b4676d774c5778031d27a578c2b007f905acf816152c3" dependencies = [ "js-sys", "minicov", - "scoped-tls", "wasm-bindgen", "wasm-bindgen-futures", "wasm-bindgen-test-macro", @@ -2111,9 +2114,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-test-macro" -version = "0.3.49" +version = "0.3.50" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "54171416ce73aa0b9c377b51cc3cb542becee1cd678204812e8392e5b0e4a031" +checksum = "17d5042cc5fa009658f9a7333ef24291b1291a25b6382dd68862a7f3b969f69b" dependencies = [ "proc-macro2", "quote", @@ -2135,9 +2138,9 @@ dependencies = [ [[package]] name = "web-sys" -version = "0.3.76" +version = "0.3.77" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "04dd7223427d52553d3702c004d3b2fe07c148165faa56313cb00211e31c12bc" +checksum = "33b6dd2ef9186f1f2072e409e99cd22a975331a6b3591b12c764e0e55c60d5d2" dependencies = [ "js-sys", "wasm-bindgen", From 142f0b313a6978086f2d3b33b93818fb5a92e185 Mon Sep 17 00:00:00 2001 From: One <43485962+c-git@users.noreply.github.com> Date: Tue, 14 Jan 2025 17:23:44 -0500 Subject: [PATCH 10/12] fix: only making one attempt --- src/data_state_retry.rs | 23 ++++++++++++++++------- 1 file changed, 16 insertions(+), 7 deletions(-) diff --git a/src/data_state_retry.rs b/src/data_state_retry.rs index 6c15a1d..b8b827a 100644 --- a/src/data_state_retry.rs +++ b/src/data_state_retry.rs @@ -93,14 +93,23 @@ impl DataStateRetry { CanMakeProgress::UnableToMakeProgress } DataState::Failed(e) => { - ui.colored_label( - ui.visuals().error_fg_color, - format!("{} attempts exhausted. {e}", self.max_attempts), - ); - if ui.button(retry_msg.unwrap_or("Restart Requests")).clicked() { - self.reset_attempts(); - self.inner = DataState::default(); + if self.attempts_left == 0 { + ui.colored_label( + ui.visuals().error_fg_color, + format!("{} attempts exhausted. {e}", self.max_attempts), + ); + if ui.button(retry_msg.unwrap_or("Restart Requests")).clicked() { + self.reset_attempts(); + self.inner = DataState::default(); + } + } else { + let is_able_to_make_progress = self.get(fetch_fn).is_able_to_make_progress(); + assert!( + is_able_to_make_progress, + "if this is not true something is very wrong" + ); } + CanMakeProgress::AbleToMakeProgress } } From e95f0433bfc65b7a19a6b70d0c26f114d57406a4 Mon Sep 17 00:00:00 2001 From: One <43485962+c-git@users.noreply.github.com> Date: Tue, 14 Jan 2025 20:47:02 -0500 Subject: [PATCH 11/12] feat: add UI for egui while waiting --- src/data_state_retry.rs | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/src/data_state_retry.rs b/src/data_state_retry.rs index b8b827a..e81576e 100644 --- a/src/data_state_retry.rs +++ b/src/data_state_retry.rs @@ -103,6 +103,15 @@ impl DataStateRetry { self.inner = DataState::default(); } } else { + let wait_left = wait_before_next_attempt(self.next_allowed_attempt); + ui.colored_label( + ui.visuals().error_fg_color, + format!( + "{} attempt(s) left. {} seconds before retry. {e}", + self.attempts_left, + wait_left.as_secs() + ), + ); let is_able_to_make_progress = self.get(fetch_fn).is_able_to_make_progress(); assert!( is_able_to_make_progress, @@ -163,10 +172,8 @@ impl DataStateRetry { if self.attempts_left == 0 { self.inner.get(fetch_fn) } else { - let wait_duration_left = self - .next_allowed_attempt - .saturating_duration_since(Instant::now()); - if wait_duration_left.is_zero() { + let wait_left = wait_before_next_attempt(self.next_allowed_attempt); + if wait_left.is_zero() { warn!(?err_msg, ?self.attempts_left, "retrying request"); self.attempts_left -= 1; self.inner = DataState::None; @@ -233,3 +240,8 @@ impl AsMut> for DataStateRetry { self } } + +/// The duration before the next attempt will be made +fn wait_before_next_attempt(next_allowed_attempt: Instant) -> Duration { + next_allowed_attempt.saturating_duration_since(Instant::now()) +} From 563b6357363e7ba4318c7db6c3f0c92df6381a33 Mon Sep 17 00:00:00 2001 From: One <43485962+c-git@users.noreply.github.com> Date: Tue, 14 Jan 2025 23:43:43 -0500 Subject: [PATCH 12/12] chore: version bump 0.5.1 --- Cargo.lock | 2 +- Cargo.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index a6ef4a6..32c3a47 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1383,7 +1383,7 @@ dependencies = [ [[package]] name = "reqwest-cross" -version = "0.5.1-dev" +version = "0.5.1" dependencies = [ "anyhow", "document-features", diff --git a/Cargo.toml b/Cargo.toml index 1e35d51..70efc01 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "reqwest-cross" -version = "0.5.1-dev" +version = "0.5.1" authors = ["One "] categories = ["web-programming::http-client", "wasm"] documentation = "https://docs.rs/reqwest-cross"