-
Notifications
You must be signed in to change notification settings - Fork 13
/
values.go
137 lines (117 loc) · 3.53 KB
/
values.go
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
// Copyright (c) 2017 Steven Roose <[email protected]>.
// Use of this source code is governed by an MIT-style
// license that can be found in the LICENSE file.
package gonfig
import (
"errors"
"fmt"
"reflect"
)
// setValueByString sets the value by parsing the string.
func setValueByString(v reflect.Value, s string) error {
if isSlice(v) {
if err := parseSlice(v, s); err != nil {
return fmt.Errorf("failed to parse slice value: %v", err)
}
} else {
if err := parseSimpleValue(v, s); err != nil {
return fmt.Errorf("failed to parse value: %v", err)
}
}
return nil
}
// setValue sets the option value to the given value.
// If the tye of the value is assignable or convertible to the type of the
// option value, it is directly set after optional conversion.
// If not, but the value is a string, it is passed to setValueByString.
// If not, and both v and the option's value are is a slice, we try converting
// the slice elements to the right elemens of the options slice.
func setValue(toSet, v reflect.Value) error {
t := toSet.Type()
if v.Type().AssignableTo(t) {
toSet.Set(v)
return nil
}
if v.Type().ConvertibleTo(t) && toSet.Type() != typeOfByteSlice {
toSet.Set(v.Convert(t))
return nil
}
if v.Type().Kind() == reflect.String {
return setValueByString(toSet, v.String())
}
if isSlice(toSet) && v.Type().Kind() == reflect.Slice {
return convertSlice(v, toSet)
}
return convertibleError(v, toSet.Type())
}
// isSupportedType returns whether the type t is supported by gonfig for parsing.
func isSupportedType(t reflect.Type) error {
if t.Implements(typeOfTextUnmarshaler) {
return nil
}
if t == typeOfByteSlice {
return nil
}
switch t.Kind() {
case reflect.Bool:
case reflect.String:
case reflect.Float32, reflect.Float64:
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
case reflect.Struct:
for i := 0; i < t.NumField(); i++ {
if err := isSupportedType(t.Field(i).Type); err != nil {
return fmt.Errorf("struct with unsupported type: %v", err)
}
}
case reflect.Slice:
// All but the fixed-bitsize types.
if err := isSupportedType(t.Elem()); err != nil {
return fmt.Errorf("slice of unsupported type: %v", err)
}
case reflect.Ptr:
if err := isSupportedType(t.Elem()); err != nil {
return fmt.Errorf("pointer to unsupported type: %v", err)
}
case reflect.Map:
if t.Key().Kind() != reflect.String || t.Elem().Kind() != reflect.Interface {
return errors.New("only maps of type map[string]interface{} are supported")
}
default:
return errors.New("type not supported")
}
return nil
}
// isZero checks if the value is the zero value for its type.
func isZero(v reflect.Value) bool {
switch v.Kind() {
case reflect.Func, reflect.Map, reflect.Slice:
return v.IsNil()
case reflect.Array:
z := true
for i := 0; i < v.Len(); i++ {
z = z && isZero(v.Index(i))
}
return z
case reflect.Struct:
z := true
for i := 0; i < v.NumField(); i++ {
z = z && isZero(v.Field(i))
}
return z
case reflect.Ptr:
return isZero(reflect.Indirect(v))
}
// Compare other types directly:
z := reflect.Zero(v.Type())
return v.Interface() == z.Interface()
}
// setSimpleMapValue trues to add the key and value to the map.
func setSimpleMapValue(mapValue reflect.Value, key, value string) error {
v := reflect.New(mapValue.Type().Elem()).Elem()
if err := parseSimpleValue(v, value); err != nil {
return err
}
mapValue.SetMapIndex(reflect.ValueOf(key), v)
return nil
}