mirror of
https://github.com/blacktop/ipsw.git
synced 2026-06-07 12:27:36 +00:00
Refactor diff jobs into task-owned renderers with persistent cache support, root-confined walkers, and quieter Mach-O/Firmware output.
111 lines
3.9 KiB
Go
111 lines
3.9 KiB
Go
package diff
|
|
|
|
import (
|
|
mcmd "github.com/blacktop/ipsw/internal/commands/macho"
|
|
)
|
|
|
|
// buildReport assembles the stable per-section report DTO that the
|
|
// JSON output path marshals. Mount-based jobs (machos, ents, features,
|
|
// localizations, launchd, files) and the migrated top-level tasks
|
|
// (kexts, kdks, firmwares, iboot, sandbox) contribute via their
|
|
// JSON() payloads keyed by JSONKey() (duck-typed through
|
|
// [reportContributor]); the still-inlined DSC/Dylibs section comes
|
|
// straight off the [Diff] fields. The map preserves the legacy
|
|
// `omitempty` shape: zero values for each contributing field are
|
|
// dropped so the JSON output remains byte-compatible with the pre-DTO
|
|
// `json.MarshalIndent(d, …)` path modulo whitespace normalization (the
|
|
// parity tests cover both raw bytes and map round-trip equivalence).
|
|
func buildReport(d *Diff) (map[string]any, error) {
|
|
report := make(map[string]any, 13)
|
|
|
|
// Title — top-level scalar, legacy `Diff.Title` had `omitempty`.
|
|
if d.Title != "" {
|
|
report["title"] = d.Title
|
|
}
|
|
|
|
// DSC/Dylibs stays mount-bound; preserved directly off the [Diff]
|
|
// field until the DSC task migration lands.
|
|
if d.Dylibs != nil {
|
|
report["dylibs"] = d.Dylibs
|
|
}
|
|
|
|
// Migrated top-level tasks contribute through their renderers.
|
|
addMachoDiffIfNotNil(report, newKextsTask(d))
|
|
addStringIfNotEmpty(report, newKDKsTask(d))
|
|
addMachoDiffIfNotNil(report, newFirmwaresTask(d))
|
|
addIBootIfNotNil(report, newIBootTask(d))
|
|
addStringIfNotEmpty(report, newSandboxTask(d))
|
|
|
|
// Mount-based jobs route through their renderers. JSON() returns the
|
|
// underlying typed field, so the per-key zero-value guards below
|
|
// reproduce the original `omitempty` behavior.
|
|
addMapIfNotEmpty(report, newMachosRenderer(d.Machos))
|
|
addMapIfNotEmpty(report, newEntsRenderer(d.Ents))
|
|
addMapIfNotEmpty(report, newFeaturesRenderer(d.Features))
|
|
addMapIfNotEmpty(report, newLocsRenderer(d.Localizations))
|
|
addPtrIfNotNil(report, newFilesRenderer(d.Files))
|
|
addStringIfNotEmpty(report, newLaunchdRenderer(d.Launchd))
|
|
|
|
return report, nil
|
|
}
|
|
|
|
// reportContributor is the minimal renderer surface buildReport relies on.
|
|
// Each helper below narrows the JSON() return type so the `omitempty`
|
|
// equivalent guard is exact, not reflection-based.
|
|
type reportContributor interface {
|
|
JSONKey() string
|
|
JSON() any
|
|
}
|
|
|
|
// addMapIfNotEmpty embeds the renderer's payload when JSON() returns a
|
|
// non-empty map. Mirrors `omitempty` on a `map[K]V` field.
|
|
func addMapIfNotEmpty(report map[string]any, r reportContributor) {
|
|
switch v := r.JSON().(type) {
|
|
case map[string]string:
|
|
if len(v) > 0 {
|
|
report[r.JSONKey()] = v
|
|
}
|
|
case map[string]*PlistDiff:
|
|
if len(v) > 0 {
|
|
report[r.JSONKey()] = v
|
|
}
|
|
case map[string]*mcmd.MachoDiff:
|
|
if len(v) > 0 {
|
|
report[r.JSONKey()] = v
|
|
}
|
|
}
|
|
}
|
|
|
|
// addPtrIfNotNil embeds the renderer's payload when JSON() returns a
|
|
// non-nil pointer. Mirrors `omitempty` on a `*T` field.
|
|
func addPtrIfNotNil(report map[string]any, r reportContributor) {
|
|
if v, ok := r.JSON().(*FileDiff); ok && v != nil {
|
|
report[r.JSONKey()] = v
|
|
}
|
|
}
|
|
|
|
// addStringIfNotEmpty embeds the renderer's payload when JSON() returns a
|
|
// non-empty string. Mirrors `omitempty` on a `string` field.
|
|
func addStringIfNotEmpty(report map[string]any, r reportContributor) {
|
|
if s, ok := r.JSON().(string); ok && s != "" {
|
|
report[r.JSONKey()] = s
|
|
}
|
|
}
|
|
|
|
// addMachoDiffIfNotNil embeds the renderer's payload when JSON() returns
|
|
// a non-nil [*mcmd.MachoDiff]. Mirrors `omitempty` on a `*mcmd.MachoDiff`
|
|
// field.
|
|
func addMachoDiffIfNotNil(report map[string]any, r reportContributor) {
|
|
if v, ok := r.JSON().(*mcmd.MachoDiff); ok && v != nil {
|
|
report[r.JSONKey()] = v
|
|
}
|
|
}
|
|
|
|
// addIBootIfNotNil embeds the renderer's payload when JSON() returns a
|
|
// non-nil [*IBootDiff]. Mirrors `omitempty` on a `*IBootDiff` field.
|
|
func addIBootIfNotNil(report map[string]any, r reportContributor) {
|
|
if v, ok := r.JSON().(*IBootDiff); ok && v != nil {
|
|
report[r.JSONKey()] = v
|
|
}
|
|
}
|