diff --git a/Cargo.lock b/Cargo.lock index ceba34b..2bde1f4 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1617,7 +1617,7 @@ dependencies = [ [[package]] name = "hyperware_process_lib" -version = "2.2.0" +version = "2.2.1" dependencies = [ "alloy", "alloy-primitives", diff --git a/src/hyperapp.rs b/src/hyperapp.rs index d6f63d0..13a3693 100644 --- a/src/hyperapp.rs +++ b/src/hyperapp.rs @@ -594,88 +594,6 @@ pub fn pretty_print_send_error(error: &SendError) { ); } -/// Classification for readiness polling of another process. -#[derive(Debug, PartialEq, Eq)] -pub enum WaitClassification { - /// The target responded but indicated it is still starting up. - Starting, - /// The target is ready (or responded with a payload we consider ready). - Ready, - /// The target responded with an unknown payload. - Unknown, -} - -/// Poll a target process until it reports ready. -/// -/// - `target`: process address to poll (e.g., hypermap-cacher). -/// - `request_body`: request payload to send each attempt. -/// - `timeout_s`: per-request timeout in seconds. -/// - `retry_delay_s`: delay between attempts when not ready or on error. -/// - `classify`: function to classify the response body. -/// - `treat_unknown_as_ready`: if true, any non-starting response is treated as ready. -pub fn wait_for_process_ready( - target: Address, - request_body: Vec, - timeout_s: u64, - retry_delay_s: u64, - mut classify: F, - treat_unknown_as_ready: bool, -) where - F: FnMut(&[u8]) -> WaitClassification, -{ - let mut attempt = 1; - loop { - match Request::to(target.clone()) - .body(request_body.clone()) - .send_and_await_response(timeout_s) - { - Ok(Ok(response)) => { - let classification = classify(response.body()); - match classification { - WaitClassification::Starting => { - info!( - "Target {} still starting (attempt {}), retrying in {}s", - target, attempt, retry_delay_s - ); - } - WaitClassification::Ready => { - info!("Target {} ready after {} attempt(s)", target, attempt); - break; - } - WaitClassification::Unknown => { - if treat_unknown_as_ready { - info!( - "Target {} responded with unknown payload, proceeding as ready", - target - ); - break; - } else { - info!( - "Target {} responded with unknown payload, retrying in {}s", - target, retry_delay_s - ); - } - } - } - } - Ok(Err(e)) => { - info!( - "Error response from {} (attempt {}): {:?}, retrying in {}s", - target, attempt, e, retry_delay_s - ); - } - Err(e) => { - info!( - "Failed to contact {} (attempt {}): {:?}, retrying in {}s", - target, attempt, e, retry_delay_s - ); - } - } - attempt += 1; - std::thread::sleep(std::time::Duration::from_secs(retry_delay_s)); - } -} - // For demonstration, we'll define them all in one place. // Make sure the signatures match the real function signatures you require! pub fn no_init_fn(_state: &mut S) { diff --git a/src/lib.rs b/src/lib.rs index f294f2c..d7102df 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -364,6 +364,115 @@ pub fn eval_our(address: &Address) -> Address { address } +/// Classification for readiness polling of another process. +#[derive(Debug, PartialEq, Eq)] +pub enum WaitClassification { + /// The target responded but indicated it is still starting up. + Starting, + /// The target is ready (or responded with a payload we consider ready). + Ready, + /// The target responded with an unknown payload. + Unknown, +} + +/// Poll a target process until it reports ready while blocking. +/// +/// - `target`: process address to poll (e.g., hypermap-cacher). +/// - `request_body`: request payload to send each attempt. +/// - `timeout_s`: per-request timeout in seconds. +/// - `retry_delay_s`: delay between attempts when not ready or on error. +/// - `classify`: function to classify the response body. +/// - `treat_unknown_as_ready`: if true, any non-starting response is treated as ready. +/// - `max_attempts`: number of attempts before continuing without a ready response. +pub fn wait_for_process_ready( + target: Address, + request_body: Vec, + timeout_s: u64, + retry_delay_s: u64, + mut classify: F, + treat_unknown_as_ready: bool, + max_attempts: Option, +) where + F: FnMut(&[u8]) -> WaitClassification, +{ + let mut attempt = 1; + loop { + let mut fail_message_suffix = format!(", retrying in {retry_delay_s}s"); + if let Some(ma) = max_attempts { + if attempt >= ma { + fail_message_suffix = ", abandoning waiting and proceeding as if ready".to_string() + } + } + + match Request::to(target.clone()) + .body(request_body.clone()) + .send_and_await_response(timeout_s) + { + Ok(Ok(response)) => { + let classification = classify(response.body()); + match classification { + WaitClassification::Starting => { + crate::print_to_terminal( + 2, + &format!( + "Target {} still starting (attempt {}){}", + target, attempt, fail_message_suffix + ), + ); + } + WaitClassification::Ready => { + crate::print_to_terminal( + 2, + &format!("Target {} ready after {} attempt(s)", target, attempt), + ); + break; + } + WaitClassification::Unknown => { + if treat_unknown_as_ready { + crate::print_to_terminal( + 2, + &format!( + "Target {} responded with unknown payload, proceeding as ready", + target + ), + ); + break; + } else { + crate::print_to_terminal( + 2, + &format!( + "Target {} responded with unknown payload{}", + target, fail_message_suffix + ), + ); + } + } + } + } + Ok(Err(e)) => { + crate::print_to_terminal( + 2, + &format!( + "Error response from {} (attempt {}): {:?}{}", + target, attempt, e, fail_message_suffix + ), + ); + } + Err(e) => { + crate::print_to_terminal( + 2, + &format!( + "Failed to contact {} (attempt {}): {:?}{}", + target, attempt, e, fail_message_suffix + ), + ); + } + } + attempt += 1; + std::thread::sleep(std::time::Duration::from_secs(retry_delay_s)); + } +} + /// The `Spawn!()` macro is defined here as a no-op. /// However, in practice, `kit build` will rewrite it during pre-processing. ///