Skip to main content

Crate smix_host_coord_resolver

Crate smix_host_coord_resolver 

Source
Expand description

§smix-host-coord-resolver

Crates.io docs.rs License

Pure pipeline from (A11yNode tree, Selector) to a normalized (nx, ny) tap coordinate. No I/O, no async, no injector awareness — the host-side resolve step decoupled from whatever HID / Apple-native / runner-side mechanism the caller wants to drive afterward.

This crate exists because the “fetch tree → resolve selector → compute centroid → normalize into app frame” half of a typical iOS automation tap call is generic across smix, maestro, Appium, and anyone building their own automation framework. Extracting it gives you a stone-sized dependency without dragging in a specific injector or runner-client.

§Quickstart

use smix_host_coord_resolver::{HostResolveError, resolve_to_norm_coord};
use smix_screen::{A11yNode, Rect};
use smix_selector::{Modifiers, Pattern, Selector};

let mut root = mk("root", Rect { x: 0.0, y: 0.0, w: 390.0, h: 844.0 });
root.children.push(mk("Login", Rect { x: 50.0, y: 100.0, w: 200.0, h: 40.0 }));

let sel = Selector::Text {
    text: Pattern::text("Login"),
    modifiers: Modifiers::default(),
};

let (nx, ny) = resolve_to_norm_coord(&root, &sel).unwrap();
// Hand (nx, ny) to whatever injector you have:
// my_runner.tap_at_norm_coord(nx, ny).await?;

§Error variants

HostResolveError is fine-grained so callers can map each variant to their own failure type:

VariantMeaning
NotFoundselector matched nothing in the tree (after visibility filter)
EmptyMatchedFramematched node has zero-or-negative w or h
UnknownAppFrametree.bounds has zero-or-negative w or h
CentroidOutOfFrame { nx, ny }computed normalized coord is outside (0, 1)

§When to reach for this

Use casePick
Building an iOS automation library and want host-side resolve as a stonesmix-host-coord-resolver
Already use smix-selector-resolver and want the centroid step toosmix-host-coord-resolver
Need a full driver with tap / fill / scroll semantics + retryuse smix-driver (consumes this stone)
Doing physical-coord tap (not normalized)normalize by your viewport first, then use this

§Scope

  • ✅ Pure function fn resolve_to_norm_coord(tree, selector) -> Result<(f64, f64), _>
  • ✅ Reuses smix-selector-resolver for the tree walk
  • ✅ Centroid + normalize math in (0, 1) app-frame coordinates
  • ❌ No async, no I/O, no injector
  • ❌ No retry / implicit wait — that’s smix-driver’s job

§License

Dual-licensed under either:

at your option. smix-host-coord-resolver — pure pipeline from (A11yNode, Selector) to normalized (nx, ny) tap coordinate, with no I/O.

This is the host-side resolve step decoupled from any specific injector (HID / Apple native event chain / runner-side mode=resolve / etc.). Higher-level orchestrators (smix-driver, user code) fetch the tree, call this stone to compute the tap coord, then hand it to their preferred injector.

§Stone scope

Pure function, no async, no I/O, no awareness of any specific injector implementation. Any iOS-automation project that has an A11yNode (or equivalent that maps to one) and a structured selector can adopt this crate to get host-side resolve.

§Pipeline (mirrors smix-driver’s tap middle section)

  1. resolve_selector(tree, selector) — DFS + spatial filter + visibility check.
  2. Sanity-check matched node frame (w*h > 0).
  3. Compute centroid in app coords.
  4. Normalize by tree.bounds (app frame).
  5. Reject (nx, ny) outside (0, 1) as offscreen.

§Example

use smix_host_coord_resolver::{resolve_to_norm_coord, HostResolveError};
use smix_screen::{A11yNode, Rect};
use smix_selector::{Modifiers, Pattern, Selector};

fn mk(label: &str, bounds: Rect) -> A11yNode {
    A11yNode {
        raw_type: "other".into(),
        element_type_raw: 1,
        role: None, identifier: None,
        label: Some(label.into()),
        title: None, placeholder_value: None, value: None, text: None,
        bounds, enabled: true, selected: false, has_focus: false,
        visible: true, children: vec![],
    }
}

let mut root = mk("root", Rect { x: 0.0, y: 0.0, w: 390.0, h: 844.0 });
root.children.push(mk("Login", Rect { x: 50.0, y: 100.0, w: 200.0, h: 40.0 }));

let sel = Selector::Text {
    text: Pattern::text("Login"),
    modifiers: Modifiers::default(),
};
let (nx, ny) = resolve_to_norm_coord(&root, &sel).unwrap();
assert!((nx - 150.0 / 390.0).abs() < 1e-9);
assert!((ny - 120.0 / 844.0).abs() < 1e-9);

Enums§

HostResolveError
Errors returned by resolve_to_norm_coord.

Functions§

resolve_to_norm_coord
Resolve selector against tree and produce a normalized tap coordinate (nx, ny) in (0, 1).