// Shared loop plumbing and no-match fallbacks for the matching engine.
//
// Engine structure (uap-python authority):
// - Per domain, rules are tried in upstream order and the FIRST rule whose
// regex matches wins (`Resolver.__call__`, basic.py:55-84, via
// `next(filter(None, ...))`).
// - When no rule matches, the domain reports the "Other" fallback:
// `family = "Other"` and every other field `None` (`Result.with_defaults`
// / dataclass defaults, core.py:22-49,74-92).
///|
/// Scan a domain's rules in order and return the index of the first rule
/// whose precompiled regex matches `ua`, together with the match result.
/// `rules` and `regexps` are index-aligned (T-04/T-05 invariant).
fn first_match(
rules_length : Int,
regexps : Array[@regexp.Regexp],
ua : String,
) -> (Int, @regexp.MatchResult)? {
for i = 0; i < rules_length; i = i + 1 {
let m = regexps[i].execute(ua)
if m.matched() {
return Some((i, m))
}
}
return None
}
///|
/// The all-miss browser fallback: family `"Other"`, no version segments.
/// No rule produced it, so `rule_index` is `None` even under
/// `with_rule_index=true` (T-09).
fn other_browser() -> Browser {
{
family: "Other",
major: None,
minor: None,
patch: None,
patch_minor: None,
rule_index: None,
}
}
///|
/// The all-miss OS fallback: family `"Other"`, no version segments.
/// No rule produced it, so `rule_index` is `None` even under
/// `with_rule_index=true` (T-09).
fn other_os() -> OS {
{
family: "Other",
major: None,
minor: None,
patch: None,
patch_minor: None,
rule_index: None,
}
}
///|
/// The all-miss device fallback: family `"Other"`, no brand or model.
/// No rule produced it, so `rule_index` is `None` even under
/// `with_rule_index=true` (T-09).
fn other_device() -> Device {
{ family: "Other", brand: None, model: None, rule_index: None, }
}