Add semantic configuration profile comparison
This commit is contained in:
@@ -397,6 +397,22 @@ func (document *compositionDocument) semanticRecords() ([]compositionValueRecord
|
||||
return records, nil
|
||||
}
|
||||
|
||||
// compactSemanticRecords returns the same logical atomic paths as
|
||||
// semanticRecords, but represents values as compact JSON-compatible YAML
|
||||
// values rather than the typed structural form used for digesting. It is the
|
||||
// stable human-facing projection for semantic comparisons.
|
||||
func (document *compositionDocument) compactSemanticRecords() ([]compositionValueRecord, error) {
|
||||
if err := validateCompositionDocument(document, "document"); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var records []compositionValueRecord
|
||||
if err := appendCompactCompositionRecords(document.root, &records); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
sort.Slice(records, func(i, j int) bool { return records[i].Path < records[j].Path })
|
||||
return records, nil
|
||||
}
|
||||
|
||||
func appendCompositionRecords(node *compositionNode, records *[]compositionValueRecord) error {
|
||||
if node.kind == yaml.MappingNode && len(node.fields) > 0 {
|
||||
for _, field := range node.fields {
|
||||
@@ -421,6 +437,59 @@ func appendCompositionRecords(node *compositionNode, records *[]compositionValue
|
||||
return nil
|
||||
}
|
||||
|
||||
func appendCompactCompositionRecords(node *compositionNode, records *[]compositionValueRecord) error {
|
||||
if node.kind == yaml.MappingNode && len(node.fields) > 0 {
|
||||
for _, field := range node.fields {
|
||||
if err := appendCompactCompositionRecords(field.value, records); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
value, err := compactCompositionValue(node)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
encoded, err := json.Marshal(value)
|
||||
if err != nil {
|
||||
return fmt.Errorf("serialize configuration value at %s: %w", displayCompositionPath(node.path), err)
|
||||
}
|
||||
*records = append(*records, compositionValueRecord{
|
||||
Path: node.path, Kind: node.kind, Value: string(encoded),
|
||||
Sources: append([]string(nil), node.sources...),
|
||||
})
|
||||
return nil
|
||||
}
|
||||
|
||||
func compactCompositionValue(node *compositionNode) (any, error) {
|
||||
switch node.kind {
|
||||
case yaml.MappingNode:
|
||||
values := make(map[string]any, len(node.fields))
|
||||
for _, field := range node.fields {
|
||||
value, err := compactCompositionValue(field.value)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
values[field.key] = value
|
||||
}
|
||||
return values, nil
|
||||
case yaml.SequenceNode:
|
||||
values := make([]any, 0, len(node.items))
|
||||
for _, item := range node.items {
|
||||
value, err := compactCompositionValue(item)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
values = append(values, value)
|
||||
}
|
||||
return values, nil
|
||||
case yaml.ScalarNode:
|
||||
return canonicalScalarValue(node)
|
||||
default:
|
||||
return nil, fmt.Errorf("configuration at %s has unsupported YAML kind %s", displayCompositionPath(node.path), yamlKindName(node.kind))
|
||||
}
|
||||
}
|
||||
|
||||
type canonicalCompositionField struct {
|
||||
Key string `json:"key"`
|
||||
Value any `json:"value"`
|
||||
|
||||
Reference in New Issue
Block a user