Skip to content

v0.1.2 — wait_for_any

Latest

Choose a tag to compare

@iagodpassos iagodpassos released this 01 Aug 18:36

wait_for_any — for actions with more than one outcome

An action in a legacy application rarely has one outcome. Committing a form
either closes it, raises an error dialog, or raises a different dialog meaning
something else entirely — and which one happened is the only way to know what
the application actually did.

AutoIt waits on one window at a time, so that race gets written by hand:

while (WinExists("Order Selection")
    && !WinExists("[CLASS:ui60Modal_W32]")
    && !WinExists("Blocked")) { Thread.Sleep(300); }

That loop is in production twice, in two separate robots, byte for byte the
same. Neither copy has a timeout: if none of the three ever happens, the
robot hangs until someone notices.

match ai.wait_for_any(&[
    (&orders,  WinCondition::Gone),   // the form closed: it saved
    (&modal,   WinCondition::Exists), // an error came up instead
    (&blocked, WinCondition::Exists), // ... or a block notice did
], Some(Duration::from_secs(60)))? {
    Some(0) => saved(),
    Some(1) => report_error()?,
    Some(2) => report_blocked()?,
    _ => return Err(wedged()),
}

Taking the timeout as a parameter is what removes the failure mode — there is
no version of this call that can hang. Returning the index rather than a bool is
the point: the caller has to branch on which outcome occurred.

WinCondition has four variants — Exists, Gone, Active, NotActive
mirroring AutoIt's four single-window waits, so racing them against each other
needs no vocabulary you do not already have.

Design notes

  • It is in the core, not recipes. That module is for portable intent
    whose mechanism differs per platform. This polls win_exists/win_active
    identically on both, and filing it there would have meant weakening the
    module's own charter. autoitx-sys and its 1:1 AU3_* surface are untouched.
  • Order is significant, and documented. Watches are evaluated in slice order
    and the lower index wins a tie. Without a stated rule the return value would
    depend on polling luck.
  • Polling runs at WinWaitDelay (250 ms by default) — the option that
    already paces the other waits, not a fresh literal.
  • An empty slice returns Ok(None) at once. Blocking would hang forever
    when the timeout is None, so the degenerate case is the one worth
    special-casing — and the one with a test, because getting it wrong hangs CI
    rather than failing it.
  • Ok(None) on timeout mirrors win_wait returning Ok(false), rather
    than an Err(Timeout) that reads as more expressive but less consistent.

Compatibility

Purely additive. Anyone on autoitx = "0.1" picks this up without editing a
manifest — hence 0.1.2 rather than 0.2.0.