@@ -364,6 +364,115 @@ pub fn eval_our(address: &Address) -> Address {
364364 address
365365}
366366
367+ /// Classification for readiness polling of another process.
368+ #[ derive( Debug , PartialEq , Eq ) ]
369+ pub enum WaitClassification {
370+ /// The target responded but indicated it is still starting up.
371+ Starting ,
372+ /// The target is ready (or responded with a payload we consider ready).
373+ Ready ,
374+ /// The target responded with an unknown payload.
375+ Unknown ,
376+ }
377+
378+ /// Poll a target process until it reports ready while blocking.
379+ ///
380+ /// - `target`: process address to poll (e.g., hypermap-cacher).
381+ /// - `request_body`: request payload to send each attempt.
382+ /// - `timeout_s`: per-request timeout in seconds.
383+ /// - `retry_delay_s`: delay between attempts when not ready or on error.
384+ /// - `classify`: function to classify the response body.
385+ /// - `treat_unknown_as_ready`: if true, any non-starting response is treated as ready.
386+ /// - `max_attempts`: number of attempts before continuing without a ready response.
387+ pub fn wait_for_process_ready < F > (
388+ target : Address ,
389+ request_body : Vec < u8 > ,
390+ timeout_s : u64 ,
391+ retry_delay_s : u64 ,
392+ mut classify : F ,
393+ treat_unknown_as_ready : bool ,
394+ max_attempts : Option < u32 > ,
395+ ) where
396+ F : FnMut ( & [ u8 ] ) -> WaitClassification ,
397+ {
398+ let mut attempt = 1 ;
399+ loop {
400+ let mut fail_message_suffix = format ! ( ", retrying in {retry_delay_s}s" ) ;
401+ if let Some ( ma) = max_attempts {
402+ if attempt >= ma {
403+ fail_message_suffix = ", abandoning waiting and proceeding as if ready" . to_string ( )
404+ }
405+ }
406+
407+ match Request :: to ( target. clone ( ) )
408+ . body ( request_body. clone ( ) )
409+ . send_and_await_response ( timeout_s)
410+ {
411+ Ok ( Ok ( response) ) => {
412+ let classification = classify ( response. body ( ) ) ;
413+ match classification {
414+ WaitClassification :: Starting => {
415+ crate :: print_to_terminal (
416+ 2 ,
417+ & format ! (
418+ "Target {} still starting (attempt {}){}" ,
419+ target, attempt, fail_message_suffix
420+ ) ,
421+ ) ;
422+ }
423+ WaitClassification :: Ready => {
424+ crate :: print_to_terminal (
425+ 2 ,
426+ & format ! ( "Target {} ready after {} attempt(s)" , target, attempt) ,
427+ ) ;
428+ break ;
429+ }
430+ WaitClassification :: Unknown => {
431+ if treat_unknown_as_ready {
432+ crate :: print_to_terminal (
433+ 2 ,
434+ & format ! (
435+ "Target {} responded with unknown payload, proceeding as ready" ,
436+ target
437+ ) ,
438+ ) ;
439+ break ;
440+ } else {
441+ crate :: print_to_terminal (
442+ 2 ,
443+ & format ! (
444+ "Target {} responded with unknown payload{}" ,
445+ target, fail_message_suffix
446+ ) ,
447+ ) ;
448+ }
449+ }
450+ }
451+ }
452+ Ok ( Err ( e) ) => {
453+ crate :: print_to_terminal (
454+ 2 ,
455+ & format ! (
456+ "Error response from {} (attempt {}): {:?}{}" ,
457+ target, attempt, e, fail_message_suffix
458+ ) ,
459+ ) ;
460+ }
461+ Err ( e) => {
462+ crate :: print_to_terminal (
463+ 2 ,
464+ & format ! (
465+ "Failed to contact {} (attempt {}): {:?}{}" ,
466+ target, attempt, e, fail_message_suffix
467+ ) ,
468+ ) ;
469+ }
470+ }
471+ attempt += 1 ;
472+ std:: thread:: sleep ( std:: time:: Duration :: from_secs ( retry_delay_s) ) ;
473+ }
474+ }
475+
367476/// The `Spawn!()` macro is defined here as a no-op.
368477/// However, in practice, `kit build` will rewrite it during pre-processing.
369478///
0 commit comments