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 pollswin_exists/win_active
identically on both, and filing it there would have meant weakening the
module's own charter.autoitx-sysand its 1:1AU3_*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 isNone, 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 mirrorswin_waitreturningOk(false), rather
than anErr(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.