package xflags import ( "reflect" "strconv" "strings" ) // This function returns the value of a field in a struct, given a path to the field. func getValueFrom[D any](obj D, keyWithDots string) (interface{}, error) { keySlice := strings.Split(keyWithDots, ".") v := reflect.ValueOf(obj) for _, key := range keySlice[0 : len(keySlice)-1] { for v.Kind() == reflect.Ptr { v = v.Elem() } if v.Kind() != reflect.Struct { return nil, newUnsupportedTypePathError(keyWithDots, v.Type()) } v = v.FieldByName(key) } for v.Kind() == reflect.Ptr { v = v.Elem() } // non-supporter type at the top of the path if v.Kind() != reflect.Struct { return nil, newUnsupportedTypeAtTopOfPathError(keyWithDots, v.Type()) } v = v.FieldByName(keySlice[len(keySlice)-1]) if !v.IsValid() { return nil, newInvalidPathError(keyWithDots) } return v.Interface(), nil } // This function sets the value of a field in a struct, given a path to the field. func setValueUsingPath[D any](obj D, keyWithDots string, newValue string) error { keySlice := strings.Split(keyWithDots, ".") v := reflect.ValueOf(obj) for _, key := range keySlice[0 : len(keySlice)-1] { for v.Kind() != reflect.Ptr { v = v.Addr() } if v.Kind() != reflect.Ptr { return newUnsupportedTypePathError(keyWithDots, v.Type()) } elem := v.Elem() if elem.Kind() != reflect.Struct { return newUnsupportedTypePathError(keyWithDots, v.Type()) } v = elem.FieldByName(key) } for v.Kind() == reflect.Ptr { v = v.Elem() } // non-supporter type at the top of the path if v.Kind() != reflect.Struct { return newUnsupportedTypeAtTopOfPathError(keyWithDots, v.Type()) } v = v.FieldByName(keySlice[len(keySlice)-1]) if !v.IsValid() { return newInvalidPathError(keyWithDots) } if !v.CanSet() { return newCannotSetError(keyWithDots) } switch v.Kind() { case reflect.String: v.SetString(newValue) case reflect.Int: s, err := strconv.Atoi(newValue) if err != nil { return err } v.SetInt(int64(s)) case reflect.Bool: v.SetBool(newValue == "true") case reflect.Float64: s, err := strconv.ParseFloat(newValue, 64) if err != nil { return err } v.SetFloat(s) default: return newUnsupportedTypeAtTopOfPathError(keyWithDots, v.Type()) } return nil }