99 lines
3.0 KiB
Go
99 lines
3.0 KiB
Go
package engine
|
|
|
|
import (
|
|
"bytes"
|
|
"errors"
|
|
"io"
|
|
|
|
"gitea.maximumdirect.net/eric/ConfigKit/internal/document"
|
|
yamladapter "gitea.maximumdirect.net/eric/ConfigKit/internal/yaml"
|
|
)
|
|
|
|
var errDiffInspectionUnavailable = errors.New("configkit: diff inspection unavailable")
|
|
var errDiffSnapshotUnavailable = errors.New("configkit: diff snapshot unavailable")
|
|
|
|
// Difference describes one canonical changed region with safe snapshots.
|
|
type Difference struct {
|
|
Path Path
|
|
Left ValueSnapshot
|
|
Right ValueSnapshot
|
|
HiddenChanged bool
|
|
}
|
|
|
|
type diffRegion struct {
|
|
path Path
|
|
left *document.Node
|
|
right *document.Node
|
|
}
|
|
|
|
func prepareDiffRegions(left, right *Inspection) ([]diffRegion, error) {
|
|
if left == nil || left.final == nil || right == nil || right.final == nil {
|
|
return nil, errDiffInspectionUnavailable
|
|
}
|
|
|
|
deltas := compareConfigurationValues(left.final, right.final)
|
|
regions := make([]diffRegion, 0, len(deltas))
|
|
for _, delta := range deltas {
|
|
leftNode, leftPresent := documentAtPathWithLookup(left.final, delta.path, left.immutableLookup)
|
|
if !leftPresent {
|
|
leftNode = nil
|
|
}
|
|
rightNode, rightPresent := documentAtPathWithLookup(right.final, delta.path, right.immutableLookup)
|
|
if !rightPresent {
|
|
rightNode = nil
|
|
}
|
|
regions = append(regions, diffRegion{path: delta.path, left: leftNode, right: rightNode})
|
|
}
|
|
return regions, nil
|
|
}
|
|
|
|
// Diff compares the final frozen values retained by two successful inspections.
|
|
// Values are serialized only after applying the combined sensitivity of both loads.
|
|
func Diff(left, right *Inspection) ([]Difference, error) {
|
|
regions, err := prepareDiffRegions(left, right)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
sensitivity := newDiffSensitivity(left.secrets, right.secrets)
|
|
differences := make([]Difference, 0, len(regions))
|
|
for _, region := range regions {
|
|
paired, supported := preparePairedDiffSnapshot(region.left, region.right, region.path, sensitivity)
|
|
if !supported {
|
|
return nil, errDiffSnapshotUnavailable
|
|
}
|
|
leftSnapshot, err := serializeDiffSnapshot(paired.left, paired.leftRedacted)
|
|
if err != nil {
|
|
return nil, errDiffSnapshotUnavailable
|
|
}
|
|
rightSnapshot, err := serializeDiffSnapshot(paired.right, paired.rightRedacted)
|
|
if err != nil {
|
|
return nil, errDiffSnapshotUnavailable
|
|
}
|
|
differences = append(differences, Difference{
|
|
Path: region.path,
|
|
Left: leftSnapshot,
|
|
Right: rightSnapshot,
|
|
HiddenChanged: paired.hiddenChanged,
|
|
})
|
|
}
|
|
return differences, nil
|
|
}
|
|
|
|
func serializeDiffSnapshot(node *document.Node, redacted bool) (ValueSnapshot, error) {
|
|
if node == nil {
|
|
return ValueSnapshot{}, nil
|
|
}
|
|
var output bytes.Buffer
|
|
if err := writeDiffDocument(&output, node); err != nil {
|
|
return ValueSnapshot{}, errDiffSnapshotUnavailable
|
|
}
|
|
return ValueSnapshot{Present: true, Redacted: redacted, YAML: output.String()}, nil
|
|
}
|
|
|
|
func writeDiffDocument(output io.Writer, node *document.Node) error {
|
|
if err := yamladapter.Write(output, node); err != nil {
|
|
return errDiffSnapshotUnavailable
|
|
}
|
|
return nil
|
|
}
|