Files
configkit/internal/engine/secret_walk.go

68 lines
2.0 KiB
Go

package engine
import (
"cmp"
"reflect"
"slices"
)
type secretSlotAccess uint8
const (
secretSlotsReadOnly secretSlotAccess = iota
secretSlotsWritable
)
// walkSecretSlots visits schema-bound secret references. Writable traversal
// copies map entries into addressable values and writes them back; read-only
// traversal borrows them. The visitor owns the operation performed at each slot.
func walkSecretSlots(root reflect.Value, descriptor *typeDescriptor, access secretSlotAccess, visit func(reflect.Value, Path, bool)) {
var walk func(reflect.Value, *typeDescriptor, Path, bool)
walk = func(value reflect.Value, descriptor *typeDescriptor, path Path, optional bool) {
if descriptor.kind == descriptorPointer {
if !value.IsNil() {
walk(value.Elem(), descriptor.element, path, descriptor.element.kind == descriptorSecret)
}
return
}
switch descriptor.kind {
case descriptorSecret:
visit(value, path, optional)
case descriptorStruct:
for _, member := range descriptor.members {
memberPath := path
if !member.inline {
memberPath = path.Key(member.name)
}
walk(value.Field(member.index), member.descriptor, memberPath, false)
}
case descriptorMap:
if value.IsNil() {
return
}
keys := value.MapKeys()
slices.SortFunc(keys, func(left, right reflect.Value) int { return cmp.Compare(left.String(), right.String()) })
for _, key := range keys {
entry := value.MapIndex(key)
if access == secretSlotsWritable {
copy := reflect.New(value.Type().Elem()).Elem()
copy.Set(entry)
entry = copy
}
walk(entry, descriptor.element, path.Key(key.String()), false)
if access == secretSlotsWritable {
value.SetMapIndex(key, entry)
}
}
case descriptorSlice, descriptorArray:
if descriptor.kind == descriptorSlice && value.IsNil() {
return
}
for index := 0; index < value.Len(); index++ {
walk(value.Index(index), descriptor.element, path.Index(index), false)
}
}
}
walk(root, descriptor, Path{}, false)
}