| 1 | module flag |
| 2 | |
| 3 | // data object storing information about a defined flag |
| 4 | pub struct Flag { |
| 5 | pub: |
| 6 | name string // name as it appears on command line |
| 7 | abbr u8 // shortcut |
| 8 | usage string // help message |
| 9 | val_desc string // something like '<arg>' that appears in usage, |
| 10 | // and also the default value, when the flag is not given |
| 11 | default_value string |
| 12 | has_default bool |
| 13 | } |
| 14 | |
| 15 | struct UnknownFlagError { |
| 16 | Error |
| 17 | flag string |
| 18 | } |
| 19 | |
| 20 | fn (err UnknownFlagError) msg() string { |
| 21 | return 'Unknown flag `${err.flag}`' |
| 22 | } |
| 23 | |
| 24 | struct ArgsCountError { |
| 25 | Error |
| 26 | got int |
| 27 | want int |
| 28 | } |
| 29 | |
| 30 | fn (err ArgsCountError) msg() string { |
| 31 | if err.want == 0 { |
| 32 | return 'Expected no arguments, but got ${err.got}' |
| 33 | } else if err.got > err.want { |
| 34 | return 'Expected at most ${err.want} arguments, but got ${err.got}' |
| 35 | } else { |
| 36 | return 'Expected at least ${err.want} arguments, but got ${err.got}' |
| 37 | } |
| 38 | } |
| 39 | |
| 40 | // free frees the resources associated with a given Flag |
| 41 | // It is called automatically when -autofree is used. |
| 42 | // It should be called manually in functions that use Flags, |
| 43 | // and are marked with [manualfree]. After you call .free() on |
| 44 | // a Flag instance, you should NOT use that instance any more. |
| 45 | @[unsafe] |
| 46 | fn (mut f Flag) free() { |
| 47 | unsafe { |
| 48 | f.name.free() |
| 49 | f.usage.free() |
| 50 | f.val_desc.free() |
| 51 | f.default_value.free() |
| 52 | } |
| 53 | } |
| 54 | |
| 55 | // str returns a string representation of the given Flag. |
| 56 | pub fn (f Flag) str() string { |
| 57 | return ' flag: |
| 58 | name: ${f.name} |
| 59 | abbr: `${f.abbr.ascii_str()}` |
| 60 | usage: ${f.usage} |
| 61 | desc: ${f.val_desc} |
| 62 | default_value: ${f.default_value} |
| 63 | has_default: ${f.has_default}' |
| 64 | } |
| 65 | |
| 66 | // str returns a string representation of the given array of Flags. |
| 67 | pub fn (af []Flag) str() string { |
| 68 | mut res := []string{} |
| 69 | res << '\n []Flag = [' |
| 70 | for f in af { |
| 71 | res << f.str() |
| 72 | } |
| 73 | res << ' ]' |
| 74 | return res.join('\n') |
| 75 | } |
| 76 | |
| 77 | // FlagParser is the heart of the `flag` module. |
| 78 | // That structure is created with `mut parser := flag.new_flag_parser(os.args)`, |
| 79 | // The returned instance can be further customised by calling various methods, |
| 80 | // for specifying the accepted options and their values. The user should finally |
| 81 | // call `rest := parser.finalize()!` to get the rest of the non optional arguments |
| 82 | // (if there are any left). |
| 83 | pub struct FlagParser { |
| 84 | pub: |
| 85 | original_args []string // the original arguments to be parsed |
| 86 | idx_dashdash int // the index of a `--`, -1 if there is not any |
| 87 | all_after_dashdash []string // all options after `--` are ignored, and will be passed to the application unmodified |
| 88 | pub mut: |
| 89 | usage_examples []string // when set, --help will print: |
| 90 | // Usage: ${appname} ${usage_examples[0]}` |
| 91 | // or: ${appname} ${usage_examples[1]}` |
| 92 | // etc |
| 93 | default_help_label string = 'display this help and exit' |
| 94 | default_version_label string = 'output version information and exit' |
| 95 | args []string // the current list of processed args |
| 96 | max_free_args int |
| 97 | flags []Flag // registered flags |
| 98 | application_name string |
| 99 | application_version string |
| 100 | application_description string |
| 101 | min_free_args int |
| 102 | args_description string |
| 103 | allow_unknown_args bool // whether passing undescribed arguments is allowed |
| 104 | footers []string // when set, --help will display all the collected footers at the bottom. |
| 105 | options DocOptions // documentation options |
| 106 | } |
| 107 | |
| 108 | // free frees the resources allocated for the given FlagParser instance. |
| 109 | // It should be called manually in functions that use it, and that are |
| 110 | // marked with `@[manualfree]`, otherwise, it is called automatically |
| 111 | // in programs, compiled with `-autofree`. Note: you should NOT use the |
| 112 | // instance over which you have called .free() for anything after the call. |
| 113 | @[unsafe] |
| 114 | fn (mut f FlagParser) free() { |
| 115 | unsafe { |
| 116 | // |
| 117 | for flag in f.flags { |
| 118 | flag.free() |
| 119 | } |
| 120 | f.flags.free() |
| 121 | // |
| 122 | f.application_name.free() |
| 123 | f.application_version.free() |
| 124 | f.application_description.free() |
| 125 | f.args_description.free() |
| 126 | } |
| 127 | } |
| 128 | |
| 129 | // used for formatting usage message |
| 130 | pub const space = ' ' |
| 131 | pub const underline = '-----------------------------------------------' |
| 132 | pub const max_args_number = 4048 |
| 133 | |
| 134 | // new_flag_parser - create a new flag parser for the given args. |
| 135 | @[manualfree] |
| 136 | pub fn new_flag_parser(args []string) &FlagParser { |
| 137 | original_args := args.clone() |
| 138 | idx_dashdash := args.index('--') |
| 139 | mut all_before_dashdash := args.clone() |
| 140 | mut all_after_dashdash := []string{} |
| 141 | if idx_dashdash >= 0 { |
| 142 | all_before_dashdash.trim(idx_dashdash) |
| 143 | if idx_dashdash < original_args.len { |
| 144 | all_after_dashdash = unsafe { original_args[idx_dashdash + 1..] } |
| 145 | } |
| 146 | } |
| 147 | return &FlagParser{ |
| 148 | original_args: original_args |
| 149 | idx_dashdash: idx_dashdash |
| 150 | all_after_dashdash: all_after_dashdash |
| 151 | args: all_before_dashdash |
| 152 | max_free_args: max_args_number |
| 153 | options: DocOptions{ |
| 154 | show: ~Show.zero() ^ .name |
| 155 | } |
| 156 | } |
| 157 | } |
| 158 | |
| 159 | // usage_example - add an usage example. |
| 160 | // All examples will be listed in the help screen. |
| 161 | // If you do not give any examples, then a default usage |
| 162 | // will be shown, based on whether the application takes |
| 163 | // options and expects additional parameters. |
| 164 | pub fn (mut fs FlagParser) usage_example(example string) { |
| 165 | fs.usage_examples << example |
| 166 | } |
| 167 | |
| 168 | // footer - adds a footnote, that will be shown at the bottom of the help screen. |
| 169 | pub fn (mut fs FlagParser) footer(footer string) { |
| 170 | fs.footers << footer |
| 171 | } |
| 172 | |
| 173 | // application changes the application name to be used in 'usage' output. |
| 174 | pub fn (mut fs FlagParser) application(name string) { |
| 175 | fs.application_name = name |
| 176 | } |
| 177 | |
| 178 | // version changes the application version to be used in 'usage' output. |
| 179 | pub fn (mut fs FlagParser) version(vers string) { |
| 180 | fs.application_version = vers |
| 181 | } |
| 182 | |
| 183 | // description appends to the application description lines, shown in the help/usage screen. |
| 184 | pub fn (mut fs FlagParser) description(desc string) { |
| 185 | if fs.application_description.len == 0 { |
| 186 | fs.application_description = desc |
| 187 | } else { |
| 188 | fs.application_description += '\n${desc}' |
| 189 | } |
| 190 | } |
| 191 | |
| 192 | // skip_executable - removes the first argv (it usually contains the executable name). |
| 193 | // In most cases you do not need it for flag parsing. |
| 194 | // This method, allows you to use `mut fp := flag.new_flag_parser(arguments())` directly. |
| 195 | pub fn (mut fs FlagParser) skip_executable() { |
| 196 | fs.args.delete(0) |
| 197 | } |
| 198 | |
| 199 | // allow_unknown_args - call this method, if your program has sub commands, that have their own arguments. |
| 200 | // After calling it, the subcommand arguments (which generally are not known to your parent program), |
| 201 | // will not cause the validation in .finalize() to fail. |
| 202 | pub fn (mut fs FlagParser) allow_unknown_args() { |
| 203 | fs.allow_unknown_args = true |
| 204 | } |
| 205 | |
| 206 | // private helper to register a flag. |
| 207 | // This version supports abbreviations. |
| 208 | fn (mut fs FlagParser) add_flag(name string, abbr u8, usage string, desc string, default_value ?string) { |
| 209 | mut has_default := false |
| 210 | mut resolved_default_value := '' |
| 211 | if value := default_value { |
| 212 | has_default = true |
| 213 | resolved_default_value = value |
| 214 | } |
| 215 | fs.flags << Flag{ |
| 216 | name: name |
| 217 | abbr: abbr |
| 218 | usage: usage |
| 219 | val_desc: desc |
| 220 | default_value: resolved_default_value |
| 221 | has_default: has_default |
| 222 | } |
| 223 | } |
| 224 | |
| 225 | fn flag_value_description(c FlagConfig, default_description string) string { |
| 226 | return if c.val_desc == '' { |
| 227 | default_description |
| 228 | } else { |
| 229 | c.val_desc |
| 230 | } |
| 231 | } |
| 232 | |
| 233 | fn escape_default_string(value string) string { |
| 234 | return value.replace('\\', '\\\\').replace('"', '\\"').replace('\n', '\\n').replace('\r', '\\r').replace('\t', |
| 235 | '\\t') |
| 236 | } |
| 237 | |
| 238 | fn flag_default_value[T](value T) string { |
| 239 | $if T is string { |
| 240 | s := '${value}' |
| 241 | return '"${escape_default_string(s)}"' |
| 242 | } $else { |
| 243 | return '${value}' |
| 244 | } |
| 245 | } |
| 246 | |
| 247 | fn (mut fs FlagParser) bool_flag(name string, abbr u8, usage string, c FlagConfig, default_value ?string) !bool { |
| 248 | val_desc := flag_value_description(c, '<bool>') |
| 249 | fs.add_flag(name, abbr, usage, val_desc, default_value) |
| 250 | parsed := fs.parse_bool_value(name, abbr) or { |
| 251 | return error("parameter '${name}' not provided") |
| 252 | } |
| 253 | return parsed == 'true' |
| 254 | } |
| 255 | |
| 256 | fn (mut fs FlagParser) int_flag(name string, abbr u8, usage string, c FlagConfig, default_value ?string) !int { |
| 257 | val_desc := flag_value_description(c, '<int>') |
| 258 | fs.add_flag(name, abbr, usage, val_desc, default_value) |
| 259 | parsed := fs.parse_value(name, abbr) |
| 260 | if parsed.len == 0 { |
| 261 | return error("parameter '${name}' not provided") |
| 262 | } |
| 263 | return parsed[0].int() |
| 264 | } |
| 265 | |
| 266 | fn (mut fs FlagParser) float_flag(name string, abbr u8, usage string, c FlagConfig, default_value ?string) !f64 { |
| 267 | val_desc := flag_value_description(c, '<float>') |
| 268 | fs.add_flag(name, abbr, usage, val_desc, default_value) |
| 269 | parsed := fs.parse_value(name, abbr) |
| 270 | if parsed.len == 0 { |
| 271 | return error("parameter '${name}' not provided") |
| 272 | } |
| 273 | return parsed[0].f64() |
| 274 | } |
| 275 | |
| 276 | fn (mut fs FlagParser) string_flag(name string, abbr u8, usage string, c FlagConfig, default_value ?string) !string { |
| 277 | val_desc := flag_value_description(c, '<string>') |
| 278 | fs.add_flag(name, abbr, usage, val_desc, default_value) |
| 279 | parsed := fs.parse_value(name, abbr) |
| 280 | if parsed.len == 0 { |
| 281 | return error("parameter '${name}' not provided") |
| 282 | } |
| 283 | return parsed[0] |
| 284 | } |
| 285 | |
| 286 | // private: general parsing a single argument |
| 287 | // - search args for existence |
| 288 | // if true |
| 289 | // extract the defined value as string |
| 290 | // else |
| 291 | // return an (dummy) error -> argument is not defined |
| 292 | // |
| 293 | // - the name, usage are registered |
| 294 | // - found arguments and corresponding values are removed from args list |
| 295 | @[manualfree] |
| 296 | fn (mut fs FlagParser) parse_value(longhand string, shorthand u8) []string { |
| 297 | full := '--${longhand}' |
| 298 | defer { |
| 299 | unsafe { full.free() } |
| 300 | } |
| 301 | mut found_entries := []string{} |
| 302 | mut to_delete := []int{} |
| 303 | defer { |
| 304 | unsafe { to_delete.free() } |
| 305 | } |
| 306 | mut should_skip_one := false |
| 307 | for i, arg in fs.args { |
| 308 | if should_skip_one { |
| 309 | should_skip_one = false |
| 310 | continue |
| 311 | } |
| 312 | if arg.len == 0 || arg[0] != `-` { |
| 313 | continue |
| 314 | } |
| 315 | if (arg.len == 2 && arg[0] == `-` && arg[1] == shorthand) || arg == full { |
| 316 | if i + 1 >= fs.args.len { |
| 317 | return [] |
| 318 | } |
| 319 | nextarg := fs.args[i + 1] |
| 320 | if nextarg.len > 2 { |
| 321 | nextarg_rest := nextarg[..2] |
| 322 | if nextarg_rest == '--' { |
| 323 | // It could be end of input (--) or another argument (--abc). |
| 324 | // Both are invalid so die. |
| 325 | unsafe { nextarg_rest.free() } |
| 326 | return [] |
| 327 | } |
| 328 | unsafe { nextarg_rest.free() } |
| 329 | } |
| 330 | found_entries << fs.args[i + 1] |
| 331 | to_delete << i |
| 332 | to_delete << i + 1 |
| 333 | should_skip_one = true |
| 334 | continue |
| 335 | } |
| 336 | if arg.len > full.len + 1 && arg[..full.len + 1] == '${full}=' { |
| 337 | found_entries << arg[full.len + 1..] |
| 338 | to_delete << i |
| 339 | continue |
| 340 | } |
| 341 | } |
| 342 | for i, del in to_delete { |
| 343 | // i entries are deleted so it's shifted left i times. |
| 344 | fs.args.delete(del - i) |
| 345 | } |
| 346 | return found_entries |
| 347 | } |
| 348 | |
| 349 | // special parsing for bool values |
| 350 | // see also: parse_value |
| 351 | // |
| 352 | // special: it is allowed to define bool flags without value |
| 353 | // -> '--flag' is parsed as true |
| 354 | // -> '--flag' is equal to '--flag=true' |
| 355 | fn (mut fs FlagParser) parse_bool_value(longhand string, shorthand u8) !string { |
| 356 | full := '--${longhand}' |
| 357 | for i, arg in fs.args { |
| 358 | if arg.len == 0 { |
| 359 | continue |
| 360 | } |
| 361 | if arg[0] != `-` { |
| 362 | continue |
| 363 | } |
| 364 | if (arg.len == 2 && arg[0] == `-` && arg[1] == shorthand) || arg == full { |
| 365 | if fs.args.len > i + 1 && fs.args[i + 1] in ['true', 'false'] { |
| 366 | val := fs.args[i + 1] |
| 367 | fs.args.delete(i + 1) |
| 368 | fs.args.delete(i) |
| 369 | return val |
| 370 | } else { |
| 371 | fs.args.delete(i) |
| 372 | return 'true' |
| 373 | } |
| 374 | } |
| 375 | if arg.len > full.len + 1 && arg[..full.len + 1] == '${full}=' { |
| 376 | // Flag abc=true |
| 377 | val := arg[full.len + 1..] |
| 378 | fs.args.delete(i) |
| 379 | return val |
| 380 | } |
| 381 | if arg.len > 1 && arg[0] == `-` && arg[1] != `-` { |
| 382 | mut found := false |
| 383 | for j in 1 .. arg.len { |
| 384 | if arg[j].is_space() { |
| 385 | break |
| 386 | } else if arg[j] == shorthand { |
| 387 | found = true |
| 388 | } |
| 389 | } |
| 390 | if found { |
| 391 | // -abc is equivalent to -a -b -c |
| 392 | fs.args[i] = arg.replace(shorthand.ascii_str(), '') // -abc -> -bc |
| 393 | return 'true' |
| 394 | } |
| 395 | } |
| 396 | } |
| 397 | return error("parameter '${longhand}' not found") |
| 398 | } |
| 399 | |
| 400 | @[params] |
| 401 | pub struct FlagConfig { |
| 402 | pub: |
| 403 | val_desc string // descriptive string for an argument |
| 404 | } |
| 405 | |
| 406 | // bool_opt returns an option with the bool value of the given command line flag, named `name`. |
| 407 | // It returns an error, when the flag is not given by the user. |
| 408 | // This version supports abbreviations. |
| 409 | // This version supports a custom value description. |
| 410 | pub fn (mut fs FlagParser) bool_opt(name string, abbr u8, usage string, c FlagConfig) !bool { |
| 411 | return fs.bool_flag(name, abbr, usage, c, none) |
| 412 | } |
| 413 | |
| 414 | // bool defines and parses a bool flag/option named `name`. |
| 415 | // If that flag is given by the user, then it returns its parsed bool value. |
| 416 | // When it is not, it returns the default value in `bdefault`. |
| 417 | // This version supports abbreviations. |
| 418 | // This version supports a custom value description. |
| 419 | pub fn (mut fs FlagParser) bool(name string, abbr u8, bdefault bool, usage string, c FlagConfig) bool { |
| 420 | value := fs.bool_flag(name, abbr, usage, c, flag_default_value(bdefault)) or { return bdefault } |
| 421 | return value |
| 422 | } |
| 423 | |
| 424 | // bool_val is a generic version of `bool` that supports optional defaults. |
| 425 | // Use `?bool(none)` as default to receive an optional result. |
| 426 | pub fn (mut fs FlagParser) bool_val[T](name string, abbr u8, bdefault T, usage string, c FlagConfig) T { |
| 427 | value := fs.bool_flag(name, abbr, usage, c, flag_default_value(bdefault)) or { return bdefault } |
| 428 | return value |
| 429 | } |
| 430 | |
| 431 | // int_multi returns all values associated with the provided flag in `name`. |
| 432 | // When that flag has no values, it returns an empty array. |
| 433 | // This version supports abbreviations. |
| 434 | // This version supports a custom value description. |
| 435 | pub fn (mut fs FlagParser) int_multi(name string, abbr u8, usage string, c FlagConfig) []int { |
| 436 | val_desc := flag_value_description(c, '<multiple ints>') |
| 437 | |
| 438 | fs.add_flag(name, abbr, usage, val_desc, none) |
| 439 | parsed := fs.parse_value(name, abbr) |
| 440 | mut value := []int{} |
| 441 | for val in parsed { |
| 442 | value << val.int() |
| 443 | } |
| 444 | return value |
| 445 | } |
| 446 | |
| 447 | // int_opt returns an option with the integer value, associated with the flag in `name`. |
| 448 | // When the flag is not given by the user, it returns an error. |
| 449 | // This version supports abbreviations. |
| 450 | // This version supports a custom value description. |
| 451 | pub fn (mut fs FlagParser) int_opt(name string, abbr u8, usage string, c FlagConfig) !int { |
| 452 | return fs.int_flag(name, abbr, usage, c, none) |
| 453 | } |
| 454 | |
| 455 | // int defines and parses an integer flag, named `name`. |
| 456 | // When the flag is given by the user, it returns its parsed integer value. |
| 457 | // When it is not, it returns the integer value in `idefault`. |
| 458 | // This version supports abbreviations. |
| 459 | // This version supports a custom value description. |
| 460 | pub fn (mut fs FlagParser) int(name string, abbr u8, idefault int, usage string, c FlagConfig) int { |
| 461 | value := fs.int_flag(name, abbr, usage, c, flag_default_value(idefault)) or { return idefault } |
| 462 | return value |
| 463 | } |
| 464 | |
| 465 | // int_val is a generic version of `int` that supports optional defaults. |
| 466 | // Use `?int(none)` as default to receive an optional result. |
| 467 | pub fn (mut fs FlagParser) int_val[T](name string, abbr u8, idefault T, usage string, c FlagConfig) T { |
| 468 | value := fs.int_flag(name, abbr, usage, c, flag_default_value(idefault)) or { return idefault } |
| 469 | return value |
| 470 | } |
| 471 | |
| 472 | // float_multi returns all floating point values, associated with the flag named `name`. |
| 473 | // When no values for that flag are found, it returns an empty array. |
| 474 | // This version supports abbreviations. |
| 475 | // This version supports a custom value description. |
| 476 | pub fn (mut fs FlagParser) float_multi(name string, abbr u8, usage string, c FlagConfig) []f64 { |
| 477 | val_desc := flag_value_description(c, '<multiple floats>') |
| 478 | |
| 479 | fs.add_flag(name, abbr, usage, val_desc, none) |
| 480 | parsed := fs.parse_value(name, abbr) |
| 481 | mut value := []f64{} |
| 482 | for val in parsed { |
| 483 | value << val.f64() |
| 484 | } |
| 485 | return value |
| 486 | } |
| 487 | |
| 488 | // float_opt returns an option with the floating point value, associated with the flag in `name`. |
| 489 | // When the flag is not given by the user, it returns an error. |
| 490 | // This version supports abbreviations. |
| 491 | // This version supports a custom value description. |
| 492 | pub fn (mut fs FlagParser) float_opt(name string, abbr u8, usage string, c FlagConfig) !f64 { |
| 493 | return fs.float_flag(name, abbr, usage, c, none) |
| 494 | } |
| 495 | |
| 496 | // float defines and parses a floating point flag, named `name`. |
| 497 | // When the flag is given by the user, it returns its parsed floating point value. |
| 498 | // When it is not, it returns the value in `fdefault`. |
| 499 | // This version supports abbreviations. |
| 500 | // This version supports a custom value description. |
| 501 | pub fn (mut fs FlagParser) float(name string, abbr u8, fdefault f64, usage string, c FlagConfig) f64 { |
| 502 | value := fs.float_flag(name, abbr, usage, c, flag_default_value(fdefault)) or { |
| 503 | return fdefault |
| 504 | } |
| 505 | return value |
| 506 | } |
| 507 | |
| 508 | // float_val is a generic version of `float` that supports optional defaults. |
| 509 | // Use `?f64(none)` as default to receive an optional result. |
| 510 | pub fn (mut fs FlagParser) float_val[T](name string, abbr u8, fdefault T, usage string, c FlagConfig) T { |
| 511 | value := fs.float_flag(name, abbr, usage, c, flag_default_value(fdefault)) or { |
| 512 | return fdefault |
| 513 | } |
| 514 | return value |
| 515 | } |
| 516 | |
| 517 | // string_multi returns all string values, associated with the flag named `name`. |
| 518 | // When no values for that flag are found, it returns an empty array. |
| 519 | // This version supports abbreviations. |
| 520 | // This version supports a custom value description. |
| 521 | pub fn (mut fs FlagParser) string_multi(name string, abbr u8, usage string, c FlagConfig) []string { |
| 522 | val_desc := flag_value_description(c, '<multiple strings>') |
| 523 | |
| 524 | fs.add_flag(name, abbr, usage, val_desc, none) |
| 525 | return fs.parse_value(name, abbr) |
| 526 | } |
| 527 | |
| 528 | // string_opt returns an option with the string value, associated with the flag in `name`. |
| 529 | // When the flag is not given by the user, it returns an error. |
| 530 | // This version supports abbreviations. |
| 531 | // This version supports a custom value description. |
| 532 | pub fn (mut fs FlagParser) string_opt(name string, abbr u8, usage string, c FlagConfig) !string { |
| 533 | return fs.string_flag(name, abbr, usage, c, none) |
| 534 | } |
| 535 | |
| 536 | // string defines and parses a string flag/option, named `name`. |
| 537 | // If that flag is given as an option, then its parsed value is returned as a string. |
| 538 | // When it is not, it returns the default string value in `sdefault`. |
| 539 | // This version supports abbreviations. |
| 540 | // This version supports a custom value description. |
| 541 | pub fn (mut fs FlagParser) string(name string, abbr u8, sdefault string, usage string, c FlagConfig) string { |
| 542 | value := fs.string_flag(name, abbr, usage, c, flag_default_value(sdefault)) or { |
| 543 | return sdefault |
| 544 | } |
| 545 | return value |
| 546 | } |
| 547 | |
| 548 | // string_val is a generic version of `string` that supports optional defaults. |
| 549 | // Use `?string(none)` as default to receive an optional result. |
| 550 | pub fn (mut fs FlagParser) string_val[T](name string, abbr u8, sdefault T, usage string, c FlagConfig) T { |
| 551 | value := fs.string_flag(name, abbr, usage, c, flag_default_value(sdefault)) or { |
| 552 | return sdefault |
| 553 | } |
| 554 | return value |
| 555 | } |
| 556 | |
| 557 | // limit_free_args_to_at_least restricts the list of free arguments (non options) to be at least `n` in length. |
| 558 | // If the user gives less free arguments to the program, the parser will return an error. |
| 559 | pub fn (mut fs FlagParser) limit_free_args_to_at_least(n int) ! { |
| 560 | if n > max_args_number { |
| 561 | return error('flag.limit_free_args_to_at_least expect n to be smaller than ${max_args_number}') |
| 562 | } |
| 563 | if n <= 0 { |
| 564 | return error('flag.limit_free_args_to_at_least expect n to be a positive number') |
| 565 | } |
| 566 | fs.min_free_args = n |
| 567 | } |
| 568 | |
| 569 | // limit_free_args_to_exactly restricts the list of free arguments (non options) to be at exactly `n` in length. |
| 570 | // If the user gives more or less free arguments to the program, the parser will return an error. |
| 571 | pub fn (mut fs FlagParser) limit_free_args_to_exactly(n int) ! { |
| 572 | if n > max_args_number { |
| 573 | return error('flag.limit_free_args_to_exactly expect n to be smaller than ${max_args_number}') |
| 574 | } |
| 575 | if n < 0 { |
| 576 | return error('flag.limit_free_args_to_exactly expect n to be a non negative number') |
| 577 | } |
| 578 | fs.min_free_args = n |
| 579 | fs.max_free_args = n |
| 580 | } |
| 581 | |
| 582 | // limit_free_args restricts the list of free arguments (non options) to be between `min` and `max` in length. |
| 583 | // If the user gives more or less free arguments to the program, the parser will return an error. |
| 584 | pub fn (mut fs FlagParser) limit_free_args(min int, max int) ! { |
| 585 | if min > max { |
| 586 | return error('flag.limit_free_args expect min < max, got ${min} >= ${max}') |
| 587 | } |
| 588 | fs.min_free_args = min |
| 589 | fs.max_free_args = max |
| 590 | } |
| 591 | |
| 592 | // arguments_description sets the description field of the parser. |
| 593 | // This field is usually shown when the `--help` option is given to the program. |
| 594 | pub fn (mut fs FlagParser) arguments_description(description string) { |
| 595 | fs.args_description = description |
| 596 | } |
| 597 | |
| 598 | // usage returns a nicely formatted usage screen, containing all the possible options, as well as the description for the program. |
| 599 | // That screen is usually shown when the `--help` option is given to the program. |
| 600 | pub fn (fs &FlagParser) usage() string { |
| 601 | positive_min_arg := (fs.min_free_args > 0) |
| 602 | positive_max_arg := (fs.max_free_args > 0 && fs.max_free_args != max_args_number) |
| 603 | no_arguments := (fs.min_free_args == 0 && fs.max_free_args == 0) |
| 604 | mut adesc := if fs.args_description.len > 0 { fs.args_description } else { '[ARGS]' } |
| 605 | if no_arguments { |
| 606 | adesc = '' |
| 607 | } |
| 608 | mut use := []string{} |
| 609 | if doc_add_name_and_version(fs.application_name, fs.application_version, fs.options, mut use) { |
| 610 | use << '${underline}' |
| 611 | } |
| 612 | if fs.usage_examples.len == 0 { |
| 613 | use << 'Usage: ${fs.application_name} [options] ${adesc}' |
| 614 | } else { |
| 615 | for i, example in fs.usage_examples { |
| 616 | if i == 0 { |
| 617 | use << 'Usage: ${fs.application_name} ${example}' |
| 618 | } else { |
| 619 | use << ' or: ${fs.application_name} ${example}' |
| 620 | } |
| 621 | } |
| 622 | } |
| 623 | use << '' |
| 624 | if fs.options.show.has(.description) && fs.application_description != '' { |
| 625 | use << 'Description: ${fs.application_description}' |
| 626 | use << '' |
| 627 | } |
| 628 | // show a message about the [ARGS]: |
| 629 | if positive_min_arg || positive_max_arg || no_arguments { |
| 630 | if no_arguments { |
| 631 | use << 'This application does not expect any arguments' |
| 632 | use << '' |
| 633 | } else { |
| 634 | mut s := []string{} |
| 635 | if positive_min_arg { |
| 636 | s << 'at least ${fs.min_free_args}' |
| 637 | } |
| 638 | if positive_max_arg { |
| 639 | s << 'at most ${fs.max_free_args}' |
| 640 | } |
| 641 | if positive_min_arg && positive_max_arg && fs.min_free_args == fs.max_free_args { |
| 642 | s = ['exactly ${fs.min_free_args}'] |
| 643 | } |
| 644 | sargs := s.join(' and ') |
| 645 | use << 'The arguments should be ${sargs} in number.' |
| 646 | use << '' |
| 647 | } |
| 648 | } |
| 649 | if fs.options.show.has(.flags) && fs.flags.len > 0 { |
| 650 | if fs.options.show.has(.flags_header) { |
| 651 | use << fs.options.flag_header.trim_space_left() |
| 652 | } |
| 653 | for f in fs.flags { |
| 654 | mut onames := []string{} |
| 655 | if f.abbr != 0 { |
| 656 | onames << '-${f.abbr.ascii_str()}' |
| 657 | } |
| 658 | if f.name != '' { |
| 659 | if !f.val_desc.contains('<bool>') { |
| 660 | onames << '--${f.name} ${f.val_desc}' |
| 661 | } else { |
| 662 | onames << '--${f.name}' |
| 663 | } |
| 664 | } |
| 665 | option_names := ' ' + onames.join(', ') |
| 666 | mut xspace := '' |
| 667 | if option_names.len > space.len - 2 { |
| 668 | xspace = '\n${space}' |
| 669 | } else { |
| 670 | xspace = space[option_names.len..] |
| 671 | } |
| 672 | mut fusage := f.usage |
| 673 | if f.has_default { |
| 674 | fusage += ' (default ${f.default_value})' |
| 675 | } |
| 676 | fdesc := '${option_names}${xspace}${fusage}' |
| 677 | use << fdesc |
| 678 | } |
| 679 | } |
| 680 | if fs.options.show.has(.footer) { |
| 681 | for footer in fs.footers { |
| 682 | use << footer |
| 683 | } |
| 684 | } |
| 685 | return use.join('\n').replace('- ,', ' ') |
| 686 | } |
| 687 | |
| 688 | // find_existing_flag looks up the given flag by name, and returns |
| 689 | // it, if it was found in the FlagParser. If it was not, it returns an error. |
| 690 | fn (mut fs FlagParser) find_existing_flag(fname string) !Flag { |
| 691 | for f in fs.flags { |
| 692 | if f.name == fname { |
| 693 | return f |
| 694 | } |
| 695 | } |
| 696 | return error('no such flag') |
| 697 | } |
| 698 | |
| 699 | // handle_builtin_options handles the default behaviour of the very frequently |
| 700 | // given options: `--help` and `--version`. |
| 701 | // You can change/customise that, by defining your own options with these names. |
| 702 | fn (mut fs FlagParser) handle_builtin_options() { |
| 703 | mut show_version := false |
| 704 | mut show_help := false |
| 705 | fs.find_existing_flag('help') or { |
| 706 | show_help = fs.bool_opt('help', `h`, fs.default_help_label) or { false } |
| 707 | } |
| 708 | fs.find_existing_flag('version') or { |
| 709 | show_version = fs.bool_opt('version', 0, fs.default_version_label) or { false } |
| 710 | } |
| 711 | if show_help { |
| 712 | println(fs.usage()) |
| 713 | exit(0) |
| 714 | } |
| 715 | if show_version { |
| 716 | println('${fs.application_name} ${fs.application_version}') |
| 717 | exit(0) |
| 718 | } |
| 719 | } |
| 720 | |
| 721 | // finalize - return all remaining arguments (non options). |
| 722 | // Call .finalize() after all arguments are defined. |
| 723 | // The remaining arguments are returned in the same order they are |
| 724 | // defined on the command line. If additional flags are found, i.e. |
| 725 | // (things starting with '--' or '-'), it returns an error. |
| 726 | pub fn (mut fs FlagParser) finalize() ![]string { |
| 727 | fs.handle_builtin_options() |
| 728 | mut remaining := fs.args.clone() |
| 729 | if !fs.allow_unknown_args { |
| 730 | for a in remaining { |
| 731 | if (a.len >= 2 && a[..2] == '--') || (a.len == 2 && a[0] == `-`) { |
| 732 | return &UnknownFlagError{ |
| 733 | flag: a |
| 734 | } |
| 735 | } |
| 736 | } |
| 737 | } |
| 738 | remaining << fs.all_after_dashdash |
| 739 | if fs.min_free_args > remaining.len { |
| 740 | return &ArgsCountError{ |
| 741 | want: fs.min_free_args |
| 742 | got: remaining.len |
| 743 | } |
| 744 | } |
| 745 | if fs.max_free_args < remaining.len { |
| 746 | return &ArgsCountError{ |
| 747 | want: fs.max_free_args |
| 748 | got: remaining.len |
| 749 | } |
| 750 | } |
| 751 | return remaining |
| 752 | } |
| 753 | |
| 754 | // remaining_parameters will return all remaining parameters. |
| 755 | // Call .remaining_parameters() *AFTER* you have defined all options |
| 756 | // that your program needs. remaining_parameters will also print any |
| 757 | // parsing errors and stop the program. Use .finalize() instead, if |
| 758 | // you want more control over the error handling. |
| 759 | pub fn (mut fs FlagParser) remaining_parameters() []string { |
| 760 | return fs.finalize() or { |
| 761 | eprintln(err.msg()) |
| 762 | println(fs.usage()) |
| 763 | exit(1) |
| 764 | } |
| 765 | } |
| 766 | |