2019-09-28 13:17:16 +02:00
|
|
|
module flag
|
2019-07-12 15:47:27 +02:00
|
|
|
|
2019-09-14 22:54:14 +02:00
|
|
|
// data object storing information about a defined flag
|
2019-12-01 10:50:13 +01:00
|
|
|
pub struct Flag {
|
2020-10-15 15:17:52 +02:00
|
|
|
pub:
|
2019-12-16 17:03:38 +01:00
|
|
|
name string // name as it appears on command line
|
2021-01-03 21:10:25 +01:00
|
|
|
abbr byte // shortcut
|
2019-12-16 17:03:38 +01:00
|
|
|
usage string // help message
|
|
|
|
val_desc string // something like '<arg>' that appears in usage,
|
|
|
|
// and also the default value, when the flag is not given
|
2019-07-12 15:47:27 +02:00
|
|
|
}
|
|
|
|
|
2021-03-30 14:31:02 +02:00
|
|
|
struct UnkownFlagError {
|
2022-02-11 14:52:33 +01:00
|
|
|
Error
|
|
|
|
flag string
|
2021-03-30 14:31:02 +02:00
|
|
|
}
|
|
|
|
|
2022-02-11 14:52:33 +01:00
|
|
|
fn (err UnkownFlagError) msg() string {
|
|
|
|
return 'Unknown flag `$err.flag`'
|
2021-03-30 14:31:02 +02:00
|
|
|
}
|
|
|
|
|
2022-02-11 14:52:33 +01:00
|
|
|
struct ArgsCountError {
|
|
|
|
Error
|
|
|
|
got int
|
|
|
|
want int
|
2021-03-30 14:31:02 +02:00
|
|
|
}
|
|
|
|
|
2022-02-11 14:52:33 +01:00
|
|
|
fn (err ArgsCountError) msg() string {
|
|
|
|
if err.want == 0 {
|
|
|
|
return 'Expected no arguments, but got $err.got'
|
|
|
|
} else if err.got > err.want {
|
|
|
|
return 'Expected at most $err.want arguments, but got $err.got'
|
|
|
|
} else {
|
|
|
|
return 'Expected at least $err.want arguments, but got $err.got'
|
|
|
|
}
|
2021-03-30 14:31:02 +02:00
|
|
|
}
|
|
|
|
|
2021-11-18 19:14:59 +01:00
|
|
|
// free frees the resources associated with a given Flag
|
|
|
|
// It is called automatically when -autofree is used.
|
|
|
|
// It should be called manually in functions that use Flags,
|
|
|
|
// and are marked with [manualfree]. After you call .free() on
|
|
|
|
// a Flag instance, you should NOT use that instance any more.
|
2021-03-23 20:28:44 +01:00
|
|
|
[unsafe]
|
|
|
|
fn (mut f Flag) free() {
|
|
|
|
unsafe {
|
|
|
|
f.name.free()
|
|
|
|
f.usage.free()
|
|
|
|
f.val_desc.free()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-11-18 19:14:59 +01:00
|
|
|
// str returns a string representation of the given Flag
|
2020-01-08 21:45:47 +01:00
|
|
|
pub fn (f Flag) str() string {
|
2021-07-09 10:13:32 +02:00
|
|
|
return '' + ' flag:\n' + ' name: $f.name\n' +
|
|
|
|
' abbr: `$f.abbr.ascii_str()`\n' + ' usag: $f.usage\n' +
|
|
|
|
' desc: $f.val_desc'
|
2020-01-08 21:45:47 +01:00
|
|
|
}
|
2020-10-15 15:17:52 +02:00
|
|
|
|
2021-11-18 19:14:59 +01:00
|
|
|
// str returns a string representation of the given array of Flags
|
2020-01-08 21:45:47 +01:00
|
|
|
pub fn (af []Flag) str() string {
|
2020-04-26 13:49:31 +02:00
|
|
|
mut res := []string{}
|
2020-01-08 21:45:47 +01:00
|
|
|
res << '\n []Flag = ['
|
|
|
|
for f in af {
|
|
|
|
res << f.str()
|
|
|
|
}
|
|
|
|
res << ' ]'
|
|
|
|
return res.join('\n')
|
|
|
|
}
|
2020-10-15 15:17:52 +02:00
|
|
|
|
2021-11-18 19:14:59 +01:00
|
|
|
// FlagParser is the heart of the `flag` module.
|
|
|
|
// That structure is created with `mut parser := flag.new_flag_parser(os.args)`,
|
|
|
|
// The returned instance can be further customised by calling various methods,
|
|
|
|
// for specifying the accepted options and their values. The user should finally
|
|
|
|
// call `rest := parser.finalize() ?` to get the rest of the non optional arguments
|
|
|
|
// (if there are any left).
|
2019-12-01 10:50:13 +01:00
|
|
|
pub struct FlagParser {
|
2021-07-09 10:13:32 +02:00
|
|
|
pub:
|
|
|
|
original_args []string // the original arguments to be parsed
|
|
|
|
idx_dashdash int // the index of a `--`, -1 if there is not any
|
|
|
|
all_after_dashdash []string // all options after `--` are ignored, and will be passed to the application unmodified
|
2020-10-15 15:17:52 +02:00
|
|
|
pub mut:
|
2021-07-11 09:52:16 +02:00
|
|
|
usage_examples []string // when set, --help will print:
|
|
|
|
// Usage: $appname $usage_examples[0]`
|
|
|
|
// or: $appname $usage_examples[1]`
|
|
|
|
// etc
|
2021-07-09 21:08:01 +02:00
|
|
|
default_help_label string = 'display this help and exit'
|
|
|
|
default_version_label string = 'output version information and exit'
|
2021-07-09 10:13:32 +02:00
|
|
|
args []string // the current list of processed args
|
2020-10-15 15:17:52 +02:00
|
|
|
max_free_args int
|
|
|
|
flags []Flag // registered flags
|
2019-12-16 17:03:38 +01:00
|
|
|
application_name string
|
|
|
|
application_version string
|
|
|
|
application_description string
|
2020-10-15 15:17:52 +02:00
|
|
|
min_free_args int
|
2019-12-16 17:03:38 +01:00
|
|
|
args_description string
|
2021-07-11 09:52:16 +02:00
|
|
|
allow_unknown_args bool // whether passing undescribed arguments is allowed
|
|
|
|
footers []string // when set, --help will display all the collected footers at the bottom.
|
2019-07-12 15:47:27 +02:00
|
|
|
}
|
|
|
|
|
2021-11-18 19:14:59 +01:00
|
|
|
// free frees the resources allocated for the given FlagParser instance.
|
|
|
|
// It should be called manually in functions that use it, and that are
|
|
|
|
// marked with `[manualfree]`, otherwise, it is called automatically
|
2022-03-06 18:01:22 +01:00
|
|
|
// in programs, compiled with `-autofree`. Note: you should NOT use the
|
2021-11-18 19:14:59 +01:00
|
|
|
// instance over which you have called .free() for anything after the call.
|
2021-03-23 19:36:46 +01:00
|
|
|
[unsafe]
|
|
|
|
fn (mut f FlagParser) free() {
|
|
|
|
unsafe {
|
2021-03-23 20:28:44 +01:00
|
|
|
for a in f.args {
|
|
|
|
a.free()
|
|
|
|
}
|
2021-03-23 19:36:46 +01:00
|
|
|
f.args.free()
|
2021-03-23 20:28:44 +01:00
|
|
|
//
|
|
|
|
for flag in f.flags {
|
|
|
|
flag.free()
|
|
|
|
}
|
2021-03-23 19:36:46 +01:00
|
|
|
f.flags.free()
|
2021-03-23 20:28:44 +01:00
|
|
|
//
|
2021-03-23 19:36:46 +01:00
|
|
|
f.application_name.free()
|
|
|
|
f.application_version.free()
|
|
|
|
f.application_description.free()
|
|
|
|
f.args_description.free()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-12-01 10:50:13 +01:00
|
|
|
pub const (
|
2019-12-16 17:03:38 +01:00
|
|
|
// used for formating usage message
|
2020-10-15 15:17:52 +02:00
|
|
|
space = ' '
|
|
|
|
underline = '-----------------------------------------------'
|
2020-05-22 17:36:09 +02:00
|
|
|
max_args_number = 4048
|
2019-09-28 13:17:16 +02:00
|
|
|
)
|
|
|
|
|
2021-11-18 19:14:59 +01:00
|
|
|
// new_flag_parser - create a new flag parser for the given args
|
2019-07-12 15:47:27 +02:00
|
|
|
pub fn new_flag_parser(args []string) &FlagParser {
|
2021-07-09 10:13:32 +02:00
|
|
|
original_args := args.clone()
|
|
|
|
idx_dashdash := args.index('--')
|
|
|
|
mut all_before_dashdash := args.clone()
|
|
|
|
mut all_after_dashdash := []string{}
|
|
|
|
if idx_dashdash >= 0 {
|
|
|
|
all_before_dashdash.trim(idx_dashdash)
|
|
|
|
if idx_dashdash < original_args.len {
|
|
|
|
all_after_dashdash = original_args[idx_dashdash + 1..]
|
|
|
|
}
|
|
|
|
}
|
2020-10-15 15:17:52 +02:00
|
|
|
return &FlagParser{
|
2021-07-09 10:13:32 +02:00
|
|
|
original_args: original_args
|
|
|
|
idx_dashdash: idx_dashdash
|
|
|
|
all_after_dashdash: all_after_dashdash
|
|
|
|
args: all_before_dashdash
|
2021-01-25 10:26:20 +01:00
|
|
|
max_free_args: flag.max_args_number
|
2020-10-15 15:17:52 +02:00
|
|
|
}
|
2019-07-12 15:47:27 +02:00
|
|
|
}
|
|
|
|
|
2021-07-11 09:52:16 +02:00
|
|
|
// usage_example - add an usage example
|
|
|
|
// All examples will be listed in the help screen.
|
|
|
|
// If you do not give any examples, then a default usage
|
|
|
|
// will be shown, based on whether the application takes
|
|
|
|
// options and expects additional parameters.
|
|
|
|
pub fn (mut fs FlagParser) usage_example(example string) {
|
|
|
|
fs.usage_examples << example
|
|
|
|
}
|
|
|
|
|
|
|
|
// add_footer - add a footnote, that will be shown
|
|
|
|
// at the bottom of the help screen.
|
|
|
|
pub fn (mut fs FlagParser) footer(footer string) {
|
|
|
|
fs.footers << footer
|
|
|
|
}
|
|
|
|
|
2019-07-12 15:47:27 +02:00
|
|
|
// change the application name to be used in 'usage' output
|
2020-05-17 13:51:18 +02:00
|
|
|
pub fn (mut fs FlagParser) application(name string) {
|
2019-12-30 16:38:32 +01:00
|
|
|
fs.application_name = name
|
2019-07-12 15:47:27 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
// change the application version to be used in 'usage' output
|
2020-05-17 13:51:18 +02:00
|
|
|
pub fn (mut fs FlagParser) version(vers string) {
|
2019-12-30 16:38:32 +01:00
|
|
|
fs.application_version = vers
|
2019-07-12 15:47:27 +02:00
|
|
|
}
|
|
|
|
|
2021-07-11 09:52:16 +02:00
|
|
|
// description appends to the application description lines, shown
|
|
|
|
// in the help/usage screen
|
2020-05-17 13:51:18 +02:00
|
|
|
pub fn (mut fs FlagParser) description(desc string) {
|
2021-07-11 09:52:16 +02:00
|
|
|
if fs.application_description.len == 0 {
|
|
|
|
fs.application_description = desc
|
|
|
|
} else {
|
|
|
|
fs.application_description += '\n$desc'
|
|
|
|
}
|
2019-07-12 15:47:27 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
// in most cases you do not need the first argv for flag parsing
|
2020-05-17 13:51:18 +02:00
|
|
|
pub fn (mut fs FlagParser) skip_executable() {
|
2019-12-16 17:03:38 +01:00
|
|
|
fs.args.delete(0)
|
2019-07-12 15:47:27 +02:00
|
|
|
}
|
|
|
|
|
2021-04-28 11:23:23 +02:00
|
|
|
// allow_unknown_args - if your program has sub commands, that have
|
|
|
|
// their own arguments, you can call .allow_unknown_args(), so that
|
|
|
|
// the subcommand arguments (which generally are not known to your
|
|
|
|
// parent program), will not cause the validation in .finalize() to fail.
|
|
|
|
pub fn (mut fs FlagParser) allow_unknown_args() {
|
|
|
|
fs.allow_unknown_args = true
|
|
|
|
}
|
|
|
|
|
2019-07-12 15:47:27 +02:00
|
|
|
// private helper to register a flag
|
2022-01-18 17:38:27 +01:00
|
|
|
// This version supports abbreviations.
|
2020-05-17 13:51:18 +02:00
|
|
|
fn (mut fs FlagParser) add_flag(name string, abbr byte, usage string, desc string) {
|
2019-12-16 17:03:38 +01:00
|
|
|
fs.flags << Flag{
|
2020-10-15 15:17:52 +02:00
|
|
|
name: name
|
|
|
|
abbr: abbr
|
|
|
|
usage: usage
|
2019-12-30 16:38:32 +01:00
|
|
|
val_desc: desc
|
2019-12-16 17:03:38 +01:00
|
|
|
}
|
2019-07-12 15:47:27 +02:00
|
|
|
}
|
|
|
|
|
2019-12-16 17:03:38 +01:00
|
|
|
// private: general parsing a single argument
|
2020-10-15 15:17:52 +02:00
|
|
|
// - search args for existence
|
|
|
|
// if true
|
|
|
|
// extract the defined value as string
|
|
|
|
// else
|
|
|
|
// return an (dummy) error -> argument is not defined
|
2019-07-12 15:47:27 +02:00
|
|
|
//
|
2020-10-15 15:17:52 +02:00
|
|
|
// - the name, usage are registered
|
|
|
|
// - found arguments and corresponding values are removed from args list
|
2021-03-23 20:28:44 +01:00
|
|
|
[manualfree]
|
2020-05-17 13:51:18 +02:00
|
|
|
fn (mut fs FlagParser) parse_value(longhand string, shorthand byte) []string {
|
2019-12-16 17:03:38 +01:00
|
|
|
full := '--$longhand'
|
2021-03-23 20:28:44 +01:00
|
|
|
defer {
|
|
|
|
unsafe { full.free() }
|
|
|
|
}
|
2020-04-26 13:49:31 +02:00
|
|
|
mut found_entries := []string{}
|
|
|
|
mut to_delete := []int{}
|
2021-03-23 20:28:44 +01:00
|
|
|
defer {
|
|
|
|
unsafe { to_delete.free() }
|
|
|
|
}
|
2019-12-16 17:03:38 +01:00
|
|
|
mut should_skip_one := false
|
|
|
|
for i, arg in fs.args {
|
|
|
|
if should_skip_one {
|
|
|
|
should_skip_one = false
|
|
|
|
continue
|
|
|
|
}
|
2021-07-24 10:26:00 +02:00
|
|
|
if arg.len == 0 || arg[0] != `-` {
|
2020-01-08 21:45:47 +01:00
|
|
|
continue
|
|
|
|
}
|
2020-10-15 15:17:52 +02:00
|
|
|
if (arg.len == 2 && arg[0] == `-` && arg[1] == shorthand) || arg == full {
|
|
|
|
if i + 1 >= fs.args.len {
|
2020-09-25 15:31:35 +02:00
|
|
|
return []
|
2019-12-16 17:03:38 +01:00
|
|
|
}
|
2020-10-15 15:17:52 +02:00
|
|
|
nextarg := fs.args[i + 1]
|
2021-03-23 20:28:44 +01:00
|
|
|
if nextarg.len > 2 {
|
|
|
|
nextarg_rest := nextarg[..2]
|
|
|
|
if nextarg_rest == '--' {
|
|
|
|
// It could be end of input (--) or another argument (--abc).
|
|
|
|
// Both are invalid so die.
|
|
|
|
unsafe { nextarg_rest.free() }
|
|
|
|
return []
|
|
|
|
}
|
|
|
|
unsafe { nextarg_rest.free() }
|
2019-12-16 17:03:38 +01:00
|
|
|
}
|
2020-10-15 15:17:52 +02:00
|
|
|
found_entries << fs.args[i + 1]
|
2019-12-16 17:03:38 +01:00
|
|
|
to_delete << i
|
2020-10-15 15:17:52 +02:00
|
|
|
to_delete << i + 1
|
2019-12-16 17:03:38 +01:00
|
|
|
should_skip_one = true
|
|
|
|
continue
|
|
|
|
}
|
2020-10-15 15:17:52 +02:00
|
|
|
if arg.len > full.len + 1 && arg[..full.len + 1] == '$full=' {
|
|
|
|
found_entries << arg[full.len + 1..]
|
2019-12-16 17:03:38 +01:00
|
|
|
to_delete << i
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
}
|
|
|
|
for i, del in to_delete {
|
2020-10-15 15:17:52 +02:00
|
|
|
// i entrys are deleted so it's shifted left i times.
|
2019-12-16 17:03:38 +01:00
|
|
|
fs.args.delete(del - i)
|
|
|
|
}
|
|
|
|
return found_entries
|
2019-07-12 15:47:27 +02:00
|
|
|
}
|
|
|
|
|
2019-12-16 17:03:38 +01:00
|
|
|
// special parsing for bool values
|
2019-07-12 15:47:27 +02:00
|
|
|
// see also: parse_value
|
2019-12-16 17:03:38 +01:00
|
|
|
//
|
2019-07-12 15:47:27 +02:00
|
|
|
// special: it is allowed to define bool flags without value
|
|
|
|
// -> '--flag' is parsed as true
|
|
|
|
// -> '--flag' is equal to '--flag=true'
|
2020-05-17 13:51:18 +02:00
|
|
|
fn (mut fs FlagParser) parse_bool_value(longhand string, shorthand byte) ?string {
|
2021-03-23 20:28:44 +01:00
|
|
|
{
|
|
|
|
full := '--$longhand'
|
|
|
|
for i, arg in fs.args {
|
|
|
|
if arg.len == 0 {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
if arg[0] != `-` {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
if (arg.len == 2 && arg[0] == `-` && arg[1] == shorthand) || arg == full {
|
|
|
|
if fs.args.len > i + 1 && (fs.args[i + 1] in ['true', 'false']) {
|
|
|
|
val := fs.args[i + 1]
|
|
|
|
fs.args.delete(i + 1)
|
|
|
|
fs.args.delete(i)
|
|
|
|
return val
|
|
|
|
} else {
|
|
|
|
fs.args.delete(i)
|
|
|
|
return 'true'
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if arg.len > full.len + 1 && arg[..full.len + 1] == '$full=' {
|
|
|
|
// Flag abc=true
|
|
|
|
val := arg[full.len + 1..]
|
2019-12-16 17:03:38 +01:00
|
|
|
fs.args.delete(i)
|
|
|
|
return val
|
2021-03-23 20:28:44 +01:00
|
|
|
}
|
2022-04-15 13:58:56 +02:00
|
|
|
if arg.len > 1 && arg[0] == `-` && arg[1] != `-` && arg.index_u8(shorthand) != -1 {
|
2021-03-23 20:28:44 +01:00
|
|
|
// -abc is equivalent to -a -b -c
|
2019-12-16 17:03:38 +01:00
|
|
|
return 'true'
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return error("parameter '$longhand' not found")
|
|
|
|
}
|
|
|
|
|
2022-01-18 17:38:27 +01:00
|
|
|
// bool_opt returns an option with the bool value of the given command line flag, named `name`.
|
|
|
|
// It returns an error, when the flag is not given by the user.
|
|
|
|
// This version supports abbreviations.
|
2020-05-17 13:51:18 +02:00
|
|
|
pub fn (mut fs FlagParser) bool_opt(name string, abbr byte, usage string) ?bool {
|
2021-03-23 20:28:44 +01:00
|
|
|
mut res := false
|
|
|
|
{
|
|
|
|
fs.add_flag(name, abbr, usage, '<bool>')
|
|
|
|
parsed := fs.parse_bool_value(name, abbr) or {
|
|
|
|
return error("parameter '$name' not provided")
|
|
|
|
}
|
|
|
|
res = parsed == 'true'
|
|
|
|
}
|
|
|
|
return res
|
2019-07-12 15:47:27 +02:00
|
|
|
}
|
|
|
|
|
2022-01-18 17:38:27 +01:00
|
|
|
// bool defines and parses a string flag/option named `name`.
|
|
|
|
// If that flag is given by the user, then it returns its parsed bool value.
|
|
|
|
// When it is not, it returns the default value in `bdefault`.
|
|
|
|
// This version supports abbreviations.
|
2020-05-17 13:51:18 +02:00
|
|
|
pub fn (mut fs FlagParser) bool(name string, abbr byte, bdefault bool, usage string) bool {
|
2020-12-21 21:24:10 +01:00
|
|
|
value := fs.bool_opt(name, abbr, usage) or { return bdefault }
|
2019-12-16 17:03:38 +01:00
|
|
|
return value
|
2019-07-12 15:47:27 +02:00
|
|
|
}
|
|
|
|
|
2022-01-18 17:38:27 +01:00
|
|
|
// int_multi returns all values associated with the provided flag in `name`.
|
|
|
|
// When that flag has no values, it returns an empty array.
|
|
|
|
// This version supports abbreviations.
|
2020-05-17 13:51:18 +02:00
|
|
|
pub fn (mut fs FlagParser) int_multi(name string, abbr byte, usage string) []int {
|
2019-12-30 16:38:32 +01:00
|
|
|
fs.add_flag(name, abbr, usage, '<multiple ints>')
|
|
|
|
parsed := fs.parse_value(name, abbr)
|
2020-04-26 13:49:31 +02:00
|
|
|
mut value := []int{}
|
2019-12-16 17:03:38 +01:00
|
|
|
for val in parsed {
|
|
|
|
value << val.int()
|
|
|
|
}
|
|
|
|
return value
|
|
|
|
}
|
|
|
|
|
2022-01-18 17:38:27 +01:00
|
|
|
// int_opt returns an option with the integer value, associated with the flag in `name`.
|
|
|
|
// When the flag is not given by the user, it returns an error.
|
|
|
|
// This version supports abbreviations.
|
2020-05-17 13:51:18 +02:00
|
|
|
pub fn (mut fs FlagParser) int_opt(name string, abbr byte, usage string) ?int {
|
2021-03-23 20:28:44 +01:00
|
|
|
mut res := 0
|
|
|
|
{
|
|
|
|
fs.add_flag(name, abbr, usage, '<int>')
|
|
|
|
parsed := fs.parse_value(name, abbr)
|
|
|
|
if parsed.len == 0 {
|
|
|
|
return error("parameter '$name' not provided")
|
|
|
|
}
|
|
|
|
parsed0 := parsed[0]
|
|
|
|
res = parsed0.int()
|
2019-12-16 17:03:38 +01:00
|
|
|
}
|
2021-03-23 20:28:44 +01:00
|
|
|
return res
|
2019-08-07 16:52:10 +02:00
|
|
|
}
|
|
|
|
|
2022-01-18 17:38:27 +01:00
|
|
|
// int defines and parses an integer flag, named `name`.
|
|
|
|
// When the flag is given by the user, it returns its parsed integer value.
|
|
|
|
// When it is not, it returns the integer value in `idefault`.
|
|
|
|
// This version supports abbreviations.
|
2020-05-17 13:51:18 +02:00
|
|
|
pub fn (mut fs FlagParser) int(name string, abbr byte, idefault int, usage string) int {
|
2020-12-21 21:24:10 +01:00
|
|
|
value := fs.int_opt(name, abbr, usage) or { return idefault }
|
2019-12-16 17:03:38 +01:00
|
|
|
return value
|
2019-07-12 15:47:27 +02:00
|
|
|
}
|
|
|
|
|
2022-01-18 17:38:27 +01:00
|
|
|
// float_multi returns all floating point values, associated with the flag named `name`.
|
|
|
|
// When no values for that flag are found, it returns an empty array.
|
|
|
|
// This version supports abbreviations.
|
2020-05-17 13:51:18 +02:00
|
|
|
pub fn (mut fs FlagParser) float_multi(name string, abbr byte, usage string) []f64 {
|
2019-12-30 16:38:32 +01:00
|
|
|
fs.add_flag(name, abbr, usage, '<multiple floats>')
|
|
|
|
parsed := fs.parse_value(name, abbr)
|
2020-04-26 13:49:31 +02:00
|
|
|
mut value := []f64{}
|
2019-12-16 17:03:38 +01:00
|
|
|
for val in parsed {
|
2020-04-21 13:44:17 +02:00
|
|
|
value << val.f64()
|
2019-12-16 17:03:38 +01:00
|
|
|
}
|
|
|
|
return value
|
2019-08-07 16:52:10 +02:00
|
|
|
}
|
|
|
|
|
2022-01-18 17:38:27 +01:00
|
|
|
// float_opt returns an option with the floating point value, associated with the flag in `name`.
|
|
|
|
// When the flag is not given by the user, it returns an error.
|
|
|
|
// This version supports abbreviations.
|
2020-05-17 13:51:18 +02:00
|
|
|
pub fn (mut fs FlagParser) float_opt(name string, abbr byte, usage string) ?f64 {
|
2021-03-23 20:28:44 +01:00
|
|
|
mut res := 0.0
|
|
|
|
{
|
|
|
|
fs.add_flag(name, abbr, usage, '<float>')
|
|
|
|
parsed := fs.parse_value(name, abbr)
|
|
|
|
if parsed.len == 0 {
|
|
|
|
return error("parameter '$name' not provided")
|
|
|
|
}
|
|
|
|
res = parsed[0].f64()
|
2019-12-16 17:03:38 +01:00
|
|
|
}
|
2021-03-23 20:28:44 +01:00
|
|
|
return res
|
2019-12-16 17:03:38 +01:00
|
|
|
}
|
|
|
|
|
2022-01-18 17:38:27 +01:00
|
|
|
// float defines and parses a floating point flag, named `name`.
|
|
|
|
// When the flag is given by the user, it returns its parsed floating point value.
|
|
|
|
// When it is not, it returns the value in `fdefault`.
|
|
|
|
// This version supports abbreviations.
|
2020-05-17 13:51:18 +02:00
|
|
|
pub fn (mut fs FlagParser) float(name string, abbr byte, fdefault f64, usage string) f64 {
|
2020-12-21 21:24:10 +01:00
|
|
|
value := fs.float_opt(name, abbr, usage) or { return fdefault }
|
2019-12-16 17:03:38 +01:00
|
|
|
return value
|
2019-07-12 15:47:27 +02:00
|
|
|
}
|
|
|
|
|
2022-01-18 17:38:27 +01:00
|
|
|
// string_multi returns all string values, associated with the flag named `name`.
|
|
|
|
// When no values for that flag are found, it returns an empty array.
|
|
|
|
// This version supports abbreviations.
|
2020-05-17 13:51:18 +02:00
|
|
|
pub fn (mut fs FlagParser) string_multi(name string, abbr byte, usage string) []string {
|
2020-05-26 14:27:01 +02:00
|
|
|
fs.add_flag(name, abbr, usage, '<multiple strings>')
|
2019-12-30 16:38:32 +01:00
|
|
|
return fs.parse_value(name, abbr)
|
2019-08-07 16:52:10 +02:00
|
|
|
}
|
|
|
|
|
2022-01-18 17:38:27 +01:00
|
|
|
// string_opt returns an option with the string value, associated with the flag in `name`.
|
|
|
|
// When the flag is not given by the user, it returns an error.
|
|
|
|
// This version supports abbreviations.
|
2020-05-17 13:51:18 +02:00
|
|
|
pub fn (mut fs FlagParser) string_opt(name string, abbr byte, usage string) ?string {
|
2021-03-23 20:28:44 +01:00
|
|
|
mut res := ''
|
|
|
|
{
|
|
|
|
fs.add_flag(name, abbr, usage, '<string>')
|
|
|
|
parsed := fs.parse_value(name, abbr)
|
|
|
|
if parsed.len == 0 {
|
|
|
|
return error("parameter '$name' not provided")
|
|
|
|
}
|
|
|
|
res = parsed[0]
|
2019-12-16 17:03:38 +01:00
|
|
|
}
|
2021-03-23 20:28:44 +01:00
|
|
|
return res
|
2019-12-16 17:03:38 +01:00
|
|
|
}
|
|
|
|
|
2022-01-18 17:38:27 +01:00
|
|
|
// string defines and parses a string flag/option, named `name`.
|
|
|
|
// If that flag is given as an option, then its parsed value is returned as a string.
|
|
|
|
// When it is not, it returns the default string value in `sdefault`.
|
|
|
|
// This version supports abbreviations.
|
2020-05-17 13:51:18 +02:00
|
|
|
pub fn (mut fs FlagParser) string(name string, abbr byte, sdefault string, usage string) string {
|
2020-12-21 21:24:10 +01:00
|
|
|
value := fs.string_opt(name, abbr, usage) or { return sdefault }
|
2019-12-16 17:03:38 +01:00
|
|
|
return value
|
2019-07-12 15:47:27 +02:00
|
|
|
}
|
|
|
|
|
2021-11-18 19:14:59 +01:00
|
|
|
// limit_free_args_to_at_least restricts the list of free arguments (non options) to be
|
|
|
|
// at least `n` in length. If the user gives less free arguments to the program,
|
|
|
|
// the parser will return an error.
|
2021-09-16 18:25:05 +02:00
|
|
|
pub fn (mut fs FlagParser) limit_free_args_to_at_least(n int) ? {
|
2021-01-25 10:26:20 +01:00
|
|
|
if n > flag.max_args_number {
|
2021-09-16 18:25:05 +02:00
|
|
|
return error('flag.limit_free_args_to_at_least expect n to be smaller than $flag.max_args_number')
|
2019-12-16 17:03:38 +01:00
|
|
|
}
|
2019-09-28 13:17:16 +02:00
|
|
|
if n <= 0 {
|
2021-09-16 18:25:05 +02:00
|
|
|
return error('flag.limit_free_args_to_at_least expect n to be a positive number')
|
2019-12-16 17:03:38 +01:00
|
|
|
}
|
|
|
|
fs.min_free_args = n
|
2019-09-28 13:17:16 +02:00
|
|
|
}
|
|
|
|
|
2021-11-18 19:14:59 +01:00
|
|
|
// limit_free_args_to_exactly restricts the list of free arguments (non options) to be
|
|
|
|
// at exactly `n` in length. If the user gives more or less free arguments to the program,
|
|
|
|
// the parser will return an error.
|
2021-09-16 18:25:05 +02:00
|
|
|
pub fn (mut fs FlagParser) limit_free_args_to_exactly(n int) ? {
|
2021-01-25 10:26:20 +01:00
|
|
|
if n > flag.max_args_number {
|
2021-09-16 18:25:05 +02:00
|
|
|
return error('flag.limit_free_args_to_exactly expect n to be smaller than $flag.max_args_number')
|
2019-12-16 17:03:38 +01:00
|
|
|
}
|
2019-09-28 13:17:16 +02:00
|
|
|
if n < 0 {
|
2021-09-16 18:25:05 +02:00
|
|
|
return error('flag.limit_free_args_to_exactly expect n to be a non negative number')
|
2019-12-16 17:03:38 +01:00
|
|
|
}
|
|
|
|
fs.min_free_args = n
|
|
|
|
fs.max_free_args = n
|
2019-09-28 13:17:16 +02:00
|
|
|
}
|
|
|
|
|
2021-11-18 19:14:59 +01:00
|
|
|
// limit_free_args restricts the list of free arguments (non options) to be between
|
|
|
|
// `min` and `max` in length. If the user gives more or less free arguments to the program,
|
|
|
|
// the parser will return an error.
|
2021-09-16 18:25:05 +02:00
|
|
|
pub fn (mut fs FlagParser) limit_free_args(min int, max int) ? {
|
2019-12-16 17:03:38 +01:00
|
|
|
if min > max {
|
2021-09-16 18:25:05 +02:00
|
|
|
return error('flag.limit_free_args expect min < max, got $min >= $max')
|
2019-12-16 17:03:38 +01:00
|
|
|
}
|
|
|
|
fs.min_free_args = min
|
|
|
|
fs.max_free_args = max
|
2019-08-05 07:50:24 +02:00
|
|
|
}
|
|
|
|
|
2021-11-18 19:14:59 +01:00
|
|
|
// arguments_description sets the description field of the parser.
|
|
|
|
// This field is usually shown when the `--help` option is given to the program.
|
2020-10-15 15:17:52 +02:00
|
|
|
pub fn (mut fs FlagParser) arguments_description(description string) {
|
2019-12-16 17:03:38 +01:00
|
|
|
fs.args_description = description
|
2019-09-28 13:17:16 +02:00
|
|
|
}
|
2019-07-12 15:47:27 +02:00
|
|
|
|
2021-11-18 19:14:59 +01:00
|
|
|
// usage returns a nicely formatted usage screen, containing all the
|
|
|
|
// possible options, as well as the description for the program.
|
|
|
|
// That screen is usually shown when the `--help` option is given to the program.
|
2019-07-12 15:47:27 +02:00
|
|
|
pub fn (fs FlagParser) usage() string {
|
2020-10-15 15:17:52 +02:00
|
|
|
positive_min_arg := (fs.min_free_args > 0)
|
2021-01-25 10:26:20 +01:00
|
|
|
positive_max_arg := (fs.max_free_args > 0 && fs.max_free_args != flag.max_args_number)
|
2020-10-15 15:17:52 +02:00
|
|
|
no_arguments := (fs.min_free_args == 0 && fs.max_free_args == 0)
|
2019-12-16 17:03:38 +01:00
|
|
|
mut adesc := if fs.args_description.len > 0 { fs.args_description } else { '[ARGS]' }
|
2020-10-15 15:17:52 +02:00
|
|
|
if no_arguments {
|
|
|
|
adesc = ''
|
|
|
|
}
|
2021-03-23 20:28:44 +01:00
|
|
|
mut use := []string{}
|
2019-12-30 05:21:22 +01:00
|
|
|
if fs.application_version != '' {
|
2021-03-23 20:28:44 +01:00
|
|
|
use << '$fs.application_name $fs.application_version'
|
|
|
|
use << '$flag.underline'
|
2019-12-30 05:21:22 +01:00
|
|
|
}
|
2021-07-11 09:52:16 +02:00
|
|
|
if fs.usage_examples.len == 0 {
|
|
|
|
use << 'Usage: $fs.application_name [options] $adesc'
|
|
|
|
} else {
|
|
|
|
for i, example in fs.usage_examples {
|
|
|
|
if i == 0 {
|
|
|
|
use << 'Usage: $fs.application_name $example'
|
|
|
|
} else {
|
|
|
|
use << ' or: $fs.application_name $example'
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2021-03-23 20:28:44 +01:00
|
|
|
use << ''
|
2019-12-16 17:03:38 +01:00
|
|
|
if fs.application_description != '' {
|
2021-03-23 20:28:44 +01:00
|
|
|
use << 'Description: $fs.application_description'
|
|
|
|
use << ''
|
2019-12-16 17:03:38 +01:00
|
|
|
}
|
|
|
|
// show a message about the [ARGS]:
|
|
|
|
if positive_min_arg || positive_max_arg || no_arguments {
|
|
|
|
if no_arguments {
|
2021-03-23 20:28:44 +01:00
|
|
|
use << 'This application does not expect any arguments'
|
|
|
|
use << ''
|
2021-02-15 14:48:24 +01:00
|
|
|
} else {
|
|
|
|
mut s := []string{}
|
|
|
|
if positive_min_arg {
|
|
|
|
s << 'at least $fs.min_free_args'
|
|
|
|
}
|
|
|
|
if positive_max_arg {
|
|
|
|
s << 'at most $fs.max_free_args'
|
|
|
|
}
|
|
|
|
if positive_min_arg && positive_max_arg && fs.min_free_args == fs.max_free_args {
|
|
|
|
s = ['exactly $fs.min_free_args']
|
|
|
|
}
|
|
|
|
sargs := s.join(' and ')
|
2021-03-23 20:28:44 +01:00
|
|
|
use << 'The arguments should be $sargs in number.'
|
|
|
|
use << ''
|
2019-12-16 17:03:38 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
if fs.flags.len > 0 {
|
2021-03-23 20:28:44 +01:00
|
|
|
use << 'Options:'
|
2019-12-16 17:03:38 +01:00
|
|
|
for f in fs.flags {
|
2020-04-27 22:53:26 +02:00
|
|
|
mut onames := []string{}
|
2019-12-30 16:38:32 +01:00
|
|
|
if f.abbr != 0 {
|
2021-01-05 18:59:51 +01:00
|
|
|
onames << '-$f.abbr.ascii_str()'
|
2019-12-30 05:21:22 +01:00
|
|
|
}
|
2019-12-30 16:38:32 +01:00
|
|
|
if f.name != '' {
|
|
|
|
if !f.val_desc.contains('<bool>') {
|
2020-10-15 15:17:52 +02:00
|
|
|
onames << '--$f.name $f.val_desc'
|
|
|
|
} else {
|
|
|
|
onames << '--$f.name'
|
2019-12-30 16:38:32 +01:00
|
|
|
}
|
2019-12-30 05:21:22 +01:00
|
|
|
}
|
2019-12-30 16:38:32 +01:00
|
|
|
option_names := ' ' + onames.join(', ')
|
2020-04-11 16:24:21 +02:00
|
|
|
mut xspace := ''
|
2021-01-25 10:26:20 +01:00
|
|
|
if option_names.len > flag.space.len - 2 {
|
|
|
|
xspace = '\n$flag.space'
|
2019-12-16 17:03:38 +01:00
|
|
|
} else {
|
2021-01-25 10:26:20 +01:00
|
|
|
xspace = flag.space[option_names.len..]
|
2019-12-16 17:03:38 +01:00
|
|
|
}
|
2021-03-23 20:28:44 +01:00
|
|
|
fdesc := '$option_names$xspace$f.usage'
|
|
|
|
use << fdesc
|
2019-12-16 17:03:38 +01:00
|
|
|
}
|
|
|
|
}
|
2021-07-11 09:52:16 +02:00
|
|
|
for footer in fs.footers {
|
|
|
|
use << footer
|
|
|
|
}
|
2021-03-23 20:28:44 +01:00
|
|
|
return use.join('\n').replace('- ,', ' ')
|
2019-07-12 15:47:27 +02:00
|
|
|
}
|
|
|
|
|
2021-11-18 19:14:59 +01:00
|
|
|
// find_existing_flag looks up the given flag by name, and returns
|
|
|
|
// it, if it was found in the FlagParser. If it was not, it returns an error.
|
2021-07-09 21:08:01 +02:00
|
|
|
fn (mut fs FlagParser) find_existing_flag(fname string) ?Flag {
|
|
|
|
for f in fs.flags {
|
|
|
|
if f.name == fname {
|
|
|
|
return f
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return error('no such flag')
|
|
|
|
}
|
|
|
|
|
2021-11-18 19:14:59 +01:00
|
|
|
// handle_builtin_options handles the default behaviour of the very frequently
|
|
|
|
// given options: `--help` and `--version`.
|
|
|
|
// You can change/customise that, by defining your own options with these names.
|
2021-07-09 21:08:01 +02:00
|
|
|
fn (mut fs FlagParser) handle_builtin_options() {
|
|
|
|
mut show_version := false
|
|
|
|
mut show_help := false
|
|
|
|
fs.find_existing_flag('help') or {
|
|
|
|
show_help = fs.bool('help', `h`, false, fs.default_help_label)
|
|
|
|
}
|
|
|
|
fs.find_existing_flag('version') or {
|
|
|
|
show_version = fs.bool('version', 0, false, fs.default_version_label)
|
|
|
|
}
|
|
|
|
if show_help {
|
|
|
|
println(fs.usage())
|
|
|
|
exit(0)
|
|
|
|
}
|
|
|
|
if show_version {
|
|
|
|
println('$fs.application_name $fs.application_version')
|
|
|
|
exit(0)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-03-22 07:21:13 +01:00
|
|
|
// finalize - return all remaining arguments (non options).
|
|
|
|
// Call .finalize() after all arguments are defined.
|
|
|
|
// The remaining arguments are returned in the same order they are
|
|
|
|
// defined on the command line. If additional flags are found, i.e.
|
|
|
|
// (things starting with '--' or '-'), it returns an error.
|
2021-07-09 21:08:01 +02:00
|
|
|
pub fn (mut fs FlagParser) finalize() ?[]string {
|
|
|
|
fs.handle_builtin_options()
|
2021-07-09 10:13:32 +02:00
|
|
|
mut remaining := fs.args.clone()
|
2021-04-28 11:23:23 +02:00
|
|
|
if !fs.allow_unknown_args {
|
2021-07-09 10:13:32 +02:00
|
|
|
for a in remaining {
|
2021-04-28 11:23:23 +02:00
|
|
|
if (a.len >= 2 && a[..2] == '--') || (a.len == 2 && a[0] == `-`) {
|
|
|
|
return IError(&UnkownFlagError{
|
2022-02-11 14:52:33 +01:00
|
|
|
flag: a
|
2021-04-28 11:23:23 +02:00
|
|
|
})
|
|
|
|
}
|
2019-12-16 17:03:38 +01:00
|
|
|
}
|
|
|
|
}
|
2021-07-09 10:13:32 +02:00
|
|
|
if remaining.len < fs.min_free_args && fs.min_free_args > 0 {
|
2022-02-11 14:52:33 +01:00
|
|
|
return IError(&ArgsCountError{
|
|
|
|
want: fs.min_free_args
|
|
|
|
got: remaining.len
|
2021-03-30 14:31:02 +02:00
|
|
|
})
|
2019-12-16 17:03:38 +01:00
|
|
|
}
|
2021-07-09 10:13:32 +02:00
|
|
|
if remaining.len > fs.max_free_args && fs.max_free_args > 0 {
|
2022-02-11 14:52:33 +01:00
|
|
|
return IError(&ArgsCountError{
|
|
|
|
want: fs.max_free_args
|
|
|
|
got: remaining.len
|
2021-03-30 14:31:02 +02:00
|
|
|
})
|
2019-12-16 17:03:38 +01:00
|
|
|
}
|
2021-07-09 10:13:32 +02:00
|
|
|
if remaining.len > 0 && fs.max_free_args == 0 && fs.min_free_args == 0 {
|
2022-02-11 14:52:33 +01:00
|
|
|
return IError(&ArgsCountError{
|
|
|
|
want: 0
|
|
|
|
got: remaining.len
|
2021-03-30 14:31:02 +02:00
|
|
|
})
|
2019-12-16 17:03:38 +01:00
|
|
|
}
|
2021-07-09 10:13:32 +02:00
|
|
|
remaining << fs.all_after_dashdash
|
|
|
|
return remaining
|
2019-07-12 15:47:27 +02:00
|
|
|
}
|
2021-07-11 09:52:16 +02:00
|
|
|
|
|
|
|
// remaining_parameters will return all remaining parameters.
|
|
|
|
// Call .remaining_parameters() *AFTER* you have defined all options
|
|
|
|
// that your program needs. remaining_parameters will also print any
|
|
|
|
// parsing errors and stop the program. Use .finalize() instead, if
|
|
|
|
// you want more control over the error handling.
|
|
|
|
pub fn (mut fs FlagParser) remaining_parameters() []string {
|
|
|
|
return fs.finalize() or {
|
2022-02-11 14:52:33 +01:00
|
|
|
eprintln(err.msg())
|
2021-07-11 09:52:16 +02:00
|
|
|
println(fs.usage())
|
|
|
|
exit(1)
|
|
|
|
}
|
|
|
|
}
|