-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathcli.go
143 lines (120 loc) · 2.84 KB
/
cli.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
138
139
140
141
142
143
package lflag
import (
"bytes"
"fmt"
"os"
"sort"
"strings"
)
type sourceCLI struct{}
// NewSourceCLI initializes and returns a new Source which will pull from the
// command line arguments at runtime. It also handles --help and --version
// options
func NewSourceCLI() Source {
return sourceCLI{}
}
func (sc sourceCLI) Parse(pp []Param) (map[string]string, error) {
return parseCLI(os.Args[1:], pp)
}
// split out for testing
func parseCLI(args []string, pp []Param) (map[string]string, error) {
cliM := map[string]Param{}
for _, p := range pp {
cliM["--"+p.Name] = p
}
var arg string
found := map[string]string{}
for {
if len(args) == 0 {
return found, nil
}
arg, args = args[0], args[1:]
argParts := strings.SplitN(arg, "=", 2)
argName := argParts[0]
if argName == "-h" || argName == "--help" {
printfAndExit(cliHelpStr(pp))
} else if argName == "-V" || argName == "--version" {
printfAndExit(Version())
}
var argVal string
var argValOk bool
if len(argParts) == 2 {
argVal = argParts[1]
argValOk = true
}
p, ok := cliM[argName]
if !ok {
continue
}
if p.ParamType == ParamTypeBool {
// check for a true/false value
if !argValOk && len(args) > 0 {
if !strings.HasPrefix(args[0], "-") {
argValOk = true
argVal, args = args[0], args[1:]
}
}
if argValOk {
found[p.Name] = argVal
} else if p.Default == "true" {
found[p.Name] = ""
} else {
found[p.Name] = "true"
}
continue
}
if !argValOk && len(args) > 0 {
argVal, args = args[0], args[1:]
}
found[p.Name] = argVal
}
}
// returns string form of help message. newline will be appended already
func cliHelpStr(pp []Param) string {
sort.Slice(pp, func(i, j int) bool {
return pp[i].Name < pp[j].Name
})
buf := bytes.NewBuffer(make([]byte, 0, 1024))
bufParam := func(p Param) {
fmt.Fprintf(buf, "\t--%s", p.Name)
if p.ParamType == ParamTypeBool {
fmt.Fprintf(buf, " (flag)")
}
fmt.Fprintf(buf, "\n")
if p.Usage != "" {
fmt.Fprintf(buf, "\t\t%s\n", p.Usage)
}
if p.Default != "" {
fmt.Fprintf(buf, "\t\tDefault: %q\n", p.Default)
} else if p.Required {
fmt.Fprintf(buf, "\t\t(Required)\n")
} else {
fmt.Fprintf(buf, "\t\t(Optional)\n")
}
// All parameters span multiple lines, so add a newline between each for
// clarity
fmt.Fprintf(buf, "\n")
}
if HelpPrefix != "" {
fmt.Fprintf(buf, "\n%s", HelpPrefix)
if HelpPrefix[len(HelpPrefix)-1] != '\n' {
// ensure we always write at least one newline
fmt.Fprint(buf, "\n")
}
}
fmt.Fprint(buf, "\n")
for _, p := range pp {
bufParam(p)
}
bufParam(Param{
ParamType: ParamTypeBool,
Name: "help",
Usage: "Show this help message and exit",
})
bufParam(Param{
ParamType: ParamTypeBool,
Name: "version",
Usage: "Print out a build string and exit",
})
return buf.String()
}