Skip to content

Commit

Permalink
feat: add protection for users
Browse files Browse the repository at this point in the history
If get is used in a loop it's possible to not know that it's not doing
anything useful anymore
  • Loading branch information
c-git committed Jan 5, 2025
1 parent 341f7fb commit 80c3cba
Show file tree
Hide file tree
Showing 2 changed files with 49 additions and 13 deletions.
27 changes: 16 additions & 11 deletions examples/loop_yield_data_state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,17 +35,22 @@ async fn common_code() -> Result<(), Box<dyn std::error::Error>> {
assert_eq!(status_code, &200);
break;
} else {
state.get(|| {
let req = client.get("http://httpbin.org/get");
let response_handler = |resp: reqwest::Result<reqwest::Response>| async {
resp.map(|resp| resp.status())
.context("Request failed, got an error back")
};
let ui_notify = || {
println!("Request Completed, this is where you would wake up your UI thread");
};
Awaiting(fetch_plus(req, response_handler, ui_notify))
});
let is_able_to_make_progress = state
.get(|| {
let req = client.get("http://httpbin.org/get");
let response_handler = |resp: reqwest::Result<reqwest::Response>| async {
resp.map(|resp| resp.status())
.context("Request failed, got an error back")
};
let ui_notify = || {
println!(
"Request Completed, this is where you would wake up your UI thread"
);
};
Awaiting(fetch_plus(req, response_handler, ui_notify))
})
.is_able_to_make_progress();
assert!(is_able_to_make_progress);
reqwest_cross::yield_now().await;
}
}
Expand Down
35 changes: 33 additions & 2 deletions src/data_state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,14 @@ pub enum DataStateError<E: ErrorBounds> {
FromE(E),
}

#[derive(Debug)]
/// Provides a way to ensure the calling code knows if it is calling a function
/// that cannot do anything useful anymore
pub enum CanMakeProgress {
AbleToMakeProgress,
UnableToMakeProgress,
}

/// Used to represent data that is pending being available
#[derive(Debug)]
pub struct Awaiting<T, E: ErrorBounds>(pub oneshot::Receiver<Result<T, E>>);
Expand Down Expand Up @@ -84,30 +92,35 @@ impl<T, E: ErrorBounds> DataState<T, E> {
}
}

/// Attempts to load the data.
/// Attempts to load the data and returns if it is able to make progress.
///
/// Note: F needs to return `AwaitingType<T>` and not T because it needs to
/// be able to be pending if T is not ready.
pub fn get<F>(&mut self, fetch_fn: F)
#[must_use]
pub fn get<F>(&mut self, fetch_fn: F) -> CanMakeProgress
where
F: FnOnce() -> Awaiting<T, E>,
{
match self {
DataState::None => {
let rx = fetch_fn();
*self = DataState::AwaitingResponse(rx);
CanMakeProgress::AbleToMakeProgress
}
DataState::AwaitingResponse(rx) => {
if let Some(new_state) = Self::await_data(rx) {
*self = new_state;
}
CanMakeProgress::AbleToMakeProgress
}
DataState::Present(_data) => {
// Does nothing data is already present
CanMakeProgress::UnableToMakeProgress
}
DataState::Failed(_e) => {
// Have no way to let the user know there is an error nothing we
// can do here
CanMakeProgress::UnableToMakeProgress
}
}
}
Expand Down Expand Up @@ -184,3 +197,21 @@ impl From<String> for DataStateError<anyhow::Error> {
anyhow!(value).into()
}
}

impl CanMakeProgress {
/// Returns `true` if the can make progress is [`AbleToMakeProgress`].
///
/// [`AbleToMakeProgress`]: CanMakeProgress::AbleToMakeProgress
#[must_use]
pub fn is_able_to_make_progress(&self) -> bool {
matches!(self, Self::AbleToMakeProgress)
}

/// Returns `true` if the can make progress is [`UnableToMakeProgress`].
///
/// [`UnableToMakeProgress`]: CanMakeProgress::UnableToMakeProgress
#[must_use]
pub fn is_unable_to_make_progress(&self) -> bool {
matches!(self, Self::UnableToMakeProgress)
}
}

0 comments on commit 80c3cba

Please sign in to comment.