Skip to main content

SimRegistry

Struct SimRegistry 

Source
pub struct SimRegistry { /* private fields */ }
Expand description

Loaded view of the registry, keyed by alias.

Implementations§

Source§

impl SimRegistry

Source

pub fn register( path: &Path, alias: &str, sim: RegisteredSim, ) -> Result<RegisterOutcome, RegistryError>

Write sim into the registry under alias.

One key, not a whole file. The read-modify-write this replaces lost an alias whenever two processes registered at once — each read the file, each inserted its own row, and the second write erased the first, with no error on either side.

Source

pub fn set_project_alias( path: &Path, project_key: &str, alias: &str, ) -> Result<(), RegistryError>

Record which alias a project defaults to, keyed by the project’s path. The value is the alias string only — a pointer into the registered devices, never a device fact. What the alias resolves to (UDID, kind, opt-in) stays in the machine registry; this says only “this project drives that one”, which is a project’s to decide and safe to keep per-project (§9 #9: the pointer may live with the project, the facts may not).

§Errors

RegistryError::Io if the store cannot be opened or written.

Source

pub fn project_alias( path: &Path, project_key: &str, ) -> Result<Option<String>, RegistryError>

The alias a project defaults to, or None if it has never set one. Read-only counterpart of Self::set_project_alias.

§Errors

RegistryError::Io if the store cannot be opened or read.

Source

pub fn unregister( path: &Path, alias: &str, ) -> Result<RegisteredSim, RegistryError>

Remove one alias from the registry at path.

The other half of Self::register, and absent until the records moved to machine scope made its absence permanent: a device registered by mistake — a test that wrote to the real book, an alias for a phone somebody no longer has — could be added and never taken back. A registry that only grows stops describing the machine.

Removes the name, not the device. Another alias for the same device keeps working, which is why this takes an alias key rather than a device ref: “forget this device” and “forget this name for it” are different requests, and only the second one is unambiguous.

§Errors

RegistryError::UnknownDevice when no such alias exists. Silently succeeding would let a typo read as a removal.

Source

pub fn allow_destructive( path: &Path, device_ref: &str, ) -> Result<(String, bool), RegistryError>

Allow destructive actions on one registered device, once.

Goes through Self::register rather than rewriting the file, for the reason that function documents: a read-modify-write of the whole registry loses a concurrent registration silently. One key in, one key out.

Returns the alias it was recorded against and whether it was already allowed — the caller can then say “already allowed” instead of implying something changed.

§Errors

RegistryError::UnknownDevice when nothing matches the ref. The opt-in is per device, so there is nothing to record it against — and silently creating an entry would mean allowing destruction on a device nobody registered.

Source

pub fn load(path: &Path) -> Result<Self, RegistryError>

Read every registered sim.

path may be the .smix directory or a legacy sims.json inside it; both land on the same store.

Source

pub fn machine_dir() -> Option<PathBuf>

Where device facts live: this machine, not this checkout.

A simulator is an operating-system object. Its UDID, its runtime version, whether it is booted and who booted it do not change when you cd — so storing them per checkout means four trees on one machine each hold their own answer, which is what they do today. On 2026-08-11 two of them held a lease on the same simulators, and a runner on port 22087 was simultaneously on the books and invisible: the rule says check the owner before touching a runner, and the checkout doing the checking was not the one holding the record.

$XDG_DATA_HOME/smix or ~/.local/share/smix, which is where the runner tree and its version stamp already live — machine scope is not a new idea here, it was just not used for this.

None when neither variable is set, which is the same condition under which the runner tree has no home either; the caller falls back to the checkout and says so.

Source

pub fn read_paths(start: &Path) -> Vec<PathBuf>

Every registry a read may draw on, in precedence order.

SMIX_SIMS_JSON names one registry and means exactly that one: it is how a test works against a book of its own, and a machine-level fallback under it would let the real one leak in.

Otherwise the machine registry first, then whatever checkout is underfoot. The checkout entry keeps books written before this move working until smix sim migrate folds them in; nothing is ever written back to it.

Source

pub fn open_all(start: &Path) -> MergedRegistry

Read every registry that applies and fold them into one.

A source that will not open is skipped rather than fatal: one corrupt book must not strand the devices recorded in the others. Which is why this returns a value and not a Result — there is no failure here other than “nothing was readable anywhere”, and that is an empty registry, which reads the same as a machine where nothing has been registered yet.

Source

pub fn discover(start: &Path) -> Option<PathBuf>

Walk up from start looking for a .smix that holds a registry — either the store or a legacy sims.json.

Source

pub fn resolve(&self, device_ref: &str) -> Result<String, RegistryError>

Resolve a device ref to the identifier its platform is addressed by.

CoreSimulator-form input passes through whether or not it is registered. Otherwise the ref must match an alias key, a deviceName, or the registered identifier itself.

That last one was missing until 2026-08-06, and Self::lookup had it — so the two disagreed about whether a device’s own identifier names it. A real phone found the disagreement: an iOS device UDID is 25 characters, not CoreSimulator’s 36, so it fell past the short-circuit into a search that never looked at the one field it matched. smix runner forward 00008120-… answered “unknown device ref” about a device that was registered right there in the file it was reading.

Source

pub fn merge(sources: impl IntoIterator<Item = Self>) -> Self

All registered sims, keyed by alias. Fold several registries into one, losing nothing.

Four checkouts on this machine each keep their own, and merging them is a one-way door: whatever this drops, the tree that was relying on it stops working, and nobody can tell which of the four to look in. So the rules are chosen to be safe, not tidy.

  • Two aliases for one device: keep both. An alias is how somebody types a device’s name. Dropping one breaks whatever script used it; a duplicate is noise.
  • Conflicting destructive consent: take the stricter. Consent is a per-device authorisation (§9 #1), and merging two books is not a moment to widen it. Granting it again is one command; un-wiping a phone is not a command at all.
  • Same alias, different devices: keep both, the later one under a suffixed alias. Silently overwriting means one tree’s alias stops resolving with no way to know which.

Order-independent for the facts that matter: four checkouts have no natural order, and a merge that depends on read order changes when somebody renames a directory.

Source

pub fn migrate( into: &Path, sources: &[PathBuf], ) -> Result<MigrationReport, RegistryError>

Fold sources into the registry at into, keeping everything.

The merge rules are Self::merge’s; this applies them to books on disk. What it adds to them is a promise about the sources: they are read and left alone. Somebody who has to go back to a smix from before device records became machine-scoped must still find their registry where they left it — and a migration that deletes what it has just copied has no way to be run twice by somebody who is not sure whether it worked.

Writes go through Self::register, one key at a time, for the reason that function documents: a whole-file rewrite loses a concurrent registration without saying so.

Source

pub fn migrate_dry_run( into: &Path, sources: &[PathBuf], ) -> Result<MigrationReport, RegistryError>

What Self::migrate would do, having done nothing.

The same function with the write skipped, rather than a second one that works the answer out again — a rehearsal with its own copy of the rules reports on its copy.

§Errors

As Self::migrate, minus the ones only a write can raise.

Source

pub fn all(&self) -> impl Iterator<Item = (&str, &RegisteredSim)>

Every alias and what it points at.

Source

pub fn insert(&mut self, alias: impl Into<String>, sim: RegisteredSim)

Add or replace one entry. For merging and for tests; the registering path goes through register, which also writes.

Source

pub fn sims(&self) -> &BTreeMap<String, RegisteredSim>

Every registered sim, keyed by alias.

Source

pub fn lookup(&self, device_ref: &str) -> Option<&RegisteredSim>

Look up a RegisteredSim by alias key, device name, or UDID. Returns None if no entry matches any of the three. Mirrors Self::resolve’s match precedence so cli callers can fetch the full spec (e.g. locale field) after they already resolved the UDID.

Trait Implementations§

Source§

impl Clone for SimRegistry

Source§

fn clone(&self) -> SimRegistry

Returns a duplicate of the value. Read more
1.0.0 (const: unstable) · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for SimRegistry

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Default for SimRegistry

Source§

fn default() -> SimRegistry

Returns the “default value” for a type. Read more

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> ToOwned for T
where T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = !

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.