aboutsummaryrefslogtreecommitdiff
path: root/v_windows/v/old/vlib/flag
diff options
context:
space:
mode:
Diffstat (limited to 'v_windows/v/old/vlib/flag')
-rw-r--r--v_windows/v/old/vlib/flag/README.md36
-rw-r--r--v_windows/v/old/vlib/flag/default_flag_options_test.v35
-rw-r--r--v_windows/v/old/vlib/flag/flag.v624
-rw-r--r--v_windows/v/old/vlib/flag/flag_test.v412
-rw-r--r--v_windows/v/old/vlib/flag/testdata/simplest_flag_program.dashdash.help.out1
-rw-r--r--v_windows/v/old/vlib/flag/testdata/simplest_flag_program.dashdash.version.out1
-rw-r--r--v_windows/v/old/vlib/flag/testdata/simplest_flag_program.help.out7
-rw-r--r--v_windows/v/old/vlib/flag/testdata/simplest_flag_program.out1
-rw-r--r--v_windows/v/old/vlib/flag/testdata/simplest_flag_program.v14
-rw-r--r--v_windows/v/old/vlib/flag/testdata/simplest_flag_program.version.out1
-rw-r--r--v_windows/v/old/vlib/flag/testdata/usage_example.help.out13
-rw-r--r--v_windows/v/old/vlib/flag/testdata/usage_example.out1
-rw-r--r--v_windows/v/old/vlib/flag/testdata/usage_example.v17
-rw-r--r--v_windows/v/old/vlib/flag/testdata/usage_example.version.out1
-rw-r--r--v_windows/v/old/vlib/flag/usage_example_test.v35
15 files changed, 1199 insertions, 0 deletions
diff --git a/v_windows/v/old/vlib/flag/README.md b/v_windows/v/old/vlib/flag/README.md
new file mode 100644
index 0000000..1122f77
--- /dev/null
+++ b/v_windows/v/old/vlib/flag/README.md
@@ -0,0 +1,36 @@
+The `flag` module helps command-line flag parsing.
+Main features are:
+- parses flags like `-f` or '--flag' or '--stuff=things' or '--things stuff'.
+- handles bool, int, float and string args.
+- can print usage information listing all the declrared flags.
+- handles unknown arguments as error.
+
+Usage example:
+
+```v
+module main
+
+import os
+import flag
+
+fn main() {
+ mut fp := flag.new_flag_parser(os.args)
+ fp.application('flag_example_tool')
+ fp.version('v0.0.1')
+ fp.limit_free_args(0, 0) // comment this, if you expect arbitrary texts after the options
+ fp.description('This tool is only designed to show how the flag lib is working')
+ fp.skip_executable()
+ an_int := fp.int('an_int', 0, 0o123, 'some int to define 0o123 is its default value')
+ a_bool := fp.bool('a_bool', 0, false, 'some boolean flag. --a_bool will set it to true.')
+ a_float := fp.float('a_float', 0, 1.0, 'some floating point value, by default 1.0 .')
+ a_string := fp.string('a_string', `a`, 'no text', 'finally, some text with ' +
+ ' `-a` as an abbreviation, so you can pass --a_string abc or just -a abc')
+ additional_args := fp.finalize() or {
+ eprintln(err)
+ println(fp.usage())
+ return
+ }
+ println('an_int: $an_int | a_bool: $a_bool | a_float: $a_float | a_string: "$a_string" ')
+ println(additional_args.join_lines())
+}
+```
diff --git a/v_windows/v/old/vlib/flag/default_flag_options_test.v b/v_windows/v/old/vlib/flag/default_flag_options_test.v
new file mode 100644
index 0000000..fb0a423
--- /dev/null
+++ b/v_windows/v/old/vlib/flag/default_flag_options_test.v
@@ -0,0 +1,35 @@
+import os
+
+const source = 'vlib/flag/testdata/simplest_flag_program.v'
+
+const simple_flag_app_executable = os.real_path(os.join_path(os.cache_dir(), 'simple_flag_app.exe'))
+
+fn testsuite_begin() {
+ os.chdir(@VMODROOT)
+ os.rm(simple_flag_app_executable) or {}
+ res := os.execute('${@VEXE} -o $simple_flag_app_executable $source')
+ assert res.exit_code == 0
+ assert os.execute(simple_flag_app_executable).exit_code == 0
+}
+
+fn testsuite_end() {
+ os.rm(simple_flag_app_executable) or {}
+ assert true
+}
+
+fn check_program(opts string, extension string) {
+ result := source.replace('.v', extension)
+ res := os.execute('$simple_flag_app_executable $opts')
+ lines := os.read_lines(result) or { panic(err) }
+ assert res.exit_code == 0
+ assert res.output.split_into_lines() == lines
+}
+
+fn test_default_builtin_flag_options() {
+ check_program('', '.out')
+ check_program(' -- --help', '.dashdash.help.out')
+ check_program(' -- --version', '.dashdash.version.out')
+ check_program(' -h', '.help.out')
+ check_program(' --help', '.help.out')
+ check_program(' --version', '.version.out')
+}
diff --git a/v_windows/v/old/vlib/flag/flag.v b/v_windows/v/old/vlib/flag/flag.v
new file mode 100644
index 0000000..85bb09d
--- /dev/null
+++ b/v_windows/v/old/vlib/flag/flag.v
@@ -0,0 +1,624 @@
+module flag
+
+// data object storing information about a defined flag
+pub struct Flag {
+pub:
+ name string // name as it appears on command line
+ abbr byte // shortcut
+ 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
+}
+
+struct UnkownFlagError {
+ msg string
+ code int
+}
+
+struct MinimumArgsCountError {
+ msg string
+ code int
+}
+
+struct MaximumArgsCountError {
+ msg string
+ code int
+}
+
+struct NoArgsExpectedError {
+ msg string
+ code int
+}
+
+[unsafe]
+fn (mut f Flag) free() {
+ unsafe {
+ f.name.free()
+ f.usage.free()
+ f.val_desc.free()
+ }
+}
+
+pub fn (f Flag) str() string {
+ return '' + ' flag:\n' + ' name: $f.name\n' +
+ ' abbr: `$f.abbr.ascii_str()`\n' + ' usag: $f.usage\n' +
+ ' desc: $f.val_desc'
+}
+
+pub fn (af []Flag) str() string {
+ mut res := []string{}
+ res << '\n []Flag = ['
+ for f in af {
+ res << f.str()
+ }
+ res << ' ]'
+ return res.join('\n')
+}
+
+//
+pub struct FlagParser {
+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
+pub mut:
+ usage_examples []string // when set, --help will print:
+ // Usage: $appname $usage_examples[0]`
+ // or: $appname $usage_examples[1]`
+ // etc
+ default_help_label string = 'display this help and exit'
+ default_version_label string = 'output version information and exit'
+ args []string // the current list of processed args
+ max_free_args int
+ flags []Flag // registered flags
+ application_name string
+ application_version string
+ application_description string
+ min_free_args int
+ args_description string
+ allow_unknown_args bool // whether passing undescribed arguments is allowed
+ footers []string // when set, --help will display all the collected footers at the bottom.
+}
+
+[unsafe]
+fn (mut f FlagParser) free() {
+ unsafe {
+ for a in f.args {
+ a.free()
+ }
+ f.args.free()
+ //
+ for flag in f.flags {
+ flag.free()
+ }
+ f.flags.free()
+ //
+ f.application_name.free()
+ f.application_version.free()
+ f.application_description.free()
+ f.args_description.free()
+ }
+}
+
+pub const (
+ // used for formating usage message
+ space = ' '
+ underline = '-----------------------------------------------'
+ max_args_number = 4048
+)
+
+// create a new flag set for parsing command line arguments
+pub fn new_flag_parser(args []string) &FlagParser {
+ 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..]
+ }
+ }
+ return &FlagParser{
+ original_args: original_args
+ idx_dashdash: idx_dashdash
+ all_after_dashdash: all_after_dashdash
+ args: all_before_dashdash
+ max_free_args: flag.max_args_number
+ }
+}
+
+// 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
+}
+
+// change the application name to be used in 'usage' output
+pub fn (mut fs FlagParser) application(name string) {
+ fs.application_name = name
+}
+
+// change the application version to be used in 'usage' output
+pub fn (mut fs FlagParser) version(vers string) {
+ fs.application_version = vers
+}
+
+// description appends to the application description lines, shown
+// in the help/usage screen
+pub fn (mut fs FlagParser) description(desc string) {
+ if fs.application_description.len == 0 {
+ fs.application_description = desc
+ } else {
+ fs.application_description += '\n$desc'
+ }
+}
+
+// in most cases you do not need the first argv for flag parsing
+pub fn (mut fs FlagParser) skip_executable() {
+ fs.args.delete(0)
+}
+
+// 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
+}
+
+// private helper to register a flag
+fn (mut fs FlagParser) add_flag(name string, abbr byte, usage string, desc string) {
+ fs.flags << Flag{
+ name: name
+ abbr: abbr
+ usage: usage
+ val_desc: desc
+ }
+}
+
+// private: general parsing a single argument
+// - search args for existence
+// if true
+// extract the defined value as string
+// else
+// return an (dummy) error -> argument is not defined
+//
+// - the name, usage are registered
+// - found arguments and corresponding values are removed from args list
+[manualfree]
+fn (mut fs FlagParser) parse_value(longhand string, shorthand byte) []string {
+ full := '--$longhand'
+ defer {
+ unsafe { full.free() }
+ }
+ mut found_entries := []string{}
+ mut to_delete := []int{}
+ defer {
+ unsafe { to_delete.free() }
+ }
+ mut should_skip_one := false
+ for i, arg in fs.args {
+ if should_skip_one {
+ should_skip_one = false
+ continue
+ }
+ if arg.len == 0 || arg[0] != `-` {
+ continue
+ }
+ if (arg.len == 2 && arg[0] == `-` && arg[1] == shorthand) || arg == full {
+ if i + 1 >= fs.args.len {
+ return []
+ }
+ nextarg := fs.args[i + 1]
+ 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() }
+ }
+ found_entries << fs.args[i + 1]
+ to_delete << i
+ to_delete << i + 1
+ should_skip_one = true
+ continue
+ }
+ if arg.len > full.len + 1 && arg[..full.len + 1] == '$full=' {
+ found_entries << arg[full.len + 1..]
+ to_delete << i
+ continue
+ }
+ }
+ for i, del in to_delete {
+ // i entrys are deleted so it's shifted left i times.
+ fs.args.delete(del - i)
+ }
+ return found_entries
+}
+
+// special parsing for bool values
+// see also: parse_value
+//
+// special: it is allowed to define bool flags without value
+// -> '--flag' is parsed as true
+// -> '--flag' is equal to '--flag=true'
+fn (mut fs FlagParser) parse_bool_value(longhand string, shorthand byte) ?string {
+ {
+ 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..]
+ fs.args.delete(i)
+ return val
+ }
+ if arg.len > 1 && arg[0] == `-` && arg[1] != `-` && arg.index_byte(shorthand) != -1 {
+ // -abc is equivalent to -a -b -c
+ return 'true'
+ }
+ }
+ }
+ return error("parameter '$longhand' not found")
+}
+
+// bool_opt returns an optional that returns the value associated with the flag.
+// In the situation that the flag was not provided, it returns null.
+pub fn (mut fs FlagParser) bool_opt(name string, abbr byte, usage string) ?bool {
+ 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
+}
+
+// defining and parsing a bool flag
+// if defined
+// the value is returned (true/false)
+// else
+// the default value is returned
+// version with abbr
+// TODO error handling for invalid string to bool conversion
+pub fn (mut fs FlagParser) bool(name string, abbr byte, bdefault bool, usage string) bool {
+ value := fs.bool_opt(name, abbr, usage) or { return bdefault }
+ return value
+}
+
+// int_multi returns all instances of values associated with the flags provided
+// In the case that none were found, it returns an empty array.
+pub fn (mut fs FlagParser) int_multi(name string, abbr byte, usage string) []int {
+ fs.add_flag(name, abbr, usage, '<multiple ints>')
+ parsed := fs.parse_value(name, abbr)
+ mut value := []int{}
+ for val in parsed {
+ value << val.int()
+ }
+ return value
+}
+
+// int_opt returns an optional that returns the value associated with the flag.
+// In the situation that the flag was not provided, it returns null.
+pub fn (mut fs FlagParser) int_opt(name string, abbr byte, usage string) ?int {
+ 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()
+ }
+ return res
+}
+
+// defining and parsing an int flag
+// if defined
+// the value is returned (int)
+// else
+// the default value is returned
+// version with abbr
+// TODO error handling for invalid string to int conversion
+pub fn (mut fs FlagParser) int(name string, abbr byte, idefault int, usage string) int {
+ value := fs.int_opt(name, abbr, usage) or { return idefault }
+ return value
+}
+
+// float_multi returns all instances of values associated with the flags provided
+// In the case that none were found, it returns an empty array.
+pub fn (mut fs FlagParser) float_multi(name string, abbr byte, usage string) []f64 {
+ fs.add_flag(name, abbr, usage, '<multiple floats>')
+ parsed := fs.parse_value(name, abbr)
+ mut value := []f64{}
+ for val in parsed {
+ value << val.f64()
+ }
+ return value
+}
+
+// float_opt returns an optional that returns the value associated with the flag.
+// In the situation that the flag was not provided, it returns null.
+pub fn (mut fs FlagParser) float_opt(name string, abbr byte, usage string) ?f64 {
+ 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()
+ }
+ return res
+}
+
+// defining and parsing a float flag
+// if defined
+// the value is returned (float)
+// else
+// the default value is returned
+// version with abbr
+// TODO error handling for invalid string to float conversion
+pub fn (mut fs FlagParser) float(name string, abbr byte, fdefault f64, usage string) f64 {
+ value := fs.float_opt(name, abbr, usage) or { return fdefault }
+ return value
+}
+
+// string_multi returns all instances of values associated with the flags provided
+// In the case that none were found, it returns an empty array.
+pub fn (mut fs FlagParser) string_multi(name string, abbr byte, usage string) []string {
+ fs.add_flag(name, abbr, usage, '<multiple strings>')
+ return fs.parse_value(name, abbr)
+}
+
+// string_opt returns an optional that returns the value associated with the flag.
+// In the situation that the flag was not provided, it returns null.
+pub fn (mut fs FlagParser) string_opt(name string, abbr byte, usage string) ?string {
+ 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]
+ }
+ return res
+}
+
+// defining and parsing a string flag
+// if defined
+// the value is returned (string)
+// else
+// the default value is returned
+// version with abbr
+pub fn (mut fs FlagParser) string(name string, abbr byte, sdefault string, usage string) string {
+ value := fs.string_opt(name, abbr, usage) or { return sdefault }
+ return value
+}
+
+pub fn (mut fs FlagParser) limit_free_args_to_at_least(n int) {
+ if n > flag.max_args_number {
+ panic('flag.limit_free_args_to_at_least expect n to be smaller than $flag.max_args_number')
+ }
+ if n <= 0 {
+ panic('flag.limit_free_args_to_at_least expect n to be a positive number')
+ }
+ fs.min_free_args = n
+}
+
+pub fn (mut fs FlagParser) limit_free_args_to_exactly(n int) {
+ if n > flag.max_args_number {
+ panic('flag.limit_free_args_to_exactly expect n to be smaller than $flag.max_args_number')
+ }
+ if n < 0 {
+ panic('flag.limit_free_args_to_exactly expect n to be a non negative number')
+ }
+ fs.min_free_args = n
+ fs.max_free_args = n
+}
+
+// this will cause an error in finalize() if free args are out of range
+// (min, ..., max)
+pub fn (mut fs FlagParser) limit_free_args(min int, max int) {
+ if min > max {
+ panic('flag.limit_free_args expect min < max, got $min >= $max')
+ }
+ fs.min_free_args = min
+ fs.max_free_args = max
+}
+
+pub fn (mut fs FlagParser) arguments_description(description string) {
+ fs.args_description = description
+}
+
+// collect all given information and
+pub fn (fs FlagParser) usage() string {
+ positive_min_arg := (fs.min_free_args > 0)
+ positive_max_arg := (fs.max_free_args > 0 && fs.max_free_args != flag.max_args_number)
+ no_arguments := (fs.min_free_args == 0 && fs.max_free_args == 0)
+ mut adesc := if fs.args_description.len > 0 { fs.args_description } else { '[ARGS]' }
+ if no_arguments {
+ adesc = ''
+ }
+ mut use := []string{}
+ if fs.application_version != '' {
+ use << '$fs.application_name $fs.application_version'
+ use << '$flag.underline'
+ }
+ 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'
+ }
+ }
+ }
+ use << ''
+ if fs.application_description != '' {
+ use << 'Description: $fs.application_description'
+ use << ''
+ }
+ // show a message about the [ARGS]:
+ if positive_min_arg || positive_max_arg || no_arguments {
+ if no_arguments {
+ use << 'This application does not expect any arguments'
+ use << ''
+ } 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 ')
+ use << 'The arguments should be $sargs in number.'
+ use << ''
+ }
+ }
+ if fs.flags.len > 0 {
+ use << 'Options:'
+ for f in fs.flags {
+ mut onames := []string{}
+ if f.abbr != 0 {
+ onames << '-$f.abbr.ascii_str()'
+ }
+ if f.name != '' {
+ if !f.val_desc.contains('<bool>') {
+ onames << '--$f.name $f.val_desc'
+ } else {
+ onames << '--$f.name'
+ }
+ }
+ option_names := ' ' + onames.join(', ')
+ mut xspace := ''
+ if option_names.len > flag.space.len - 2 {
+ xspace = '\n$flag.space'
+ } else {
+ xspace = flag.space[option_names.len..]
+ }
+ fdesc := '$option_names$xspace$f.usage'
+ use << fdesc
+ }
+ }
+ for footer in fs.footers {
+ use << footer
+ }
+ return use.join('\n').replace('- ,', ' ')
+}
+
+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')
+}
+
+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)
+ }
+}
+
+// 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.
+pub fn (mut fs FlagParser) finalize() ?[]string {
+ fs.handle_builtin_options()
+ mut remaining := fs.args.clone()
+ if !fs.allow_unknown_args {
+ for a in remaining {
+ if (a.len >= 2 && a[..2] == '--') || (a.len == 2 && a[0] == `-`) {
+ return IError(&UnkownFlagError{
+ msg: 'Unknown flag `$a`'
+ })
+ }
+ }
+ }
+ if remaining.len < fs.min_free_args && fs.min_free_args > 0 {
+ return IError(&MinimumArgsCountError{
+ msg: 'Expected at least $fs.min_free_args arguments, but given $remaining.len'
+ })
+ }
+ if remaining.len > fs.max_free_args && fs.max_free_args > 0 {
+ return IError(&MaximumArgsCountError{
+ msg: 'Expected at most $fs.max_free_args arguments, but given $remaining.len'
+ })
+ }
+ if remaining.len > 0 && fs.max_free_args == 0 && fs.min_free_args == 0 {
+ return IError(&NoArgsExpectedError{
+ msg: 'Expected no arguments, but given $remaining.len'
+ })
+ }
+ remaining << fs.all_after_dashdash
+ return remaining
+}
+
+// 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 {
+ eprintln(err.msg)
+ println(fs.usage())
+ exit(1)
+ }
+}
diff --git a/v_windows/v/old/vlib/flag/flag_test.v b/v_windows/v/old/vlib/flag/flag_test.v
new file mode 100644
index 0000000..8326193
--- /dev/null
+++ b/v_windows/v/old/vlib/flag/flag_test.v
@@ -0,0 +1,412 @@
+import flag
+
+fn test_if_flag_not_given_return_default_values() {
+ mut fp := flag.new_flag_parser([])
+ assert false == fp.bool('a_bool', 0, false, '')
+ assert 42 == fp.int('an_int', 0, 42, '')
+ assert 1.0 == fp.float('a_float', 0, 1.0, '')
+ assert 'stuff' == fp.string('a_string', 0, 'stuff', '')
+}
+
+fn test_could_define_application_name_and_version() {
+ mut fp := flag.new_flag_parser([])
+ fp.application('test app')
+ fp.version('0.0.42')
+ fp.description('some text')
+ assert fp.application_name == 'test app'
+ assert fp.application_version == '0.0.42'
+ assert fp.application_description == 'some text'
+}
+
+fn test_bool_flags_do_not_need_an_value() {
+ mut fp := flag.new_flag_parser(['--a_bool'])
+ assert true == fp.bool('a_bool', 0, false, '')
+}
+
+fn test_flags_could_be_defined_with_eq() {
+ mut fp := flag.new_flag_parser([
+ '--an_int=42',
+ '--a_float=2.0',
+ '--bool_without',
+ '--a_string=stuff',
+ '--a_bool=true',
+ ])
+ assert 42 == fp.int('an_int', 0, 0o666, '')
+ assert true == fp.bool('a_bool', 0, false, '')
+ assert true == fp.bool('bool_without', 0, false, '')
+ assert 2.0 == fp.float('a_float', 0, 1.0, '')
+ assert 'stuff' == fp.string('a_string', 0, 'not_stuff', '')
+}
+
+fn test_values_could_be_defined_without_eq() {
+ mut fp := flag.new_flag_parser([
+ '--an_int',
+ '42',
+ '--a_float',
+ '2.0',
+ '--bool_without',
+ '--a_string',
+ 'stuff',
+ '--a_bool',
+ 'true',
+ ])
+ assert 42 == fp.int('an_int', 0, 0o666, '')
+ assert true == fp.bool('a_bool', 0, false, '')
+ assert true == fp.bool('bool_without', 0, false, '')
+ assert 2.0 == fp.float('a_float', 0, 1.0, '')
+ assert 'stuff' == fp.string('a_string', 0, 'not_stuff', '')
+}
+
+fn test_values_could_be_defined_mixed() {
+ mut fp := flag.new_flag_parser([
+ '--an_int',
+ '42',
+ '--a_float=2.0',
+ '--bool_without',
+ '--a_string',
+ 'stuff',
+ '--a_bool=true',
+ ])
+ assert 42 == fp.int('an_int', 0, 0o666, '')
+ assert true == fp.bool('a_bool', 0, false, '')
+ assert true == fp.bool('bool_without', 0, false, '')
+ assert 2.0 == fp.float('a_float', 0, 1.0, '')
+ assert 'stuff' == fp.string('a_string', 0, 'not_stuff', '')
+}
+
+fn test_beaware_for_argument_names_with_same_prefix() {
+ mut fp := flag.new_flag_parser([
+ '--short',
+ '5',
+ '--shorter=7',
+ ])
+ assert 5 == fp.int('short', 0, 0o666, '')
+ assert 7 == fp.int('shorter', 0, 0o666, '')
+}
+
+fn test_beaware_for_argument_names_with_same_prefix_inverse() {
+ mut fp := flag.new_flag_parser([
+ '--shorter=7',
+ '--short',
+ '5',
+ ])
+ assert 5 == fp.int('short', 0, 0o666, '')
+ assert 7 == fp.int('shorter', 0, 0o666, '')
+}
+
+fn test_allow_to_skip_executable_path() {
+ mut fp := flag.new_flag_parser(['./path/to/execuable'])
+ fp.skip_executable()
+ args := fp.finalize() or {
+ assert false
+ return
+ }
+ assert !args.contains('./path/to/execuable')
+}
+
+fn test_none_flag_arguments_are_allowed() {
+ mut fp := flag.new_flag_parser([
+ 'file1',
+ '--an_int=2',
+ 'file2',
+ 'file3',
+ '--bool_without',
+ 'file4',
+ '--outfile',
+ 'outfile',
+ ])
+ assert 2 == fp.int('an_int', 0, 0o666, '')
+ assert 'outfile' == fp.string('outfile', 0, 'bad', '')
+ assert true == fp.bool('bool_without', 0, false, '')
+}
+
+fn test_finalize_returns_none_flag_arguments_ordered() {
+ mut fp := flag.new_flag_parser(['d', 'b', 'x', 'a', '--outfile', 'outfile'])
+ fp.string('outfile', 0, 'bad', '')
+ finalized := fp.finalize() or {
+ assert false
+ return
+ }
+ expected := ['d', 'b', 'x', 'a']
+ for i, v in finalized {
+ assert v == expected[i]
+ }
+}
+
+fn test_finalize_returns_error_for_unknown_flags_long() {
+ mut fp := flag.new_flag_parser(['--known', '--unknown'])
+ fp.bool('known', 0, false, '')
+ finalized := fp.finalize() or {
+ assert err.msg == 'Unknown flag `--unknown`'
+ return
+ }
+ assert finalized.len < 0 // expect error to be returned
+}
+
+fn test_finalize_returns_error_for_unknown_flags_short() {
+ mut fp := flag.new_flag_parser(['--known', '-x'])
+ fp.bool('known', 0, false, '')
+ finalized := fp.finalize() or {
+ assert err.msg == 'Unknown flag `-x`'
+ return
+ }
+ assert finalized.len < 0 // expect error to be returned
+}
+
+fn test_allow_to_build_usage_message() {
+ mut fp := flag.new_flag_parser([])
+ fp.limit_free_args(1, 4)
+ fp.application('flag_tool')
+ fp.version('v0.0.0')
+ fp.description('some short information about this tool')
+ fp.int('an_int', 0, 0o666, 'some int to define')
+ fp.bool('a_bool', 0, false, 'some bool to define')
+ fp.bool('bool_without_but_really_big', 0, false, 'this should appear on the next line')
+ fp.float('a_float', 0, 1.0, 'some float as well')
+ fp.string('a_string', 0, 'not_stuff', 'your credit card number')
+ usage := fp.usage()
+ mut all_strings_found := true
+ for s in ['flag_tool', 'v0.0.0', 'an_int <int>', 'a_bool', 'bool_without', 'a_float <float>',
+ 'a_string <string>', 'some int to define', 'some bool to define',
+ 'this should appear on the next line', 'some float as well', 'your credit card number',
+ 'The arguments should be at least 1 and at most 4 in number.', 'Usage', 'Options:',
+ 'Description:', 'some short information about this tool'] {
+ if !usage.contains(s) {
+ eprintln(" missing '$s' in usage message")
+ all_strings_found = false
+ }
+ }
+ assert all_strings_found
+}
+
+fn test_if_no_description_given_usage_message_does_not_contain_descpription() {
+ mut fp := flag.new_flag_parser([])
+ fp.application('flag_tool')
+ fp.version('v0.0.0')
+ fp.bool('a_bool', 0, false, '')
+ assert !fp.usage().contains('Description:')
+}
+
+fn test_if_no_options_given_usage_message_does_not_contain_options() {
+ mut fp := flag.new_flag_parser([])
+ fp.application('flag_tool')
+ fp.version('v0.0.0')
+ assert !fp.usage().contains('Options:')
+}
+
+fn test_free_args_could_be_limited() {
+ mut fp1 := flag.new_flag_parser(['a', 'b', 'c'])
+ fp1.limit_free_args(1, 4)
+ args := fp1.finalize() or {
+ assert false
+ return
+ }
+ assert args[0] == 'a'
+ assert args[1] == 'b'
+ assert args[2] == 'c'
+}
+
+fn test_error_for_to_few_free_args() {
+ mut fp1 := flag.new_flag_parser(['a', 'b', 'c'])
+ fp1.limit_free_args(5, 6)
+ args := fp1.finalize() or {
+ assert err.msg.starts_with('Expected at least 5 arguments')
+ return
+ }
+ assert args.len < 0 // expect an error and need to use args
+}
+
+fn test_error_for_to_much_free_args() {
+ mut fp1 := flag.new_flag_parser(['a', 'b', 'c'])
+ fp1.limit_free_args(1, 2)
+ args := fp1.finalize() or {
+ assert err.msg.starts_with('Expected at most 2 arguments')
+ return
+ }
+ assert args.len < 0 // expect an error and need to use args
+}
+
+fn test_could_expect_no_free_args() {
+ mut fp1 := flag.new_flag_parser(['a'])
+ fp1.limit_free_args(0, 0)
+ args := fp1.finalize() or {
+ assert err.msg.starts_with('Expected no arguments')
+ return
+ }
+ assert args.len < 0 // expect an error and need to use args
+}
+
+fn test_allow_abreviations() {
+ mut fp := flag.new_flag_parser(['-v', '-o', 'some_file', '-i', '42', '-f', '2.0'])
+ v := fp.bool('version', `v`, false, '')
+ o := fp.string('output', `o`, 'empty', '')
+ i := fp.int('count', `i`, 0, '')
+ f := fp.float('value', `f`, 0.0, '')
+ assert v == true
+ assert o == 'some_file'
+ assert i == 42
+ assert f == 2.0
+ u := fp.usage()
+ assert u.contains(' -v')
+ assert u.contains(' -o')
+ assert u.contains(' -i')
+ assert u.contains(' -f')
+ assert u.contains(' -o, --output <string>')
+ assert u.contains(' -i, --count <int>')
+ assert u.contains(' -f, --value <float>')
+}
+
+fn test_allow_kebab_options() {
+ default_value := 'this_is_the_default_value_of_long_option'
+ long_option_value := 'this_is_a_long_option_value_as_argument'
+ mut fp := flag.new_flag_parser(['--my-long-flag', 'true', '--my-long-option', long_option_value])
+ my_flag := fp.bool('my-long-flag', 0, false, 'flag with long-kebab-name')
+ my_option := fp.string('my-long-option', 0, default_value, 'string with long-kebab-name')
+ assert my_flag == true
+ assert my_option == long_option_value
+ u := fp.usage()
+ assert u.contains(' --my-long-flag')
+ assert u.contains(' --my-long-option')
+}
+
+fn test_not_provided_option_is_not_returned() {
+ mut fp := flag.new_flag_parser([])
+ fp.bool_opt('some-flag', `a`, '') or {
+ fp.int_opt('some-flag', `a`, '') or {
+ fp.float_opt('some-flag', `a`, '') or {
+ fp.string_opt('some-flag', `a`, '') or {
+ // Everything should not return
+ return
+ }
+ return
+ }
+ return
+ }
+ return
+ }
+ // If we reach here, one of them returned a value.
+ assert false
+}
+
+fn test_provided_option_is_returned() {
+ mut fp := flag.new_flag_parser(['-a', '-b', '3', '-c', 'hello', '-d', '3.14'])
+ a := fp.bool_opt('some-flag', `a`, '') or { panic('bool_opt did not return a bool') }
+ b := fp.int_opt('some-flag', `b`, '') or { panic('int_opt did not return an int') }
+ c := fp.string_opt('some-flag', `c`, '') or { panic('string_opt did not return a string') }
+ d := fp.float_opt('some-flag', `d`, '') or { panic('float_opt did not return a float') }
+ assert true == a
+ assert b == 3
+ assert c == 'hello'
+ assert d == 3.14
+}
+
+fn test_multiple_arguments() {
+ mut fp := flag.new_flag_parser([
+ '-a',
+ '2',
+ '-a',
+ '3',
+ '-a',
+ '5',
+ '-b',
+ 'a',
+ '-b',
+ 'c',
+ '-b',
+ 'b',
+ '-c',
+ '1.23',
+ '-c',
+ '2.34',
+ '-c',
+ '3.45',
+ ])
+ // TODO Move to array comparison once it's implemented
+ // assert fp.int_multi('some-flag', `a`, '') == [2, 3, 5] &&
+ // fp.string_multi('some-flag', `b`, '') == ['a', 'c', 'b'] &&
+ // fp.float_multi('some-flag', `c`, '') == [1.23, 2.34, 3.45]
+ a := fp.int_multi('some-flag', `a`, '')
+ b := fp.string_multi('some-flag', `b`, '')
+ c := fp.float_multi('some-flag', `c`, '')
+ assert a.len == 3
+ assert b.len == 3
+ assert c.len == 3
+ assert a[0] == 2
+ assert a[1] == 3
+ assert a[2] == 5
+ assert b[0] == 'a'
+ assert b[1] == 'c'
+ assert b[2] == 'b'
+ assert c[0] == 1.23
+ assert c[1] == 2.34
+ assert c[2] == 3.45
+}
+
+fn test_long_options_that_start_with_the_same_letter_as_another_short_option() {
+ mut fp := flag.new_flag_parser([
+ '--vabc',
+ '/abc',
+ ])
+ verbose := fp.bool('verbose', `v`, false, 'Be more verbose.')
+ vabc := fp.string('vabc', `x`, 'default', 'Another option that *may* conflict with v, but *should not*')
+ assert verbose == false
+ assert vabc == '/abc'
+}
+
+fn test_long_options_that_start_with_the_same_letter_as_another_short_option_both_set() {
+ mut fp := flag.new_flag_parser([
+ '-v',
+ '--vabc',
+ '/abc',
+ ])
+ verbose := fp.bool('verbose', `v`, false, 'Be more verbose.')
+ vabc := fp.string('vabc', `x`, 'default', 'Another option that *may* conflict with v, but *should not*')
+ assert verbose == true
+ assert vabc == '/abc'
+}
+
+fn test_single_dash() {
+ mut fp := flag.new_flag_parser([
+ '-',
+ ])
+ flag_update := fp.bool('update', `u`, false, 'Update tools')
+ assert flag_update == false
+}
+
+fn test_optional_flags() {
+ mut fp := flag.new_flag_parser(['-a', '10', '-b'])
+ fp.int_opt('some-flag', `a`, '') or {
+ assert false
+ return
+ }
+ b := fp.string_opt('another-flag', `b`, '') or { 'some_default_value' }
+ assert b == 'some_default_value'
+}
+
+fn test_dashdash_acts_as_parser_full_stop() ? {
+ mut fp := flag.new_flag_parser(['-b', '5', '--', '-d', '-x', '-b', '4', '-a', '-c', 'hello',
+ 'some', 'other', 'parameters'])
+ a := fp.bool_opt('a-bool-flag', `a`, '') or { false }
+ b := fp.int_opt('an-int-flag', `b`, '') or { -1 }
+ c := fp.string_opt('a-string-flag', `c`, '') or { 'default' }
+ assert a == false
+ assert b == 5
+ assert c == 'default'
+ args := fp.finalize() ?
+ assert args.len > 0
+ assert args[0] != '--'
+ assert args == ['-d', '-x', '-b', '4', '-a', '-c', 'hello', 'some', 'other', 'parameters']
+}
+
+fn test_dashdash_acts_as_parser_full_stop_dashdash_at_end() ? {
+ mut fp := flag.new_flag_parser(['-b', '5', '-b', '4', 'other', 'params', '--'])
+ b := fp.int_multi('an-int-flag', `b`, '')
+ assert b == [5, 4]
+ args := fp.finalize() ?
+ assert args.len > 0
+}
+
+fn test_empty_string_with_flag() {
+ mut fp := flag.new_flag_parser([''])
+ s := fp.string('something', `s`, 'default', 'Hey parse me')
+}
diff --git a/v_windows/v/old/vlib/flag/testdata/simplest_flag_program.dashdash.help.out b/v_windows/v/old/vlib/flag/testdata/simplest_flag_program.dashdash.help.out
new file mode 100644
index 0000000..2529e9f
--- /dev/null
+++ b/v_windows/v/old/vlib/flag/testdata/simplest_flag_program.dashdash.help.out
@@ -0,0 +1 @@
+[vlib/flag/testdata/simplest_flag_program.v:13] rest_of_args: ['--help']
diff --git a/v_windows/v/old/vlib/flag/testdata/simplest_flag_program.dashdash.version.out b/v_windows/v/old/vlib/flag/testdata/simplest_flag_program.dashdash.version.out
new file mode 100644
index 0000000..b0b4d65
--- /dev/null
+++ b/v_windows/v/old/vlib/flag/testdata/simplest_flag_program.dashdash.version.out
@@ -0,0 +1 @@
+[vlib/flag/testdata/simplest_flag_program.v:13] rest_of_args: ['--version']
diff --git a/v_windows/v/old/vlib/flag/testdata/simplest_flag_program.help.out b/v_windows/v/old/vlib/flag/testdata/simplest_flag_program.help.out
new file mode 100644
index 0000000..9b5ab9e
--- /dev/null
+++ b/v_windows/v/old/vlib/flag/testdata/simplest_flag_program.help.out
@@ -0,0 +1,7 @@
+abc 0.0.1
+-----------------------------------------------
+Usage: abc [options] [ARGS]
+
+Options:
+ -h, --help display this help and exit
+ --version output version information and exit
diff --git a/v_windows/v/old/vlib/flag/testdata/simplest_flag_program.out b/v_windows/v/old/vlib/flag/testdata/simplest_flag_program.out
new file mode 100644
index 0000000..02b7cea
--- /dev/null
+++ b/v_windows/v/old/vlib/flag/testdata/simplest_flag_program.out
@@ -0,0 +1 @@
+[vlib/flag/testdata/simplest_flag_program.v:13] rest_of_args: []
diff --git a/v_windows/v/old/vlib/flag/testdata/simplest_flag_program.v b/v_windows/v/old/vlib/flag/testdata/simplest_flag_program.v
new file mode 100644
index 0000000..cee2ff7
--- /dev/null
+++ b/v_windows/v/old/vlib/flag/testdata/simplest_flag_program.v
@@ -0,0 +1,14 @@
+import os
+import flag
+
+fn main() {
+ mut fp := flag.new_flag_parser(os.args)
+ fp.application('abc')
+ fp.version('0.0.1')
+ fp.skip_executable()
+ rest_of_args := fp.finalize() or {
+ eprintln(err)
+ exit(1)
+ }
+ dump(rest_of_args)
+}
diff --git a/v_windows/v/old/vlib/flag/testdata/simplest_flag_program.version.out b/v_windows/v/old/vlib/flag/testdata/simplest_flag_program.version.out
new file mode 100644
index 0000000..de52cb0
--- /dev/null
+++ b/v_windows/v/old/vlib/flag/testdata/simplest_flag_program.version.out
@@ -0,0 +1 @@
+abc 0.0.1
diff --git a/v_windows/v/old/vlib/flag/testdata/usage_example.help.out b/v_windows/v/old/vlib/flag/testdata/usage_example.help.out
new file mode 100644
index 0000000..b40047b
--- /dev/null
+++ b/v_windows/v/old/vlib/flag/testdata/usage_example.help.out
@@ -0,0 +1,13 @@
+xyz 0.0.2
+-----------------------------------------------
+Usage: xyz [NUMBER]...
+ or: xyz OPTION
+
+Description: description line 1
+description line 2
+
+Options:
+ -h, --help display this help and exit
+ --version output version information and exit
+footer 1
+footer 2
diff --git a/v_windows/v/old/vlib/flag/testdata/usage_example.out b/v_windows/v/old/vlib/flag/testdata/usage_example.out
new file mode 100644
index 0000000..b2fd000
--- /dev/null
+++ b/v_windows/v/old/vlib/flag/testdata/usage_example.out
@@ -0,0 +1 @@
+[vlib/flag/testdata/usage_example.v:16] rest_of_args: ['abc', 'def']
diff --git a/v_windows/v/old/vlib/flag/testdata/usage_example.v b/v_windows/v/old/vlib/flag/testdata/usage_example.v
new file mode 100644
index 0000000..522b758
--- /dev/null
+++ b/v_windows/v/old/vlib/flag/testdata/usage_example.v
@@ -0,0 +1,17 @@
+import os
+import flag
+
+fn main() {
+ mut fp := flag.new_flag_parser(os.args)
+ fp.skip_executable()
+ fp.application('xyz')
+ fp.version('0.0.2')
+ fp.usage_example('[NUMBER]...')
+ fp.usage_example('OPTION')
+ fp.description('description line 1')
+ fp.description('description line 2')
+ fp.footer('footer 1')
+ fp.footer('footer 2')
+ rest_of_args := fp.remaining_parameters()
+ dump(rest_of_args)
+}
diff --git a/v_windows/v/old/vlib/flag/testdata/usage_example.version.out b/v_windows/v/old/vlib/flag/testdata/usage_example.version.out
new file mode 100644
index 0000000..9196894
--- /dev/null
+++ b/v_windows/v/old/vlib/flag/testdata/usage_example.version.out
@@ -0,0 +1 @@
+xyz 0.0.2
diff --git a/v_windows/v/old/vlib/flag/usage_example_test.v b/v_windows/v/old/vlib/flag/usage_example_test.v
new file mode 100644
index 0000000..30fbccc
--- /dev/null
+++ b/v_windows/v/old/vlib/flag/usage_example_test.v
@@ -0,0 +1,35 @@
+import os
+
+const the_source = 'vlib/flag/testdata/usage_example.v'
+
+const the_executable = os.real_path(os.join_path(os.cache_dir(), 'flag_usage_example_app.exe'))
+
+fn testsuite_begin() {
+ os.chdir(@VMODROOT)
+ os.rm(the_executable) or {}
+ res := os.execute('${@VEXE} -o $the_executable $the_source')
+ assert res.exit_code == 0
+ assert os.execute(the_executable).exit_code == 0
+ C.atexit(fn () {
+ os.rm(the_executable) or {}
+ })
+}
+
+fn normalise_lines(lines []string) string {
+ return '\n' + lines.join('\n')
+}
+
+fn check_program(opts string, extension string) {
+ result := the_source.replace('.v', extension)
+ res := os.execute('$the_executable $opts')
+ assert res.exit_code == 0
+ assert normalise_lines(res.output.split_into_lines()) == normalise_lines(os.read_lines(result) or {
+ panic(err)
+ })
+}
+
+fn test_normal_usage() {
+ check_program('abc def', '.out')
+ check_program(' --help', '.help.out')
+ check_program(' --version', '.version.out')
+}