Files
simslim-mirror/features.go
Interlap ffd3a13d34 refactor: split into an importable library and a cmd/simslim CLI (#14)
The repo root becomes `package simslim`, an importable library holding every
piece of slimming logic; the CLI moves to `cmd/simslim`. Previously everything
was `package main`, so nothing here could be imported at all.

The library carries no external dependencies — urfave/cli stays on the CLI side,
along with all terminal-facing code: the interactive profile wizard, writeJSON,
truncate/humanBytes, fatal and usage. The library never writes to stdout; it
returns values and reports progress through the Reporter callback.

Renames forced by collisions with existing types: status() -> ReadStatus,
statusForDevice() -> ReadStatusForDevice, diskCleanupPlan() -> PlanDiskCleanup.
The shutdown/boot timeouts move to simctl.go as ShutdownTimeout/BootTimeout.

`go install` gains a path element: github.com/mobai-app/simslim/cmd/simslim.
The installed binary is still named simslim, and -X main.version still applies.
packaging/homebrew/simslim.rb.in is updated to match, since std_go_args passes
no package path.

Verified live against a real simulator: list, profiles, status, doctor, size,
disk-plan, measure, boot, shutdown, on, off, on --except, on --profile, clone,
rename, delete and the interactive wizard all behave identically to the previous
build. JSON output, human output, help text and exit codes are unchanged.
2026-07-23 12:39:20 +02:00

83 lines
3.8 KiB
Go

package simslim
import "fmt"
// Feature is a user-facing capability backed by specific launchd daemons.
// Every label must also live in a Category (enforced in features_test.go), so a
// feature only ever names daemons the tool could actually have turned off.
type Feature struct {
ID string `json:"id"`
Name string `json:"name"`
Labels []string `json:"labels"`
}
// Features maps commonly required capabilities to the daemons they need running.
var Features = []Feature{
{ID: "push", Name: "Push notifications", Labels: []string{"com.apple.apsd"}},
{ID: "storekit", Name: "StoreKit / in-app purchase", Labels: []string{"com.apple.storekitd"}},
{ID: "app-store", Name: "App Store", Labels: []string{"com.apple.appstored", "com.apple.itunesstored"}},
{ID: "universal-links", Name: "Universal links / associated domains", Labels: []string{"com.apple.swcd"}},
{ID: "spotlight", Name: "Spotlight & Settings search", Labels: []string{"com.apple.searchd", "com.apple.searchtoold"}},
{ID: "siri", Name: "Siri & speech", Labels: []string{"com.apple.assistantd", "com.apple.corespeechd"}},
{ID: "icloud", Name: "iCloud sync", Labels: []string{"com.apple.cloudd"}},
{ID: "keychain-sync", Name: "iCloud Keychain", Labels: []string{"com.apple.akd"}},
{ID: "contacts", Name: "Contacts", Labels: []string{"com.apple.contactsd"}},
{ID: "calendar", Name: "Calendar", Labels: []string{"com.apple.calaccessd"}},
{ID: "reminders", Name: "Reminders", Labels: []string{"com.apple.remindd"}},
{ID: "mail", Name: "Mail", Labels: []string{"com.apple.email.maild"}},
{ID: "photos", Name: "Photos library & analysis", Labels: []string{"com.apple.assetsd", "com.apple.photoanalysisd"}},
{ID: "health", Name: "HealthKit", Labels: []string{"com.apple.healthd"}},
{ID: "homekit", Name: "HomeKit", Labels: []string{"com.apple.homed"}},
{ID: "imessage", Name: "iMessage & FaceTime", Labels: []string{"com.apple.identityservicesd"}},
{ID: "widgets", Name: "Widgets & Live Activities", Labels: []string{"com.apple.chronod", "com.apple.liveactivitiesd"}},
{ID: "wallet", Name: "Wallet & passes", Labels: []string{"com.apple.passd"}},
{ID: "maps", Name: "Maps background services", Labels: []string{"com.apple.Maps.mapssyncd"}},
{ID: "weather", Name: "Weather", Labels: []string{"com.apple.weatherd"}},
{ID: "news", Name: "News", Labels: []string{"com.apple.newsd"}},
{ID: "game-center", Name: "Game Center", Labels: []string{"com.apple.gamed"}},
{ID: "find-my", Name: "Find My", Labels: []string{"com.apple.findmy.findmylocated"}},
{ID: "screen-time", Name: "Screen Time", Labels: []string{"com.apple.ScreenTimeAgent"}},
}
func featureByID(id string) (Feature, bool) {
for _, f := range Features {
if f.ID == id {
return f, true
}
}
return Feature{}, false
}
// resolveFeatures maps requested IDs to their Features, erroring on the first
// unknown one so a typo in --requires fails loudly instead of passing silently.
func ResolveFeatures(ids []string) ([]Feature, error) {
out := make([]Feature, 0, len(ids))
for _, id := range ids {
f, ok := featureByID(id)
if !ok {
return nil, fmt.Errorf("unknown feature %q (see `simslim doctor --list`)", id)
}
out = append(out, f)
}
return out, nil
}
// diagnoseFeatures reports, for each feature, which of its daemons the device
// currently has disabled. A feature is OK only when none of them are.
func DiagnoseFeatures(features []Feature, disabled map[string]bool) DoctorOutput {
statuses := make([]FeatureStatus, 0, len(features))
allOK := true
for _, f := range features {
var down []string
for _, l := range f.Labels {
if disabled[l] {
down = append(down, l)
}
}
ok := len(down) == 0
allOK = allOK && ok
statuses = append(statuses, FeatureStatus{ID: f.ID, Name: f.Name, OK: ok, Disabled: down})
}
return DoctorOutput{OK: allOK, Features: statuses}
}