// Copyright (c) HashiCorp, Inc. // SPDX-License-Identifier: MPL-2.0 package prepared_query import ( "fmt" "reflect" ) // visitor is a function that will get called for each string element of a // structure. type visitor func(path string, v reflect.Value) error // visit calls the visitor function for each string it finds, and will descend // recursively into structures and slices. If any visitor returns an error then // the search will stop and that error will be returned. func visit(path string, v reflect.Value, t reflect.Type, fn visitor) error { switch v.Kind() { case reflect.String: return fn(path, v) case reflect.Struct: for i := 0; i < v.NumField(); i++ { vf := v.Field(i) tf := t.Field(i) newPath := path if !tf.Anonymous { newPath = fmt.Sprintf("%s.%s", path, tf.Name) } if err := visit(newPath, vf, tf.Type, fn); err != nil { return err } } case reflect.Slice: for i := 0; i < v.Len(); i++ { vi := v.Index(i) ti := vi.Type() newPath := fmt.Sprintf("%s[%d]", path, i) if err := visit(newPath, vi, ti, fn); err != nil { return err } } case reflect.Map: for _, key := range v.MapKeys() { value := v.MapIndex(key) newValue := reflect.New(value.Type()).Elem() newValue.SetString(value.String()) if err := visit(fmt.Sprintf("%s[%s]", path, key.String()), newValue, newValue.Type(), fn); err != nil { return err } // overwrite the entry in case it was modified by the callback v.SetMapIndex(key, newValue) } } return nil } // walk finds all the string elements of a given structure (and its sub- // structures) and calls the visitor function. Each string found will get // a unique path computed. If any visitor returns an error then the search // will stop and that error will be returned. func walk(obj interface{}, fn visitor) error { v := reflect.ValueOf(obj).Elem() t := v.Type() return visit("", v, t, fn) }