initial config manager
This commit is contained in:
@@ -0,0 +1,140 @@
|
||||
package confmgr
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"sort"
|
||||
)
|
||||
|
||||
// ChangeStatus classifies a single diff entry.
|
||||
type ChangeStatus string
|
||||
|
||||
const (
|
||||
StatusAdded ChangeStatus = "added"
|
||||
StatusRemoved ChangeStatus = "removed"
|
||||
StatusChanged ChangeStatus = "changed"
|
||||
StatusUnchanged ChangeStatus = "unchanged"
|
||||
)
|
||||
|
||||
// SetDiffEntry is one parameter-level difference between two config sets.
|
||||
type SetDiffEntry struct {
|
||||
Key string `json:"key"`
|
||||
Status ChangeStatus `json:"status"`
|
||||
Left *Parameter `json:"left,omitempty"`
|
||||
Right *Parameter `json:"right,omitempty"`
|
||||
}
|
||||
|
||||
// InstanceDiffEntry is one value-level difference between two config instances.
|
||||
type InstanceDiffEntry struct {
|
||||
Key string `json:"key"`
|
||||
Status ChangeStatus `json:"status"`
|
||||
Left any `json:"left,omitempty"`
|
||||
Right any `json:"right,omitempty"`
|
||||
}
|
||||
|
||||
// DiffSets compares two config sets parameter-by-parameter (keyed by Key),
|
||||
// returning entries sorted by key. Both sides are included so the frontend can
|
||||
// render either unified or side-by-side.
|
||||
func DiffSets(left, right ConfigSet) []SetDiffEntry {
|
||||
keys := map[string]bool{}
|
||||
li := map[string]Parameter{}
|
||||
ri := map[string]Parameter{}
|
||||
for _, p := range left.Parameters {
|
||||
li[p.Key] = p
|
||||
keys[p.Key] = true
|
||||
}
|
||||
for _, p := range right.Parameters {
|
||||
ri[p.Key] = p
|
||||
keys[p.Key] = true
|
||||
}
|
||||
out := make([]SetDiffEntry, 0, len(keys))
|
||||
for k := range keys {
|
||||
l, lok := li[k]
|
||||
r, rok := ri[k]
|
||||
e := SetDiffEntry{Key: k}
|
||||
switch {
|
||||
case lok && !rok:
|
||||
e.Status = StatusRemoved
|
||||
lp := l
|
||||
e.Left = &lp
|
||||
case !lok && rok:
|
||||
e.Status = StatusAdded
|
||||
rp := r
|
||||
e.Right = &rp
|
||||
default:
|
||||
lp, rp := l, r
|
||||
e.Left, e.Right = &lp, &rp
|
||||
if paramEqual(l, r) {
|
||||
e.Status = StatusUnchanged
|
||||
} else {
|
||||
e.Status = StatusChanged
|
||||
}
|
||||
}
|
||||
out = append(out, e)
|
||||
}
|
||||
sort.Slice(out, func(i, j int) bool { return out[i].Key < out[j].Key })
|
||||
return out
|
||||
}
|
||||
|
||||
// DiffInstances compares two config instances value-by-value (keyed by
|
||||
// parameter key), returning entries sorted by key.
|
||||
func DiffInstances(left, right ConfigInstance) []InstanceDiffEntry {
|
||||
keys := map[string]bool{}
|
||||
for k := range left.Values {
|
||||
keys[k] = true
|
||||
}
|
||||
for k := range right.Values {
|
||||
keys[k] = true
|
||||
}
|
||||
out := make([]InstanceDiffEntry, 0, len(keys))
|
||||
for k := range keys {
|
||||
lv, lok := left.Values[k]
|
||||
rv, rok := right.Values[k]
|
||||
e := InstanceDiffEntry{Key: k, Left: lv, Right: rv}
|
||||
switch {
|
||||
case lok && !rok:
|
||||
e.Status = StatusRemoved
|
||||
case !lok && rok:
|
||||
e.Status = StatusAdded
|
||||
case valueEqual(lv, rv):
|
||||
e.Status = StatusUnchanged
|
||||
default:
|
||||
e.Status = StatusChanged
|
||||
}
|
||||
out = append(out, e)
|
||||
}
|
||||
sort.Slice(out, func(i, j int) bool { return out[i].Key < out[j].Key })
|
||||
return out
|
||||
}
|
||||
|
||||
func paramEqual(a, b Parameter) bool {
|
||||
if a.Label != b.Label || a.Group != b.Group || a.Subgroup != b.Subgroup ||
|
||||
a.DS != b.DS || a.Signal != b.Signal || a.Type != b.Type ||
|
||||
a.Mandatory != b.Mandatory || a.Unit != b.Unit || a.Description != b.Description {
|
||||
return false
|
||||
}
|
||||
if !valueEqual(a.Default, b.Default) || !floatPtrEqual(a.Min, b.Min) || !floatPtrEqual(a.Max, b.Max) {
|
||||
return false
|
||||
}
|
||||
if len(a.EnumValues) != len(b.EnumValues) {
|
||||
return false
|
||||
}
|
||||
for i := range a.EnumValues {
|
||||
if a.EnumValues[i] != b.EnumValues[i] {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func floatPtrEqual(a, b *float64) bool {
|
||||
if a == nil || b == nil {
|
||||
return a == b
|
||||
}
|
||||
return *a == *b
|
||||
}
|
||||
|
||||
// valueEqual compares two JSON-decoded scalar values for diff purposes by
|
||||
// rendering them to a canonical string.
|
||||
func valueEqual(a, b any) bool {
|
||||
return fmt.Sprintf("%v", a) == fmt.Sprintf("%v", b)
|
||||
}
|
||||
Reference in New Issue
Block a user