File: retrieve.go

package info (click to toggle)
golang-github-thoas-go-funk 0.9.3-1
  • links: PTS, VCS
  • area: main
  • in suites: bookworm, forky, sid, trixie
  • size: 504 kB
  • sloc: makefile: 10
file content (146 lines) | stat: -rw-r--r-- 3,482 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
package funk

import (
	"reflect"
	"strings"
)

// Get retrieves the value from given path, retriever can be modified with available RetrieverOptions
func Get(out interface{}, path string, opts ...option) interface{} {
	options := newOptions(opts...)

	result := get(reflect.ValueOf(out), path, opts...)
	// valid kind and we can return a result.Interface() without panic
	if result.Kind() != reflect.Invalid && result.CanInterface() {
		// if we don't allow zero and the result is a zero value return nil
		if !options.allowZero && result.IsZero() {
			return nil
		}
		// if the result kind is a pointer and its nil return nil
		if result.Kind() == reflect.Ptr && result.IsNil() {
			return nil
		}
		// return the result interface (i.e the zero value of it)
		return result.Interface()
	}

	return nil
}

// GetOrElse retrieves the value of the pointer or default.
func GetOrElse(v interface{}, def interface{}) interface{} {
	val := reflect.ValueOf(v)
	if v == nil || (val.Kind() == reflect.Ptr && val.IsNil()) {
		return def
	} else if val.Kind() != reflect.Ptr {
		return v
	}
	return val.Elem().Interface()
}

func get(value reflect.Value, path string, opts ...option) reflect.Value {
	options := newOptions(opts...)

	if value.Kind() == reflect.Slice || value.Kind() == reflect.Array {
		var resultSlice reflect.Value

		length := value.Len()

		if length == 0 {
			zeroElement := reflect.Zero(value.Type().Elem())
			pathValue := get(zeroElement, path)
			value = reflect.MakeSlice(reflect.SliceOf(pathValue.Type()), 0, 0)

			return value
		}

		for i := 0; i < length; i++ {
			item := value.Index(i)

			resultValue := get(item, path)

			if resultValue.Kind() == reflect.Invalid || (resultValue.IsZero() && !options.allowZero) {
				continue
			}

			resultType := resultValue.Type()

			if resultSlice.Kind() == reflect.Invalid {
				resultType := reflect.SliceOf(resultType)

				resultSlice = reflect.MakeSlice(resultType, 0, 0)
			}

			resultSlice = reflect.Append(resultSlice, resultValue)
		}

		// if the result is a slice of a slice, we need to flatten it
		if resultSlice.Kind() != reflect.Invalid && resultSlice.Type().Elem().Kind() == reflect.Slice {
			return flattenDeep(resultSlice)
		}

		return resultSlice
	}

	quoted := false
	parts := strings.FieldsFunc(path, func(r rune) bool {
		if r == '"' {
			quoted = !quoted
		}
		return !quoted && r == '.'
	})

	for i, part := range parts {
		parts[i] = strings.Trim(part, "\"")
	}

	for _, part := range parts {
		value = redirectValue(value)
		kind := value.Kind()

		switch kind {
		case reflect.Invalid:
			continue
		case reflect.Struct:
			if isNilIndirection(value, part) {
				return reflect.ValueOf(nil)
			}
			value = value.FieldByName(part)
		case reflect.Map:
			value = value.MapIndex(reflect.ValueOf(part))
		case reflect.Slice, reflect.Array:
			value = get(value, part)
		default:
			return reflect.ValueOf(nil)
		}
	}

	return value
}

func isNilIndirection(v reflect.Value, name string) bool {
	vType := v.Type()
	for i := 0; i < vType.NumField(); i++ {
		field := vType.Field(i)
		if !isEmbeddedStructPointerField(field) {
			return false
		}

		fieldType := field.Type.Elem()

		_, found := fieldType.FieldByName(name)
		if found {
			return v.Field(i).IsNil()
		}
	}

	return false
}

func isEmbeddedStructPointerField(field reflect.StructField) bool {
	if !field.Anonymous {
		return false
	}

	return field.Type.Kind() == reflect.Ptr && field.Type.Elem().Kind() == reflect.Struct
}