-
Notifications
You must be signed in to change notification settings - Fork 7
/
Copy pathresolve.go
125 lines (114 loc) · 3.12 KB
/
resolve.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
package goconf
import (
"flag"
"fmt"
"go/token"
"os"
"reflect"
"regexp"
"strings"
)
var matchFirstCap = regexp.MustCompile("(.)([A-Z][a-z]+)")
var matchAllCap = regexp.MustCompile("([a-z0-9])([A-Z])")
func snakeCase(str string) string {
snake := matchFirstCap.ReplaceAllString(str, "${1}_${2}")
snake = matchAllCap.ReplaceAllString(snake, "${1}_${2}")
return strings.ToLower(snake)
}
func hasArg(fs *flag.FlagSet, s string) bool {
var found bool
fs.Visit(func(flag *flag.Flag) {
if flag.Name == s {
found = true
}
})
return found
}
func innerResolve(options interface{}, flagSet *flag.FlagSet, cfg map[string]interface{}, dstMap map[string]interface{}, autoSet bool, Log func(string)) {
val := reflect.ValueOf(options).Elem()
typ := val.Type()
for i := 0; i < typ.NumField(); i++ {
field := typ.Field(i)
if field.Anonymous {
var fieldPtr reflect.Value
switch val.FieldByName(field.Name).Kind() {
case reflect.Struct:
fieldPtr = val.FieldByName(field.Name).Addr()
case reflect.Ptr:
fieldPtr = reflect.Indirect(val).FieldByName(field.Name)
}
if !fieldPtr.IsNil() {
innerResolve(fieldPtr.Interface(), flagSet, cfg, dstMap, autoSet, Log)
}
continue
}
if !token.IsExported(field.Name) {
continue
}
var v interface{}
flagName := field.Tag.Get("flag")
cfgName := field.Tag.Get("cfg")
defaultVal := field.Tag.Get("default")
if flagName == "" {
flagName = snakeCase(field.Name)
}
if cfgName == "" {
cfgName = strings.Replace(flagName, "-", "_", -1)
}
if autoSet {
if flagSet != nil && flagSet.Lookup(flagName) == nil {
if defaultVal != "" {
v = defaultVal
} else {
v = val.Field(i).Interface()
}
if err := coerceAutoSet(v, val.FieldByName(field.Name).Interface(), flagSet, flagName); err != nil {
Log(fmt.Sprintf("auto flag fail, name: %s val: %v err: %s", flagName, v, err.Error()))
} else {
Log(fmt.Sprintf("auto flag succ, name: %s val: %v", flagName, v))
}
}
} else {
// resolve the flags according to priority
if flagSet != nil && hasArg(flagSet, flagName) { // command line flag value
flagInst := flagSet.Lookup(flagName)
v = flagInst.Value.String()
} else if envVal, ok := os.LookupEnv(flagName); ok { // env value
v = envVal
} else if cfgVal, ok := cfg[cfgName]; ok { // config file value
v = cfgVal
} else if defaultVal != "" { // default value
v = defaultVal
} else {
v = val.Field(i).Interface()
}
fieldVal := val.FieldByName(field.Name)
coerced, err := coerce(v, fieldVal.Interface(), field.Tag.Get("arg"))
if err != nil {
Log(fmt.Sprintf("coerce fail: %v for %s (%+v) - %s", v, field.Name, fieldVal, err))
}
if coerced != nil {
func(){
defer func() {
if reason := recover(); reason != nil {
switch value :=coerced.(type) {
case string:
fieldVal.SetString(value)
default:
panic(fmt.Errorf("%v", reason))
}
}
}()
fieldVal.Set(reflect.ValueOf(coerced))
}()
}
if dstMap != nil {
if err == nil {
dstMap[flagName] = coerced
} else {
dstMap[flagName] = v
}
}
}
}
}