pub struct SimRegistry { /* private fields */ }Expand description
Loaded view of the registry, keyed by alias.
Implementations§
Source§impl SimRegistry
impl SimRegistry
Sourcepub fn register(
path: &Path,
alias: &str,
sim: RegisteredSim,
) -> Result<RegisterOutcome, RegistryError>
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.
Sourcepub fn unregister(
path: &Path,
alias: &str,
) -> Result<RegisteredSim, RegistryError>
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.
Sourcepub fn allow_destructive(
path: &Path,
device_ref: &str,
) -> Result<(String, bool), RegistryError>
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.
Sourcepub fn load(path: &Path) -> Result<Self, RegistryError>
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.
Sourcepub fn machine_dir() -> Option<PathBuf>
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.
Sourcepub fn read_paths(start: &Path) -> Vec<PathBuf>
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.
Sourcepub fn open_all(start: &Path) -> MergedRegistry
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.
Sourcepub fn discover(start: &Path) -> Option<PathBuf>
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.
Sourcepub fn resolve(&self, device_ref: &str) -> Result<String, RegistryError>
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.
Sourcepub fn merge(sources: impl IntoIterator<Item = Self>) -> Self
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.
Sourcepub fn migrate(
into: &Path,
sources: &[PathBuf],
) -> Result<MigrationReport, RegistryError>
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.
Sourcepub fn migrate_dry_run(
into: &Path,
sources: &[PathBuf],
) -> Result<MigrationReport, RegistryError>
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.
Sourcepub fn all(&self) -> impl Iterator<Item = (&str, &RegisteredSim)>
pub fn all(&self) -> impl Iterator<Item = (&str, &RegisteredSim)>
Every alias and what it points at.
Sourcepub fn insert(&mut self, alias: impl Into<String>, sim: RegisteredSim)
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.
Sourcepub fn sims(&self) -> &BTreeMap<String, RegisteredSim>
pub fn sims(&self) -> &BTreeMap<String, RegisteredSim>
Every registered sim, keyed by alias.
Sourcepub fn lookup(&self, device_ref: &str) -> Option<&RegisteredSim>
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
impl Clone for SimRegistry
Source§fn clone(&self) -> SimRegistry
fn clone(&self) -> SimRegistry
1.0.0 (const: unstable) · Source§fn clone_from(&mut self, source: &Self)
fn clone_from(&mut self, source: &Self)
source. Read more