89 lines
2.9 KiB
Go
89 lines
2.9 KiB
Go
package node
|
|
|
|
import (
|
|
"reflect"
|
|
)
|
|
|
|
// UpdateFrom copies fields from src to dst, but only those fields whose
|
|
// corresponding cobra flag (identified by the "lopt" struct tag) reports
|
|
// as changed. This replaces the old pattern of marshaling to YAML (to strip
|
|
// zero values via omitempty) and then merging with mergo.
|
|
//
|
|
// The changed function should typically be cmd.Flags().Changed.
|
|
func (dst *Node) UpdateFrom(src *Node, changed func(string) bool) {
|
|
recursiveUpdateFrom(reflect.ValueOf(dst).Elem(), reflect.ValueOf(src).Elem(), changed)
|
|
}
|
|
|
|
// UpdateFrom copies fields from src to dst for profiles.
|
|
func (dst *Profile) UpdateFrom(src *Profile, changed func(string) bool) {
|
|
recursiveUpdateFrom(reflect.ValueOf(dst).Elem(), reflect.ValueOf(src).Elem(), changed)
|
|
}
|
|
|
|
func recursiveUpdateFrom(dst, src reflect.Value, changed func(string) bool) {
|
|
srcType := src.Type()
|
|
|
|
for i := 0; i < src.NumField(); i++ {
|
|
field := srcType.Field(i)
|
|
srcField := src.Field(i)
|
|
dstField := dst.Field(i)
|
|
|
|
if !field.IsExported() {
|
|
continue
|
|
}
|
|
|
|
if lopt := field.Tag.Get("lopt"); lopt != "" && field.Tag.Get("comment") != "" {
|
|
// Leaf field with a cobra flag — copy if the flag was changed
|
|
if changed(lopt) {
|
|
dstField.Set(srcField)
|
|
}
|
|
} else if field.Anonymous {
|
|
// Embedded struct (e.g., Profile in Node)
|
|
recursiveUpdateFrom(dstField, srcField, changed)
|
|
} else if field.Type.Kind() == reflect.Ptr && field.Type.Elem().Kind() == reflect.Struct {
|
|
// Pointer-to-struct (e.g., *IpmiConf, *KernelConf)
|
|
if srcField.IsNil() {
|
|
continue
|
|
}
|
|
if dstField.IsNil() {
|
|
dstField.Set(reflect.New(field.Type.Elem()))
|
|
}
|
|
recursiveUpdateFrom(dstField.Elem(), srcField.Elem(), changed)
|
|
} else if field.Type.Kind() == reflect.Struct {
|
|
// Direct struct
|
|
recursiveUpdateFrom(dstField, srcField, changed)
|
|
} else if field.Type.Kind() == reflect.Map {
|
|
// Map fields (e.g., NetDevs, Disks, FileSystems)
|
|
switch field.Type.Elem().Kind() {
|
|
case reflect.String, reflect.Interface:
|
|
// Tags map[string]string — handled separately by tag add/del operations
|
|
continue
|
|
case reflect.Pointer:
|
|
// map[string]*Struct — update entries from src into dst
|
|
if srcField.IsNil() {
|
|
continue
|
|
}
|
|
if dstField.IsNil() {
|
|
dstField.Set(reflect.MakeMap(field.Type))
|
|
}
|
|
for _, key := range srcField.MapKeys() {
|
|
srcEntry := srcField.MapIndex(key)
|
|
if srcEntry.IsNil() {
|
|
continue
|
|
}
|
|
dstEntry := dstField.MapIndex(key)
|
|
if !dstEntry.IsValid() || dstEntry.IsNil() {
|
|
// Create new entry in dst
|
|
dstEntry = reflect.New(field.Type.Elem().Elem())
|
|
dstField.SetMapIndex(key, dstEntry)
|
|
}
|
|
// Map values are not addressable, so we need to work with a copy
|
|
tmpDst := reflect.New(field.Type.Elem().Elem()).Elem()
|
|
tmpDst.Set(dstEntry.Elem())
|
|
recursiveUpdateFrom(tmpDst, srcEntry.Elem(), changed)
|
|
dstField.SetMapIndex(key, tmpDst.Addr())
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|