1package funk 2 3import ( 4 "reflect" 5 "strings" 6) 7 8// Get retrieves the value from given path, retriever can be modified with available RetrieverOptions 9func Get(out interface{}, path string, opts ...option) interface{} { 10 options := newOptions(opts...) 11 12 result := get(reflect.ValueOf(out), path) 13 // valid kind and we can return a result.Interface() without panic 14 if result.Kind() != reflect.Invalid && result.CanInterface() { 15 // if we don't allow zero and the result is a zero value return nil 16 if !options.allowZero && result.IsZero() { 17 return nil 18 } 19 // if the result kind is a pointer and its nil return nil 20 if result.Kind() == reflect.Ptr && result.IsNil() { 21 return nil 22 } 23 // return the result interface (i.e the zero value of it) 24 return result.Interface() 25 } 26 27 return nil 28} 29 30// GetOrElse retrieves the value of the pointer or default. 31func GetOrElse(v interface{}, def interface{}) interface{} { 32 val := reflect.ValueOf(v) 33 if v == nil || (val.Kind() == reflect.Ptr && val.IsNil()) { 34 return def 35 } else if val.Kind() != reflect.Ptr { 36 return v 37 } 38 return val.Elem().Interface() 39} 40 41func get(value reflect.Value, path string) reflect.Value { 42 if value.Kind() == reflect.Slice || value.Kind() == reflect.Array { 43 var resultSlice reflect.Value 44 45 length := value.Len() 46 47 if length == 0 { 48 zeroElement := reflect.Zero(value.Type().Elem()) 49 pathValue := get(zeroElement, path) 50 value = reflect.MakeSlice(reflect.SliceOf(pathValue.Type()), 0, 0) 51 52 return value 53 } 54 55 for i := 0; i < length; i++ { 56 item := value.Index(i) 57 58 resultValue := get(item, path) 59 60 if resultValue.Kind() == reflect.Invalid || resultValue.IsZero() { 61 continue 62 } 63 64 resultType := resultValue.Type() 65 66 if resultSlice.Kind() == reflect.Invalid { 67 resultType := reflect.SliceOf(resultType) 68 69 resultSlice = reflect.MakeSlice(resultType, 0, 0) 70 } 71 72 resultSlice = reflect.Append(resultSlice, resultValue) 73 } 74 75 // if the result is a slice of a slice, we need to flatten it 76 if resultSlice.Kind() != reflect.Invalid && resultSlice.Type().Elem().Kind() == reflect.Slice { 77 return flattenDeep(resultSlice) 78 } 79 80 return resultSlice 81 } 82 83 parts := strings.Split(path, ".") 84 85 for _, part := range parts { 86 value = redirectValue(value) 87 kind := value.Kind() 88 89 switch kind { 90 case reflect.Invalid: 91 continue 92 case reflect.Struct: 93 value = value.FieldByName(part) 94 case reflect.Map: 95 value = value.MapIndex(reflect.ValueOf(part)) 96 case reflect.Slice, reflect.Array: 97 value = get(value, part) 98 default: 99 return reflect.ValueOf(nil) 100 } 101 } 102 103 return value 104} 105