forked from zekroTJA/shinpuru
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathparser.go
203 lines (183 loc) · 4.44 KB
/
parser.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
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
package argp
import (
"encoding/json"
"fmt"
"os"
"regexp"
"strings"
)
var argsRx = regexp.MustCompile(`(?:[^\s"]+|"[^"]*")+`)
type flag struct {
flag string
def interface{}
help string
}
// Parser takes an array of arguments and provides
// functionalities to parse flags and values contained.
type Parser struct {
args []string
flags []flag
}
// New initializes a new instance of Parser.
//
// It defaultly takes the value of os.Args[1:]
// as array of arguments. Optionally, you can
// pass a custom array of arguments you want
// to scan.
func New(args ...[]string) (p *Parser) {
p = &Parser{
args: os.Args[1:],
}
if len(args) > 0 {
p.args = args[0]
}
p.args = resplit(p.args)
return
}
// Scan looks for the passed flag (unprefixed) in
// the arguments array. If the flag was found, the
// value of the flag is scanned into the pointer
// of val. If the flag and value was found and valid,
// true is returned. Otherwise, false is returned and
// if an error occurs, the error is returned as well.
//
// Example:
// var config string
// p := argp.New([]string{"--config", "myconfig.yml"})
// ok, err := p.Scan("--config", &config)
// // config = "myconfig.yml"
// // ok = true
// // err = nil
func (p *Parser) Scan(flag string, val interface{}) (ok bool, err error) {
var (
arg string
sval string
i int
pad int
found bool
)
for i, arg = range p.args {
if strings.HasPrefix(arg, flag) {
found = true
break
}
}
if !found {
return
}
if _, isBool := val.(*bool); isBool && len(arg) == len(flag) {
arg += "=true"
}
if len(arg) == len(flag) {
if len(p.args) < i+2 {
return
}
sval = p.args[i+1]
pad++
} else {
split := strings.SplitN(arg, "=", 2)
if len(split) != 2 {
return
}
sval = split[1]
}
if _, isStr := val.(*string); isStr {
sval = "\"" + sval + "\""
}
err = json.Unmarshal([]byte(sval), val)
ok = err == nil
if ok {
p.args = append(p.args[:i], p.args[i+1+pad:]...)
}
return
}
// String is shorthand for Scan with a string flag value.
// It returns the scanned value and an error if the parsing
// failed. If no flag or value was found and a def value was
// passed, def is returned as val.
func (p *Parser) String(flag string, def string, help ...string) (val string, err error) {
ok, err := p.Scan(flag, &val)
if err != nil {
return
}
if !ok {
val = def
}
p.register(flag, help, def)
return
}
// Bool is shorthand for Scan with a bool flag value.
// If the flag was passed (with or wirhout value specified),
// true is returned. If the parsing fails, the error is
// returned. When def is passed and no flag was found, def
// is returned as val.
func (p *Parser) Bool(flag string, def bool, help ...string) (val bool, err error) {
ok, err := p.Scan(flag, &val)
if err != nil {
return
}
if !ok {
val = def
}
p.register(flag, help, def)
return
}
// Int is shorthand for Scan with a integer flag value.
// It returns the scanned value and an error if the parsing
// failed. If no flag or value was found and a def value was
// passed, def is returned as val.
func (p *Parser) Int(flag string, def int, help ...string) (val int, err error) {
ok, err := p.Scan(flag, &val)
if err != nil {
return
}
if !ok {
val = def
}
p.register(flag, help, def)
return
}
// Float is shorthand for Scan with a float flag value.
// It returns the scanned value and an error if the parsing
// failed. If no flag or value was found and a def value was
// passed, def is returned as val.
func (p *Parser) Float(flag string, def float64, help ...string) (val float64, err error) {
ok, err := p.Scan(flag, &val)
if err != nil {
return
}
if !ok {
val = def
}
p.register(flag, help, def)
return
}
// Args returns all other un-scanned arguments of
// the passed arguments array.
//
// Example:
// p := New([]string{"whats", "-n", "up"})
// val, err := p.Bool("-n")
// // val = true
// // err = nil
// // p.Args() = []string{"whats", "up"}
func (p *Parser) Args() []string {
return p.args
}
// Help returns a compiled string listing all
// scanned flag specifications.
func (p *Parser) Help() string {
lines := make([]string, len(p.flags))
for i, f := range p.flags {
lines[i] = fmt.Sprintf(" %s %s [default: %v]\n %s",
f.flag, typName(f.def), f.def, f.help)
}
return strings.Join(lines, "\n")
}
func (p *Parser) register(f string, help []string, def interface{}) {
p.flags = append(p.flags, flag{
flag: f,
def: def,
help: optStr(help, ""),
})
}