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