-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathmain.go
More file actions
118 lines (109 loc) · 3.83 KB
/
main.go
File metadata and controls
118 lines (109 loc) · 3.83 KB
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
package struct2pflag
import (
"reflect"
"strings"
"github.com/spf13/pflag"
)
type tagType int // tag type
const (
TAG_NONE tagType = iota // no tag
TAG_FLAG // flag tag
TAG_PFLAG // pflag tag
)
// Bind binds the exported fields of a configuration struct to flags on the given pflag.FlagSet.
//
// Bind expects cfg to be a pointer to a struct. It walks the struct's exported fields and, for each
// field that carries either a "pflag" or "flag" struct tag, registers a corresponding flag on fs.
// Fields without those tags or unexported fields are ignored.
//
// Tag formats:
// - flag: "longname,usage"
// If the tag does not contain a comma the tag value is treated as the usage text and the
// long flag name defaults to the lowercase field name.
// - pflag: "longname,shortname,usage"
// If shortname is omitted (no second comma) shortname is set to the empty string and the
// remainder is treated as usage. If the tag doesn't contain a comma at all the behavior is
// the same as for "flag".
//
// Recursion:
// - If a field is itself a struct, Bind is called recursively on that struct value.
// - If a field is a pointer to a struct, the pointer is followed and Bind is called only if the
// pointer is non-nil (nil pointers are skipped).
//
// Supported field kinds:
// - bool, int, uint, string
//
// For each supported field Bind registers the corresponding fs.*VarP binding using the field's
// address and the field's current value as the flag default.
//
// Notes:
// - Only exported struct fields are considered for binding.
// - The function uses the field's current value as the default for the registered flag.
// - shortname for pflag entries may be supplied as the second tag component; if omitted an
// empty short name is used.
func Bind(fs *pflag.FlagSet, cfg interface{}) {
v := reflect.ValueOf(cfg).Elem()
t := v.Type()
for i := range v.NumField() {
f := v.Field(i)
field := t.Field(i)
if !field.IsExported() { // unexported field, skip
continue
}
var desc string
var ok bool
var t tagType
if desc, ok = field.Tag.Lookup("pflag"); ok {
t = TAG_PFLAG
} else if desc, ok = field.Tag.Lookup("flag"); ok {
t = TAG_FLAG
} else {
continue
}
switch f.Kind() {
case reflect.Struct:
Bind(fs, f.Addr().Interface()) // Bind the struct field
continue
case reflect.Pointer:
if f.Type().Elem().Kind() == reflect.Struct && !f.IsNil() {
Bind(fs, f.Interface()) // Bind the struct pointer field
continue
}
}
var longname, shortname, usage string
switch t {
case TAG_FLAG:
if longname, usage, ok = strings.Cut(desc, ","); !ok {
longname = strings.ToLower(field.Name)
usage = desc
}
case TAG_PFLAG:
var rest string
if longname, rest, ok = strings.Cut(desc, ","); !ok {
longname = strings.ToLower(field.Name)
usage = desc
} else if shortname, usage, ok = strings.Cut(rest, ","); !ok {
shortname = ""
usage = rest
}
}
switch f.Kind() {
case reflect.Bool:
fs.BoolVarP(f.Addr().Interface().(*bool), longname, shortname, f.Bool(), usage)
case reflect.Int:
fs.IntVarP(f.Addr().Interface().(*int), longname, shortname, int(f.Int()), usage)
case reflect.Uint:
fs.UintVarP(f.Addr().Interface().(*uint), longname, shortname, uint(f.Uint()), usage)
case reflect.String:
fs.StringVarP(f.Addr().Interface().(*string), longname, shortname, f.String(), usage)
}
}
}
// BindDefault registers flags for the given configuration value on the
// default pflag.CommandLine flag set. It is a convenience wrapper around Bind
// that saves callers from having to specify the FlagSet explicitly — pass a
// pointer to your config struct and BindDefault will create the corresponding
// command-line flags on the global pflag.CommandLine.
func BindDefault(cfg interface{}) {
Bind(pflag.CommandLine, cfg)
}