diff --git a/vlib/cli/command.v b/vlib/cli/command.v index 06963ce003..6eecdd5cd0 100644 --- a/vlib/cli/command.v +++ b/vlib/cli/command.v @@ -15,6 +15,9 @@ pub mut: disable_version bool disable_flags bool + sort_flags bool = true + sort_commands bool = true + parent &Command = nil() commands []Command flags []Flag @@ -49,6 +52,13 @@ pub fn (mut cmd Command) parse(args []string) { } cmd.add_default_commands() + if cmd.sort_flags { + cmd.flags.sort() + } + if cmd.sort_commands { + cmd.commands.sort() + } + cmd.args = args[1..] for i in 0..cmd.commands.len { cmd.commands[i].parent = cmd @@ -88,6 +98,7 @@ fn (mut cmd Command) parse_flags() { mut flag := &cmd.flags[i] if flag.matches(cmd.args) { found = true + flag.found = true cmd.args = flag.parse(cmd.args) or { println('failed to parse flag ${cmd.args[0]}: ${err}') exit(1) @@ -127,8 +138,7 @@ fn (mut cmd Command) parse_commands() { if int(cmd.execute) == 0 { if !cmd.disable_help { help_cmd := cmd.commands.get('help') or { return } // ignore error and handle command normally - execute := help_cmd.execute - execute(help_cmd) + help_cmd.execute(help_cmd) } } else { cmd.check_required_flags() @@ -153,8 +163,7 @@ fn (mut cmd Command) check_help_flag() { help_flag := cmd.flags.get_bool('help') or { return } // ignore error and handle command normally if help_flag { help_cmd := cmd.commands.get('help') or { return } // ignore error and handle command normally - execute := help_cmd.execute - execute(help_cmd) + help_cmd.execute(help_cmd) exit(0) } } @@ -168,8 +177,7 @@ fn (mut cmd Command) check_version_flag() { version_flag := cmd.flags.get_bool('version') or { return } // ignore error and handle command normally if version_flag { version_cmd := cmd.commands.get('version') or { return } // ignore error and handle command normally - execute := version_cmd.execute - execute(version_cmd) + version_cmd.execute(version_cmd) exit(0) } } @@ -185,6 +193,15 @@ fn (mut cmd Command) check_required_flags() { } } +fn (cmds []Command) get(name string) ?Command { + for cmd in cmds { + if cmd.name == name { + return cmd + } + } + return error('command \'${name}\' not found.') +} + fn (cmds []Command) contains(name string) bool { for cmd in cmds { if cmd.name == name { @@ -194,11 +211,8 @@ fn (cmds []Command) contains(name string) bool { return false } -fn (cmds []Command) get(name string) ?Command { - for cmd in cmds { - if cmd.name == name { - return cmd - } - } - return error('command \'${name}\' not found.') +fn (mut cmds []Command) sort() { + cmds.sort_with_compare(fn(a &Command, b &Command) int { + return compare_strings(&a.name, &b.name) + }) } diff --git a/vlib/cli/flag.v b/vlib/cli/flag.v index 5cc0d38757..031ab64801 100644 --- a/vlib/cli/flag.v +++ b/vlib/cli/flag.v @@ -15,14 +15,24 @@ pub mut: description string global bool required bool - value string + +mut: + found bool +} + +pub fn (flags []Flag) get_all_found() []Flag { + return flags.filter(it.found) +} + +pub fn (flag Flag) get_bool() ?bool { + if flag.flag != .bool { return error('invalid flag type') } + return flag.value == 'true' } pub fn (flags []Flag) get_bool(name string) ?bool { flag := flags.get(name) or { return error(err) } - if flag.flag != .bool { return error('invalid flag type') } - return flag.value == 'true' + return flag.get_bool() } pub fn (flags []Flag) get_bool_or(name string, or_value bool) bool { @@ -30,34 +40,46 @@ pub fn (flags []Flag) get_bool_or(name string, or_value bool) bool { return value } -pub fn (flags []Flag) get_int(name string) ?int { - flag := flags.get(name) or { return error(err) } +pub fn (flag Flag) get_int() ?int { if flag.flag != .int { return error('invalid flag type') } return flag.value.int() } +pub fn (flags []Flag) get_int(name string) ?int { + flag := flags.get(name) or { return error(err) } + return flag.get_int() +} + pub fn (flags []Flag) get_int_or(name string, or_value int) int { value := flags.get_int(name) or { return or_value } return value } -pub fn (flags []Flag) get_float(name string) ?f32 { - flag := flags.get(name) or { return error(err) } +pub fn (flag Flag) get_float() ?f64 { if flag.flag != .float { return error('invalid flag type') } - return flag.value.f32() + return flag.value.f64() } -pub fn (flags []Flag) get_float_or(name string, or_value f32) f32 { +pub fn (flags []Flag) get_float(name string) ?f64 { + flag := flags.get(name) or { return error(err) } + return flag.get_float() +} + +pub fn (flags []Flag) get_float_or(name string, or_value f64) f64 { value := flags.get_float(name) or { return or_value } return value } -pub fn (flags []Flag) get_string(name string) ?string { - flag := flags.get(name) or { return error(err) } +pub fn (flag Flag) get_string() ?string { if flag.flag != .string { return error('invalid flag type') } return flag.value } +pub fn (flags []Flag) get_string(name string) ?string { + flag := flags.get(name) or { return error(err) } + return flag.get_string() +} + pub fn (flags []Flag) get_string_or(name string, or_value string) string { value := flags.get_string(name) or { return or_value } return value @@ -81,8 +103,10 @@ fn (mut flag Flag) parse(args []string) ?[]string { // check if first arg matches flag fn (mut flag Flag) matches(args []string) bool { return - (flag.name != '' && args[0].starts_with('--${flag.name}')) || - (flag.abbrev != '' && args[0].starts_with('-${flag.abbrev}')) + (flag.name != '' && args[0] == '--${flag.name}') || + (flag.name != '' && args[0].starts_with('--${flag.name}=')) || + (flag.abbrev != '' && args[0] == '-${flag.abbrev}') || + (flag.abbrev != '' && args[0].starts_with('-${flag.abbrev}=')) } fn (mut flag Flag) parse_raw(args []string) ?[]string { @@ -127,3 +151,9 @@ fn (flags []Flag) contains(name string) bool { } return false } + +fn (mut flags []Flag) sort() { + flags.sort_with_compare(fn(a &Flag, b &Flag) int { + return compare_strings(&a.name, &b.name) + }) +} diff --git a/vlib/cli/flag_test.v b/vlib/cli/flag_test.v index 220d3f4a80..b670ee0891 100644 --- a/vlib/cli/flag_test.v +++ b/vlib/cli/flag_test.v @@ -18,15 +18,27 @@ fn test_if_bool_flag_parses() { flag: .bool, name: 'flag', } + mut value := false flag.parse(['--flag']) or { panic(err) } - assert flag.value == 'true' + value = flag.get_bool() or { panic(err) } + assert value == true flag.parse(['--flag', 'true']) or { panic(err) } - assert flag.value == 'true' + value = flag.get_bool() or { panic(err) } + assert value == true flag.parse(['--flag=true']) or { panic(err) } - assert flag.value == 'true' + value = flag.get_bool() or { panic(err) } + assert value == true + + flag.parse(['--flag', 'false']) or { panic(err) } + value = flag.get_bool() or { panic(err) } + assert value == false + + flag.parse(['--flag=false']) or { panic(err) } + value = flag.get_bool() or { panic(err) } + assert value == false } fn test_if_int_flag_parses() { @@ -34,12 +46,15 @@ fn test_if_int_flag_parses() { flag: .int, name: 'flag', } + mut value := 0 flag.parse(['--flag', '42']) or { panic(err) } - assert flag.value.int() == 42 + value = flag.get_int() or { panic(err) } + assert value == 42 flag.parse(['--flag=42']) or { panic(err) } - assert flag.value.int() == 42 + value = flag.get_int() or { panic(err) } + assert value == 42 } fn test_if_float_flag_parses() { @@ -47,10 +62,14 @@ fn test_if_float_flag_parses() { flag: .float, name: 'flag', } + mut value := f64(0) flag.parse(['--flag', '3.14159']) or { panic(err) } - assert flag.value.f64() == 3.14159 + value = flag.get_float() or { panic(err) } + assert value == 3.14159 flag.parse(['--flag=3.14159']) or { panic(err) } assert flag.value.f64() == 3.14159 + value = flag.get_float() or { panic(err) } + assert value == 3.14159 }