v / vlib / cli
Raw file | 297 loc (254 sloc) | 7.83 KB | Latest commit hash da68b2d36
1module cli
2
3pub enum FlagType {
4 bool
5 int
6 float
7 string
8 // If flag can set multiple time, use array type
9 int_array
10 float_array
11 string_array
12}
13
14// Flag holds information for a command line flag.
15// (flags are also commonly referred to as "options" or "switches")
16// These are typically denoted in the shell by a short form `-f` and/or a long form `--flag`
17pub struct Flag {
18pub mut:
19 flag FlagType
20 // Name of flag
21 name string
22 // Like short option
23 abbrev string
24 // Desciption of flag
25 description string
26 // If the flag is added to this command and to all subcommands
27 global bool
28 // If flag is requierd
29 required bool
30 // Default value if no value provide by command line
31 default_value []string = []
32mut:
33 // Set true if flag found.
34 found bool
35 // Value of flag
36 value []string = []
37}
38
39// get_all_found returns an array of all `Flag`s found in the command parameters
40pub fn (flags []Flag) get_all_found() []Flag {
41 return flags.filter(it.found)
42}
43
44// get_bool returns `true` if the flag is set.
45// get_bool returns an error if the `FlagType` is not boolean.
46pub fn (flag Flag) get_bool() !bool {
47 if flag.flag != .bool {
48 return error('${flag.name}: Invalid flag type `${flag.flag}`, expected `bool`')
49 }
50
51 val := flag.get_value_or_default_value()
52
53 return val.len > 0 && val[0] == 'true'
54}
55
56// get_bool returns `true` if the flag specified in `name` is set.
57// get_bool returns an error if the `FlagType` is not boolean.
58pub fn (flags []Flag) get_bool(name string) !bool {
59 flag := flags.get(name)!
60 return flag.get_bool()
61}
62
63// get_int returns the `int` value argument of the flag.
64// get_int returns an error if the `FlagType` is not integer.
65pub fn (flag Flag) get_int() !int {
66 if flag.flag != .int {
67 return error('${flag.name}: Invalid flag type `${flag.flag}`, expected `int`')
68 }
69
70 val := flag.get_value_or_default_value()
71
72 if val.len == 0 {
73 return 0
74 } else {
75 return val[0].int()
76 }
77}
78
79// get_ints returns the array of `int` value argument of the flag specified in `name`.
80// get_ints returns an error if the `FlagType` is not integer.
81pub fn (flag Flag) get_ints() ![]int {
82 if flag.flag != .int_array {
83 return error('${flag.name}: Invalid flag type `${flag.flag}`, expected `int_array`')
84 }
85
86 val := flag.get_value_or_default_value()
87
88 if val.len == 0 {
89 return []int{}
90 } else {
91 mut values := []int{}
92
93 for f in val {
94 values << f.int()
95 }
96
97 return values
98 }
99}
100
101// get_int returns the `int` value argument of the flag specified in `name`.
102// get_int returns an error if the `FlagType` is not integer.
103pub fn (flags []Flag) get_int(name string) !int {
104 flag := flags.get(name)!
105 return flag.get_int()
106}
107
108// get_ints returns the array of `int` value argument of the flag specified in `name`.
109// get_ints returns an error if the `FlagType` is not integer.
110pub fn (flags []Flag) get_ints(name string) ![]int {
111 flag := flags.get(name)!
112 return flag.get_ints()
113}
114
115// get_float returns the `f64` value argument of the flag.
116// get_float returns an error if the `FlagType` is not floating point.
117pub fn (flag Flag) get_float() !f64 {
118 if flag.flag != .float {
119 return error('${flag.name}: Invalid flag type `${flag.flag}`, expected `float`')
120 }
121
122 val := flag.get_value_or_default_value()
123
124 if val.len == 0 {
125 return 0.0
126 } else {
127 return val[0].f64()
128 }
129}
130
131// get_floats returns the `f64` value argument of the flag.
132// get_floats returns an error if the `FlagType` is not floating point.
133pub fn (flag Flag) get_floats() ![]f64 {
134 if flag.flag != .float_array {
135 return error('${flag.name}: Invalid flag type `${flag.flag}`, expected `float_array`')
136 }
137
138 val := flag.get_value_or_default_value()
139
140 if val.len == 0 {
141 return []f64{}
142 } else {
143 mut values := []f64{}
144
145 for f in val {
146 values << f.f64()
147 }
148
149 return values
150 }
151}
152
153// get_float returns the `f64` value argument of the flag specified in `name`.
154// get_float returns an error if the `FlagType` is not floating point.
155pub fn (flags []Flag) get_float(name string) !f64 {
156 flag := flags.get(name)!
157 return flag.get_float()
158}
159
160// get_floats returns the array of `f64` value argument of the flag specified in `name`.
161// get_floats returns an error if the `FlagType` is not floating point.
162pub fn (flags []Flag) get_floats(name string) ![]f64 {
163 flag := flags.get(name)!
164 return flag.get_floats()
165}
166
167// get_string returns the `string` value argument of the flag.
168// get_string returns an error if the `FlagType` is not string.
169pub fn (flag Flag) get_string() !string {
170 if flag.flag != .string {
171 return error('${flag.name}: Invalid flag type `${flag.flag}`, expected `string`')
172 }
173
174 val := flag.get_value_or_default_value()
175
176 if val.len == 0 {
177 return ''
178 } else {
179 return val[0]
180 }
181}
182
183// get_strings returns the array of `string` value argument of the flag.
184// get_strings returns an error if the `FlagType` is not string.
185pub fn (flag Flag) get_strings() ![]string {
186 if flag.flag != .string_array {
187 return error('${flag.name}: Invalid flag type `${flag.flag}`, expected `string_array`')
188 }
189
190 val := flag.get_value_or_default_value()
191
192 if val.len == 0 {
193 return []string{}
194 } else {
195 return val
196 }
197}
198
199// get_string returns the `string` value argument of the flag specified in `name`.
200// get_string returns an error if the `FlagType` is not string.
201pub fn (flags []Flag) get_string(name string) !string {
202 flag := flags.get(name)!
203 return flag.get_string()
204}
205
206// get_strings returns the `string` value argument of the flag specified in `name`.
207// get_strings returns an error if the `FlagType` is not string.
208pub fn (flags []Flag) get_strings(name string) ![]string {
209 flag := flags.get(name)!
210 return flag.get_strings()
211}
212
213// parse parses flag values from arguments and return
214// an array of arguments with all consumed elements removed.
215pub fn (mut flag Flag) parse(args []string, posix_mode bool) ![]string {
216 if flag.matches(args, posix_mode) {
217 if flag.flag == .bool {
218 new_args := flag.parse_bool(args)!
219 return new_args
220 } else {
221 if flag.value.len > 0 && flag.flag != .int_array && flag.flag != .float_array
222 && flag.flag != .string_array {
223 return error('The argument `${flag.name}` accept only one value!')
224 }
225
226 new_args := flag.parse_raw(args)!
227 return new_args
228 }
229 } else {
230 return args
231 }
232}
233
234// matches returns `true` if first arg in `args` matches this flag.
235fn (mut flag Flag) matches(args []string, posix_mode bool) bool {
236 prefix := if posix_mode { '--' } else { '-' }
237 return (flag.name != '' && args[0] == '${prefix}${flag.name}')
238 || (flag.name != '' && args[0].starts_with('${prefix}${flag.name}='))
239 || (flag.abbrev != '' && args[0] == '-${flag.abbrev}')
240 || (flag.abbrev != '' && args[0].starts_with('-${flag.abbrev}='))
241}
242
243fn (mut flag Flag) parse_raw(args []string) ![]string {
244 if args[0].len > flag.name.len && args[0].contains('=') {
245 flag.value << args[0].split('=')[1]
246 return args[1..]
247 } else if args.len >= 2 {
248 flag.value << args[1]
249 return args[2..]
250 }
251 return error('Missing argument for `${flag.name}`')
252}
253
254fn (mut flag Flag) parse_bool(args []string) ![]string {
255 if args[0].len > flag.name.len && args[0].contains('=') {
256 flag.value = [args[0].split('=')[1]]
257 return args[1..]
258 } else if args.len >= 2 {
259 if args[1] in ['true', 'false'] {
260 flag.value = [args[1]]
261 return args[2..]
262 }
263 }
264 // In fact bool cannot be multiple
265 flag.value = ['true']
266 return args[1..]
267}
268
269// get returns the `Flag` matching `name` or an error
270// if it can't be found.
271fn (flags []Flag) get(name string) !Flag {
272 for flag in flags {
273 if flag.name == name {
274 return flag
275 }
276 }
277 return error('Flag `${name}` not found in ${flags}')
278}
279
280fn (flags []Flag) contains(name string) bool {
281 for flag in flags {
282 if flag.name == name || flag.abbrev == name {
283 return true
284 }
285 }
286 return false
287}
288
289// Check if value is set by command line option. If not, return default value.
290fn (flag Flag) get_value_or_default_value() []string {
291 if flag.value.len == 0 && flag.default_value.len > 0 {
292 // If default value is set and no value provide, use default value.
293 return flag.default_value
294 } else {
295 return flag.value
296 }
297}