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) }