helmfile/tmpl/get_or_nil.go

61 lines
1.5 KiB
Go

package tmpl
import (
"fmt"
"reflect"
"strings"
)
type noValueError struct {
msg string
}
func (e *noValueError) Error() string {
return e.msg
}
func get(path string, obj interface{}) (interface{}, error) {
if path == "" {
return obj, nil
}
keys := strings.Split(path, ".")
switch typedObj := obj.(type) {
case map[string]interface{}:
v, ok := typedObj[keys[0]]
if !ok {
return nil, &noValueError{fmt.Sprintf("no value exist for key \"%s\" in %v", keys[0], typedObj)}
}
return get(strings.Join(keys[1:], "."), v)
case map[interface{}]interface{}:
v, ok := typedObj[keys[0]]
if !ok {
return nil, &noValueError{fmt.Sprintf("no value exist for key \"%s\" in %v", keys[0], typedObj)}
}
return get(strings.Join(keys[1:], "."), v)
default:
maybeStruct := reflect.ValueOf(typedObj)
if maybeStruct.NumField() < 1 {
return nil, &noValueError{fmt.Sprintf("unexpected type(%v) of value for key \"%s\": it must be either map[string]interface{} or any struct", reflect.TypeOf(obj), keys[0])}
}
f := maybeStruct.FieldByName(keys[0])
if !f.IsValid() {
return nil, &noValueError{fmt.Sprintf("no field named \"%s\" exist in %v", keys[0], typedObj)}
}
v := f.Interface()
return get(strings.Join(keys[1:], "."), v)
}
}
func getOrNil(path string, o interface{}) (interface{}, error) {
v, err := get(path, o)
if err != nil {
switch err.(type) {
case *noValueError:
return nil, nil
default:
return nil, err
}
}
return v, nil
}