forked from akamensky/argparse
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathargument.go
208 lines (196 loc) · 5.34 KB
/
argument.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
204
205
206
207
208
package argparse
import (
"fmt"
"os"
"strings"
)
type arg struct {
result interface{} // Pointer to the resulting value
opts *Options // Options
sname string // Short name (in Parser will start with "-"
lname string // Long name (in Parser will start with "--"
size int // Size defines how many args after match will need to be consumed
unique bool // Specifies whether flag should be present only ones
parsed bool // Specifies whether flag has been parsed already
fileFlag int // File mode to open file with
filePerm os.FileMode // File permissions to set a file
selector *[]string // Used in Selector type to allow to choose only one from list of options
parent *Command // Used to get access to specific Command
}
type help struct{}
func (o *arg) check(argument string) bool {
// Shortcut to showing help
if argument == "-h" || argument == "--help" {
helpText := o.parent.Usage(nil)
fmt.Print(helpText)
os.Exit(0)
}
// Check for long name only if not empty
if o.lname != "" {
// If argument begins with "--" and next is not "-" then it is a long name
if len(argument) > 2 && strings.HasPrefix(argument, "--") && argument[2] != '-' {
if argument[2:] == o.lname {
return true
}
}
}
// Check for short name only if not empty
if o.sname != "" {
// If argument begins with "-" and next is not "-" then it is a short name
if len(argument) > 1 && strings.HasPrefix(argument, "-") && argument[1] != '-' {
switch o.result.(type) {
case *bool:
// For flags we allow multiple shorthand in one
if strings.Contains(argument[1:], o.sname) {
return true
}
default:
// For all other types it must be separate argument
if argument[1:] == o.sname {
return true
}
}
}
}
return false
}
func (o *arg) reduce(position int, args *[]string) {
argument := (*args)[position]
// Check for long name only if not empty
if o.lname != "" {
// If argument begins with "--" and next is not "-" then it is a long name
if len(argument) > 2 && strings.HasPrefix(argument, "--") && argument[2] != '-' {
if argument[2:] == o.lname {
for i := position; i < position+o.size; i++ {
(*args)[i] = ""
}
}
}
}
// Check for short name only if not empty
if o.sname != "" {
// If argument begins with "-" and next is not "-" then it is a short name
if len(argument) > 1 && strings.HasPrefix(argument, "-") && argument[1] != '-' {
switch o.result.(type) {
case *bool:
// For flags we allow multiple shorthand in one
if strings.Contains(argument[1:], o.sname) {
(*args)[position] = strings.Replace(argument, o.sname, "", -1)
if (*args)[position] == "-" {
(*args)[position] = ""
}
}
default:
// For all other types it must be separate argument
if argument[1:] == o.sname {
for i := position; i < position+o.size; i++ {
(*args)[i] = ""
}
}
}
}
}
}
func (o *arg) parse(args []string) error {
// If unique do not allow more than one time
if o.unique && o.parsed {
return fmt.Errorf("[%s] can only be present once", o.name())
}
// If validation function provided -- execute, on error return it immediately
if o.opts != nil && o.opts.Validate != nil {
err := o.opts.Validate(args)
if err != nil {
return err
}
}
switch o.result.(type) {
case *help:
helpText := o.parent.Usage(nil)
fmt.Print(helpText)
os.Exit(0)
case *bool:
*o.result.(*bool) = true
o.parsed = true
case *string:
if len(args) < 1 {
return fmt.Errorf("[%s] must be followed by a string", o.name())
}
if len(args) > 1 {
return fmt.Errorf("[%s] followed by too many arguments", o.name())
}
// Selector case
if o.selector != nil {
match := false
for _, v := range *o.selector {
if args[0] == v {
match = true
}
}
if !match {
return fmt.Errorf("bad value for [%s]. Allowed values are %v", o.name(), *o.selector)
}
}
*o.result.(*string) = args[0]
o.parsed = true
case *os.File:
if len(args) < 1 {
return fmt.Errorf("[%s] must be followed by a path to file", o.name())
}
if len(args) > 1 {
return fmt.Errorf("[%s] followed by too many arguments", o.name())
}
f, err := os.OpenFile(args[0], o.fileFlag, o.filePerm)
if err != nil {
return err
}
*o.result.(*os.File) = *f
o.parsed = true
case *[]string:
if len(args) < 1 {
return fmt.Errorf("[%s] must be followed by a string", o.name())
}
if len(args) > 1 {
return fmt.Errorf("[%s] followed by too many arguments", o.name())
}
*o.result.(*[]string) = append(*o.result.(*[]string), args[0])
o.parsed = true
default:
return fmt.Errorf("unsupported type [%t]", o.result)
}
return nil
}
func (o *arg) name() string {
var name string
if o.lname == "" {
name = "-" + o.sname
} else if o.sname == "" {
name = "--" + o.lname
} else {
name = "-" + o.sname + "|" + "--" + o.lname
}
return name
}
func (o *arg) usage() string {
var result string
result = o.name()
switch o.result.(type) {
case *bool:
break
case *string:
if o.selector != nil {
result = result + " (" + strings.Join(*o.selector, "|") + ")"
} else {
result = result + " \"<value>\""
}
case *os.File:
result = result + " <file>"
case *[]string:
result = result + " \"<string>\""
default:
break
}
if o.opts == nil || o.opts.Required == false {
result = "[" + result + "]"
}
return result
}