// Copyright 2022 schukai GmbH // SPDX-License-Identifier: AGPL-3.0 package pathfinder import ( "reflect" "strconv" "strings" ) // GetValue returns the value of a field in a struct, given a path to the field. // The path can contain dots to access nested fields. // The object must be a pointer to a struct, a struct, a map, a slice or an array, // otherwise an error is returned. func GetValue[D any](obj D, keyWithDots string) (any, error) { keySlice := strings.Split(keyWithDots, ".") v := reflect.ValueOf(obj) for _, key := range keySlice[0:] { if !v.IsValid() { return nil, newInvalidPathError(keyWithDots) } switch v.Kind() { case reflect.Ptr, reflect.Interface: v = v.Elem() } switch v.Kind() { case reflect.Map: v = v.MapIndex(reflect.ValueOf(key)) if !v.IsValid() { return nil, newInvalidPathError(keyWithDots) } case reflect.Slice, reflect.Array: index, err := strconv.Atoi(key) if err != nil { return nil, newInvalidPathError(keyWithDots) } // check if index is in range if index >= v.Len() { return nil, newInvalidPathError(keyWithDots) } v = v.Index(index) case reflect.Struct: v = v.FieldByName(key) if !v.IsValid() { return nil, newInvalidPathError(keyWithDots) } default: return nil, newInvalidPathError(keyWithDots) } } if v.Kind() == reflect.Invalid { return nil, newInvalidPathError(keyWithDots) } //for v.Kind() == reflect.Ptr { // v = v.Elem() //} // check if v can interface if !v.CanInterface() { return nil, newInvalidPathError(keyWithDots) } return v.Interface(), nil }