package ckconfig import ( "fmt" "strings" commandkit "go.maximumdirect.net/commandkit" configkit "go.maximumdirect.net/configkit" ) func decodeJSONPointer(path configkit.Path) []string { pointer := path.String() if pointer == "" { return nil } parts := strings.Split(pointer[1:], "/") decoder := strings.NewReplacer("~1", "/", "~0", "~") for index, part := range parts { parts[index] = decoder.Replace(part) } return parts } func placeReportValue(root any, path []string, value any) (any, error) { if len(path) == 0 { return value, nil } current, ok := root.(map[string]any) if !ok { return nil, fmt.Errorf("report path ancestor is not a mapping") } for index, token := range path { if index == len(path)-1 { current[token] = value return root, nil } child, exists := current[token] if !exists { next := make(map[string]any) current[token] = next current = next continue } next, ok := child.(map[string]any) if !ok { return nil, fmt.Errorf("report path ancestor %q is not a mapping", token) } current = next } return root, nil } type diffReport struct { Additions any `json:"additions"` Changes any `json:"changes"` Deletions []string `json:"deletions"` } type detailedDiffReport struct { Scope string `json:"scope"` Differences []detailedDifference `json:"differences"` } type detailedDifference struct { Path string `json:"path"` Before detailedDiffSnapshot `json:"before"` After detailedDiffSnapshot `json:"after"` HiddenChanged bool `json:"hidden_changed"` } type detailedDiffSnapshot struct { Present bool `json:"present"` Redacted bool `json:"redacted"` Value any `json:"value"` } func renderDetailedDiff(scope string, differences []configkit.Difference) (any, error) { report := detailedDiffReport{Scope: scope, Differences: make([]detailedDifference, 0, len(differences))} for _, difference := range differences { before, err := detailedSnapshot(difference.Left) if err != nil { return nil, fmt.Errorf("difference at %q before: %w", difference.Path.String(), err) } after, err := detailedSnapshot(difference.Right) if err != nil { return nil, fmt.Errorf("difference at %q after: %w", difference.Path.String(), err) } report.Differences = append(report.Differences, detailedDifference{Path: difference.Path.String(), Before: before, After: after, HiddenChanged: difference.HiddenChanged}) } return report, nil } func detailedSnapshot(snapshot configkit.ValueSnapshot) (detailedDiffSnapshot, error) { report := detailedDiffSnapshot{Present: snapshot.Present, Redacted: snapshot.Redacted} if snapshot.Present { value, err := commandkit.DecodeYAMLReport(strings.NewReader(snapshot.YAML)) if err != nil { return report, err } report.Value = value } return report, nil } func renderDiffReport(differences []configkit.Difference) (any, error) { report := diffReport{Additions: map[string]any{}, Changes: map[string]any{}, Deletions: []string{}} for _, difference := range differences { path := difference.Path.String() if difference.Left.Present && !difference.Right.Present { report.Deletions = append(report.Deletions, path) continue } if !difference.Right.Present { continue } tokens := decodeJSONPointer(difference.Path) value, err := commandkit.DecodeYAMLReport(strings.NewReader(difference.Right.YAML)) if err != nil { return nil, fmt.Errorf("difference at %q: %w", path, err) } if difference.Left.Present { report.Changes, err = placeReportValue(report.Changes, tokens, value) } else { report.Additions, err = placeReportValue(report.Additions, tokens, value) } if err != nil { return nil, fmt.Errorf("difference at %q: %w", path, err) } } return report, nil }