cli: extract improvements to vlib/cli, based on PR 5616 (without cmd/v2)
parent
7ab6899538
commit
3a4f2dfe8b
|
@ -1,48 +1,105 @@
|
|||
module cli
|
||||
|
||||
fn nil() voidptr { return 0 }
|
||||
type FnCommandCallback fn (cmd Command)?
|
||||
|
||||
pub fn (f FnCommandCallback) str() string {
|
||||
return 'FnCommandCallback=>' + ptr_str(f)
|
||||
}
|
||||
|
||||
pub struct Command {
|
||||
pub mut:
|
||||
name string
|
||||
usage string
|
||||
description string
|
||||
version string
|
||||
pre_execute fn(cmd Command)
|
||||
execute fn(cmd Command)
|
||||
post_execute fn(cmd Command)
|
||||
|
||||
pre_execute FnCommandCallback
|
||||
execute FnCommandCallback
|
||||
post_execute FnCommandCallback
|
||||
disable_help bool
|
||||
disable_version bool
|
||||
disable_flags bool
|
||||
|
||||
sort_flags bool = true
|
||||
sort_commands bool = true
|
||||
|
||||
parent &Command = nil()
|
||||
parent &Command = 0
|
||||
commands []Command
|
||||
flags []Flag
|
||||
args []string
|
||||
}
|
||||
|
||||
pub fn (cmd Command) full_name() string {
|
||||
if isnil(cmd.parent) {
|
||||
return cmd.name
|
||||
pub fn (cmd Command) str() string {
|
||||
mut res := []string{}
|
||||
res << 'Command{'
|
||||
res << ' name: "$cmd.name"'
|
||||
res << ' usage: "$cmd.usage"'
|
||||
res << ' version: "$cmd.version"'
|
||||
res << ' description: "$cmd.description"'
|
||||
res << ' disable_help: $cmd.disable_help'
|
||||
res << ' disable_flags: $cmd.disable_flags'
|
||||
res << ' disable_version: $cmd.disable_version'
|
||||
res << ' sort_flags: $cmd.sort_flags'
|
||||
res << ' sort_commands: $cmd.sort_commands'
|
||||
res << ' cb execute: $cmd.execute'
|
||||
res << ' cb pre_execute: $cmd.pre_execute'
|
||||
res << ' cb post_execute: $cmd.post_execute'
|
||||
if cmd.parent == 0 {
|
||||
res << ' parent: &Command(0)'
|
||||
} else {
|
||||
res << ' parent: &Command{$cmd.parent.name ...}'
|
||||
}
|
||||
return cmd.parent.full_name() + ' ${cmd.name}'
|
||||
res << ' commands: $cmd.commands'
|
||||
res << ' flags: $cmd.flags'
|
||||
res << ' args: $cmd.args'
|
||||
res << '}'
|
||||
return res.join('\n')
|
||||
}
|
||||
|
||||
pub fn (cmd Command) is_root() bool {
|
||||
return isnil(cmd.parent)
|
||||
}
|
||||
|
||||
pub fn (cmd Command) root() Command {
|
||||
if isnil(cmd.parent) {
|
||||
if cmd.is_root() {
|
||||
return cmd
|
||||
}
|
||||
return cmd.parent.root()
|
||||
}
|
||||
|
||||
pub fn (cmd Command) full_name() string {
|
||||
if cmd.is_root() {
|
||||
return cmd.name
|
||||
}
|
||||
return cmd.parent.full_name() + ' $cmd.name'
|
||||
}
|
||||
|
||||
pub fn (mut cmd Command) add_commands(commands []Command) {
|
||||
for command in commands {
|
||||
cmd.add_command(command)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn (mut cmd Command) add_command(command Command) {
|
||||
for existing_cmd in cmd.commands {
|
||||
if existing_cmd.name == command.name {
|
||||
println("command with the same name \'$command.name\' already exists")
|
||||
exit(1)
|
||||
}
|
||||
}
|
||||
cmd.commands << command
|
||||
}
|
||||
|
||||
pub fn (mut cmd Command) add_flags(flags []Flag) {
|
||||
for flag in flags {
|
||||
cmd.add_flag(flag)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn (mut cmd Command) add_flag(flag Flag) {
|
||||
for existing_flag in cmd.flags {
|
||||
if existing_flag.name == flag.name {
|
||||
println("flag with the same name \'$flag.name\' already exists")
|
||||
exit(1)
|
||||
}
|
||||
}
|
||||
cmd.flags << flag
|
||||
}
|
||||
|
||||
|
@ -51,19 +108,16 @@ pub fn (mut cmd Command) parse(args []string) {
|
|||
cmd.add_default_flags()
|
||||
}
|
||||
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
|
||||
}
|
||||
|
||||
if !cmd.disable_flags {
|
||||
cmd.parse_flags()
|
||||
}
|
||||
|
@ -72,18 +126,18 @@ pub fn (mut cmd Command) parse(args []string) {
|
|||
|
||||
fn (mut cmd Command) add_default_flags() {
|
||||
if !cmd.disable_help && !cmd.flags.contains('help') {
|
||||
cmd.add_flag(help_flag(!cmd.flags.contains('h')))
|
||||
cmd.add_flag(help_flag(!cmd.flags.contains('h') && cmd.has_abbrev_flags()))
|
||||
}
|
||||
if !cmd.disable_version && cmd.version != '' && !cmd.flags.contains('version') {
|
||||
cmd.add_flag(version_flag(!cmd.flags.contains('v')))
|
||||
if cmd.version != '' && !cmd.flags.contains('version') {
|
||||
cmd.add_flag(version_flag(!cmd.flags.contains('v') && cmd.has_abbrev_flags()))
|
||||
}
|
||||
}
|
||||
|
||||
fn (mut cmd Command) add_default_commands() {
|
||||
if !cmd.disable_help && !cmd.commands.contains('help') {
|
||||
if !cmd.disable_help && !cmd.commands.contains('help') && cmd.is_root() {
|
||||
cmd.add_command(help_cmd())
|
||||
}
|
||||
if !cmd.disable_version && cmd.version != '' && !cmd.commands.contains('version') {
|
||||
if cmd.version != '' && !cmd.commands.contains('version') {
|
||||
cmd.add_command(version_cmd())
|
||||
}
|
||||
}
|
||||
|
@ -96,17 +150,16 @@ fn (mut cmd Command) parse_flags() {
|
|||
mut found := false
|
||||
for i in 0 .. cmd.flags.len {
|
||||
mut flag := &cmd.flags[i]
|
||||
if flag.matches(cmd.args) {
|
||||
if flag.matches(cmd.args, cmd.has_abbrev_flags()) {
|
||||
found = true
|
||||
flag.found = true
|
||||
cmd.args = flag.parse(cmd.args) or {
|
||||
println('failed to parse flag ${cmd.args[0]}: ${err}')
|
||||
cmd.args = flag.parse(cmd.args, cmd.has_abbrev_flags()) or {
|
||||
println('failed to parse flag ${cmd.args[0]}: $err')
|
||||
exit(1)
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if !found {
|
||||
println('invalid flag: ${cmd.args[0]}')
|
||||
exit(1)
|
||||
|
@ -116,10 +169,8 @@ fn (mut cmd Command) parse_flags() {
|
|||
|
||||
fn (mut cmd Command) parse_commands() {
|
||||
global_flags := cmd.flags.filter(it.global)
|
||||
|
||||
cmd.check_help_flag()
|
||||
cmd.check_version_flag()
|
||||
|
||||
for i in 0 .. cmd.args.len {
|
||||
arg := cmd.args[i]
|
||||
for j in 0 .. cmd.commands.len {
|
||||
|
@ -133,73 +184,97 @@ fn (mut cmd Command) parse_commands() {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
// if no further command was found, execute current command
|
||||
if int(cmd.execute) == 0 {
|
||||
if !cmd.disable_help {
|
||||
help_cmd := cmd.commands.get('help') or { return } // ignore error and handle command normally
|
||||
help_cmd.execute(help_cmd)
|
||||
cmd.execute_help()
|
||||
}
|
||||
} else {
|
||||
cmd.check_required_flags()
|
||||
|
||||
if int(cmd.pre_execute) > 0 {
|
||||
cmd.pre_execute(*cmd)
|
||||
cmd.pre_execute(*cmd) or {
|
||||
println('cli preexecution error: $err')
|
||||
exit(1)
|
||||
}
|
||||
}
|
||||
cmd.execute(*cmd) or {
|
||||
println('cli execution error: $err')
|
||||
exit(1)
|
||||
}
|
||||
|
||||
cmd.execute(*cmd)
|
||||
|
||||
if int(cmd.post_execute) > 0 {
|
||||
cmd.post_execute(*cmd)
|
||||
cmd.post_execute(*cmd) or {
|
||||
println('cli postexecution error: $err')
|
||||
exit(1)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn (mut cmd Command) check_help_flag() {
|
||||
if cmd.disable_help {
|
||||
return
|
||||
fn (cmd Command) has_abbrev_flags() bool {
|
||||
mut has_abbrev := false
|
||||
for flag in cmd.flags {
|
||||
if flag.abbrev != '' {
|
||||
has_abbrev = true
|
||||
}
|
||||
if cmd.flags.contains('help') {
|
||||
help_flag := cmd.flags.get_bool('help') or { return } // ignore error and handle command normally
|
||||
}
|
||||
return has_abbrev
|
||||
}
|
||||
|
||||
fn (cmd Command) check_help_flag() {
|
||||
if !cmd.disable_help && cmd.flags.contains('help') {
|
||||
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
|
||||
help_cmd.execute(help_cmd)
|
||||
cmd.execute_help()
|
||||
exit(0)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn (mut cmd Command) check_version_flag() {
|
||||
if cmd.disable_version {
|
||||
return
|
||||
}
|
||||
fn (cmd Command) check_version_flag() {
|
||||
if cmd.version != '' && cmd.flags.contains('version') {
|
||||
version_flag := cmd.flags.get_bool('version') or { return } // ignore error and handle command normally
|
||||
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
|
||||
version_cmd := cmd.commands.get('version') or {
|
||||
return
|
||||
} // ignore error and handle command normally
|
||||
version_cmd.execute(version_cmd)
|
||||
exit(0)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn (mut cmd Command) check_required_flags() {
|
||||
fn (cmd Command) check_required_flags() {
|
||||
for flag in cmd.flags {
|
||||
if flag.required && flag.value == '' {
|
||||
full_name := cmd.full_name()
|
||||
println('flag \'${flag.name}\' is required by \'${full_name}\'')
|
||||
println("flag \'$flag.name\' is required by \'$full_name\'")
|
||||
exit(1)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn (cmd Command) execute_help() {
|
||||
if cmd.commands.contains('help') {
|
||||
help_cmd := cmd.commands.get('help') or {
|
||||
return
|
||||
} // ignore error and handle command normally
|
||||
help_cmd.execute(help_cmd)
|
||||
} else {
|
||||
print(cmd.help_message())
|
||||
}
|
||||
}
|
||||
|
||||
fn (cmds []Command) get(name string) ?Command {
|
||||
for cmd in cmds {
|
||||
if cmd.name == name {
|
||||
return cmd
|
||||
}
|
||||
}
|
||||
return error('command \'${name}\' not found.')
|
||||
return error("command \'$name\' not found.")
|
||||
}
|
||||
|
||||
fn (cmds []Command) contains(name string) bool {
|
||||
|
@ -212,7 +287,7 @@ fn (cmds []Command) contains(name string) bool {
|
|||
}
|
||||
|
||||
fn (mut cmds []Command) sort() {
|
||||
cmds.sort_with_compare(fn(a &Command, b &Command) int {
|
||||
cmds.sort_with_compare(fn (a, b &Command) int {
|
||||
return compare_strings(&a.name, &b.name)
|
||||
})
|
||||
}
|
||||
|
|
|
@ -2,100 +2,95 @@ import cli
|
|||
|
||||
fn test_if_command_parses_empty_args() {
|
||||
mut cmd := cli.Command{
|
||||
name: 'command',
|
||||
execute: empty_func,
|
||||
name: 'command'
|
||||
execute: empty_func
|
||||
}
|
||||
cmd.parse(['command'])
|
||||
assert cmd.name == 'command'
|
||||
&& compare_arrays(cmd.args, [])
|
||||
assert cmd.name == 'command' && compare_arrays(cmd.args, [])
|
||||
}
|
||||
|
||||
fn test_if_command_parses_args() {
|
||||
mut cmd := cli.Command{
|
||||
name: 'command',
|
||||
execute: empty_func,
|
||||
name: 'command'
|
||||
execute: empty_func
|
||||
}
|
||||
cmd.parse(['command', 'arg0', 'arg1'])
|
||||
|
||||
assert cmd.name == 'command'
|
||||
&& compare_arrays(cmd.args, ['arg0', 'arg1'])
|
||||
assert cmd.name == 'command' && compare_arrays(cmd.args, ['arg0', 'arg1'])
|
||||
}
|
||||
|
||||
fn test_if_subcommands_parse_args() {
|
||||
mut cmd := cli.Command{
|
||||
name: 'command',
|
||||
name: 'command'
|
||||
}
|
||||
subcmd := cli.Command{
|
||||
name: 'subcommand',
|
||||
execute: empty_func,
|
||||
name: 'subcommand'
|
||||
execute: empty_func
|
||||
}
|
||||
cmd.add_command(subcmd)
|
||||
cmd.parse(['command', 'subcommand', 'arg0', 'arg1'])
|
||||
}
|
||||
|
||||
fn if_subcommands_parse_args_func(cmd cli.Command) {
|
||||
assert cmd.name == 'subcommand'
|
||||
&& compare_arrays(cmd.args, ['arg0', 'arg1'])
|
||||
assert cmd.name == 'subcommand' && compare_arrays(cmd.args, ['arg0', 'arg1'])
|
||||
}
|
||||
|
||||
fn test_if_command_has_default_help_subcommand() {
|
||||
mut cmd := cli.Command{
|
||||
name: 'command',
|
||||
name: 'command'
|
||||
}
|
||||
cmd.parse(['command'])
|
||||
|
||||
assert has_command(cmd, 'help')
|
||||
}
|
||||
|
||||
fn test_if_command_has_default_version_subcommand_if_version_is_set() {
|
||||
mut cmd := cli.Command{
|
||||
name: 'command',
|
||||
version: '1.0.0',
|
||||
name: 'command'
|
||||
version: '1.0.0'
|
||||
}
|
||||
cmd.parse(['command'])
|
||||
|
||||
assert has_command(cmd, 'version')
|
||||
}
|
||||
|
||||
fn test_if_flag_gets_set() {
|
||||
mut cmd := cli.Command{
|
||||
name: 'command',
|
||||
execute: if_flag_gets_set_func,
|
||||
name: 'command'
|
||||
execute: fn (cmd cli.Command) ? {
|
||||
flag := cmd.flags.get_string('flag')?
|
||||
assert flag == 'value'
|
||||
}
|
||||
}
|
||||
cmd.add_flag(cli.Flag{
|
||||
flag: .string
|
||||
name: 'flag'
|
||||
})
|
||||
cmd.parse(['command', '--flag', 'value'])
|
||||
}
|
||||
|
||||
fn if_flag_gets_set_func(cmd cli.Command) {
|
||||
flag := cmd.flags.get_string('flag') or { panic(err) }
|
||||
assert flag == 'value'
|
||||
cmd.parse(['command', '-flag', 'value'])
|
||||
}
|
||||
|
||||
fn test_if_flag_gets_set_with_abbrev() {
|
||||
mut cmd := cli.Command{
|
||||
name: 'command',
|
||||
execute: if_flag_gets_set_with_abbrev_func,
|
||||
name: 'command'
|
||||
execute: fn (cmd cli.Command) ? {
|
||||
flag := cmd.flags.get_string('flag')?
|
||||
assert flag == 'value'
|
||||
}
|
||||
}
|
||||
cmd.add_flag(cli.Flag{
|
||||
flag: .string,
|
||||
name: 'flag',
|
||||
abbrev: 'f',
|
||||
flag: .string
|
||||
name: 'flag'
|
||||
abbrev: 'f'
|
||||
})
|
||||
cmd.parse(['command', '-f', 'value'])
|
||||
}
|
||||
|
||||
fn if_flag_gets_set_with_abbrev_func(cmd cli.Command) {
|
||||
flag := cmd.flags.get_string('flag') or { panic(err) }
|
||||
assert flag == 'value'
|
||||
cmd.parse(['command', '--flag', 'value'])
|
||||
}
|
||||
|
||||
fn test_if_multiple_flags_get_set() {
|
||||
mut cmd := cli.Command{
|
||||
name: 'command',
|
||||
execute: if_multiple_flags_get_set_func,
|
||||
name: 'command'
|
||||
execute: fn (cmd cli.Command) ? {
|
||||
flag := cmd.flags.get_string('flag')?
|
||||
value := cmd.flags.get_int('value')?
|
||||
assert flag == 'value' && value == 42
|
||||
}
|
||||
}
|
||||
cmd.add_flag(cli.Flag{
|
||||
flag: .string
|
||||
|
@ -105,65 +100,57 @@ fn test_if_multiple_flags_get_set() {
|
|||
flag: .int
|
||||
name: 'value'
|
||||
})
|
||||
cmd.parse(['command', '--flag', 'value', '--value', '42'])
|
||||
}
|
||||
|
||||
fn if_multiple_flags_get_set_func(cmd cli.Command) {
|
||||
flag := cmd.flags.get_string('flag') or { panic(err) }
|
||||
value := cmd.flags.get_int('value') or { panic(err) }
|
||||
assert flag == 'value'
|
||||
&& value == 42
|
||||
cmd.parse(['command', '-flag', 'value', '-value', '42'])
|
||||
}
|
||||
|
||||
fn test_if_flag_gets_set_in_subcommand() {
|
||||
mut cmd := cli.Command{
|
||||
name: 'command',
|
||||
execute: empty_func,
|
||||
name: 'command'
|
||||
execute: empty_func
|
||||
}
|
||||
mut subcmd := cli.Command{
|
||||
name: 'subcommand',
|
||||
execute: if_flag_gets_set_in_subcommand_func
|
||||
name: 'subcommand'
|
||||
execute: fn (cmd cli.Command) ? {
|
||||
flag := cmd.flags.get_string('flag') or {
|
||||
panic(err)
|
||||
}
|
||||
assert flag == 'value'
|
||||
}
|
||||
}
|
||||
subcmd.add_flag(cli.Flag{
|
||||
flag: .string
|
||||
name: 'flag'
|
||||
})
|
||||
cmd.add_command(subcmd)
|
||||
cmd.parse(['command', 'subcommand', '--flag', 'value'])
|
||||
}
|
||||
|
||||
fn if_flag_gets_set_in_subcommand_func(cmd cli.Command) {
|
||||
flag := cmd.flags.get_string('flag') or { panic(err) }
|
||||
assert flag == 'value'
|
||||
cmd.parse(['command', 'subcommand', '-flag', 'value'])
|
||||
}
|
||||
|
||||
fn test_if_global_flag_gets_set_in_subcommand() {
|
||||
mut cmd := cli.Command{
|
||||
name: 'command',
|
||||
execute: empty_func,
|
||||
name: 'command'
|
||||
execute: empty_func
|
||||
}
|
||||
cmd.add_flag(cli.Flag{
|
||||
flag: .string,
|
||||
name: 'flag',
|
||||
global: true,
|
||||
flag: .string
|
||||
name: 'flag'
|
||||
global: true
|
||||
})
|
||||
subcmd := cli.Command{
|
||||
name: 'subcommand',
|
||||
execute: if_global_flag_gets_set_in_subcommand_func,
|
||||
name: 'subcommand'
|
||||
execute: fn (cmd cli.Command) {
|
||||
flag := cmd.flags.get_string('flag') or {
|
||||
panic(err)
|
||||
}
|
||||
cmd.add_command(subcmd)
|
||||
cmd.parse(['command', '--flag', 'value', 'subcommand'])
|
||||
}
|
||||
|
||||
fn if_global_flag_gets_set_in_subcommand_func(cmd cli.Command) {
|
||||
flag := cmd.flags.get_string('flag') or { panic(err) }
|
||||
assert flag == 'value'
|
||||
}
|
||||
|
||||
}
|
||||
cmd.add_command(subcmd)
|
||||
cmd.parse(['command', '-flag', 'value', 'subcommand'])
|
||||
}
|
||||
|
||||
// helper functions
|
||||
|
||||
fn empty_func(cmd cli.Command) {}
|
||||
fn empty_func(cmd cli.Command) ? {
|
||||
}
|
||||
|
||||
fn has_command(cmd cli.Command, name string) bool {
|
||||
for subcmd in cmd.commands {
|
||||
|
@ -174,7 +161,7 @@ fn has_command(cmd cli.Command, name string) bool {
|
|||
return false
|
||||
}
|
||||
|
||||
fn compare_arrays(array0 []string, array1 []string) bool {
|
||||
fn compare_arrays(array0, array1 []string) bool {
|
||||
if array0.len != array1.len {
|
||||
return false
|
||||
}
|
||||
|
|
|
@ -16,7 +16,6 @@ pub mut:
|
|||
global bool
|
||||
required bool
|
||||
value string
|
||||
|
||||
mut:
|
||||
found bool
|
||||
}
|
||||
|
@ -26,73 +25,101 @@ pub fn (flags []Flag) get_all_found() []Flag {
|
|||
}
|
||||
|
||||
pub fn (flag Flag) get_bool() ?bool {
|
||||
if flag.flag != .bool { return error('invalid flag type') }
|
||||
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) }
|
||||
flag := flags.get(name) or {
|
||||
return error(err)
|
||||
}
|
||||
return flag.get_bool()
|
||||
}
|
||||
|
||||
pub fn (flags []Flag) get_bool_or(name string, or_value bool) bool {
|
||||
value := flags.get_bool(name) or { return or_value }
|
||||
value := flags.get_bool(name) or {
|
||||
return or_value
|
||||
}
|
||||
return value
|
||||
}
|
||||
|
||||
pub fn (flag Flag) get_int() ?int {
|
||||
if flag.flag != .int { return error('invalid flag type') }
|
||||
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) }
|
||||
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 }
|
||||
value := flags.get_int(name) or {
|
||||
return or_value
|
||||
}
|
||||
return value
|
||||
}
|
||||
|
||||
pub fn (flag Flag) get_float() ?f64 {
|
||||
if flag.flag != .float { return error('invalid flag type') }
|
||||
if flag.flag != .float {
|
||||
return error('invalid flag type')
|
||||
}
|
||||
return flag.value.f64()
|
||||
}
|
||||
|
||||
pub fn (flags []Flag) get_float(name string) ?f64 {
|
||||
flag := flags.get(name) or { return error(err) }
|
||||
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 }
|
||||
value := flags.get_float(name) or {
|
||||
return or_value
|
||||
}
|
||||
return value
|
||||
}
|
||||
|
||||
pub fn (flag Flag) get_string() ?string {
|
||||
if flag.flag != .string { return error('invalid flag type') }
|
||||
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) }
|
||||
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 }
|
||||
pub fn (flags []Flag) get_string_or(name, or_value string) string {
|
||||
value := flags.get_string(name) or {
|
||||
return or_value
|
||||
}
|
||||
return value
|
||||
}
|
||||
|
||||
// parse flag value from arguments and return arguments with all consumed element removed
|
||||
fn (mut flag Flag) parse(args []string) ?[]string {
|
||||
if flag.matches(args) {
|
||||
fn (mut flag Flag) parse(args []string, with_abbrev bool) ?[]string {
|
||||
if flag.matches(args, with_abbrev) {
|
||||
if flag.flag == .bool {
|
||||
new_args := flag.parse_bool(args) or { return error(err) }
|
||||
new_args := flag.parse_bool(args) or {
|
||||
return error(err)
|
||||
}
|
||||
return new_args
|
||||
} else {
|
||||
new_args := flag.parse_raw(args) or { return error(err) }
|
||||
new_args := flag.parse_raw(args) or {
|
||||
return error(err)
|
||||
}
|
||||
return new_args
|
||||
}
|
||||
} else {
|
||||
|
@ -101,12 +128,16 @@ 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] == '--${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) matches(args []string, with_abbrev bool) bool {
|
||||
if with_abbrev {
|
||||
return (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='))
|
||||
} else {
|
||||
return (flag.name != '' && args[0] == '-$flag.name') ||
|
||||
(flag.name != '' && args[0].starts_with('-$flag.name='))
|
||||
}
|
||||
}
|
||||
|
||||
fn (mut flag Flag) parse_raw(args []string) ?[]string {
|
||||
|
@ -117,7 +148,7 @@ fn (mut flag Flag) parse_raw(args []string) ?[]string {
|
|||
flag.value = args[1]
|
||||
return args[2..]
|
||||
}
|
||||
return error('missing argument for ${flag.name}')
|
||||
return error('missing argument for $flag.name')
|
||||
}
|
||||
|
||||
fn (mut flag Flag) parse_bool(args []string) ?[]string {
|
||||
|
@ -140,7 +171,7 @@ fn (flags []Flag) get(name string) ?Flag {
|
|||
return flag
|
||||
}
|
||||
}
|
||||
return error('flag ${name} not found.')
|
||||
return error('flag $name not found.')
|
||||
}
|
||||
|
||||
fn (flags []Flag) contains(name string) bool {
|
||||
|
@ -153,7 +184,7 @@ fn (flags []Flag) contains(name string) bool {
|
|||
}
|
||||
|
||||
fn (mut flags []Flag) sort() {
|
||||
flags.sort_with_compare(fn(a &Flag, b &Flag) int {
|
||||
flags.sort_with_compare(fn (a, b &Flag) int {
|
||||
return compare_strings(&a.name, &b.name)
|
||||
})
|
||||
}
|
||||
|
|
|
@ -2,74 +2,126 @@ import cli
|
|||
|
||||
fn test_if_string_flag_parses() {
|
||||
mut flag := cli.Flag{
|
||||
flag: .string,
|
||||
name: 'flag',
|
||||
flag: .string
|
||||
name: 'flag'
|
||||
}
|
||||
flag.parse(['-flag', 'value'], false) or {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
flag.parse(['--flag', 'value']) or { panic(err) }
|
||||
assert flag.value == 'value'
|
||||
|
||||
flag.parse(['--flag=value']) or { panic(err) }
|
||||
flag.parse(['-flag=value'], false) or {
|
||||
panic(err)
|
||||
}
|
||||
assert flag.value == 'value'
|
||||
}
|
||||
|
||||
fn test_if_bool_flag_parses() {
|
||||
mut flag := cli.Flag{
|
||||
flag: .bool,
|
||||
name: 'flag',
|
||||
flag: .bool
|
||||
name: 'flag'
|
||||
}
|
||||
mut value := false
|
||||
|
||||
flag.parse(['--flag']) or { panic(err) }
|
||||
value = flag.get_bool() or { panic(err) }
|
||||
flag.parse(['-flag'], false) or {
|
||||
panic(err)
|
||||
}
|
||||
value = flag.get_bool() or {
|
||||
panic(err)
|
||||
}
|
||||
assert value == true
|
||||
|
||||
flag.parse(['--flag', 'true']) or { panic(err) }
|
||||
value = flag.get_bool() or { panic(err) }
|
||||
flag.parse(['-flag', 'true'], false) or {
|
||||
panic(err)
|
||||
}
|
||||
value = flag.get_bool() or {
|
||||
panic(err)
|
||||
}
|
||||
assert value == true
|
||||
|
||||
flag.parse(['--flag=true']) or { panic(err) }
|
||||
value = flag.get_bool() or { panic(err) }
|
||||
flag.parse(['-flag=true'], false) or {
|
||||
panic(err)
|
||||
}
|
||||
value = flag.get_bool() or {
|
||||
panic(err)
|
||||
}
|
||||
assert value == true
|
||||
|
||||
flag.parse(['--flag', 'false']) or { panic(err) }
|
||||
value = flag.get_bool() or { panic(err) }
|
||||
flag.parse(['-flag', 'false'], 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) }
|
||||
flag.parse(['-flag=false'], false) or {
|
||||
panic(err)
|
||||
}
|
||||
value = flag.get_bool() or {
|
||||
panic(err)
|
||||
}
|
||||
assert value == false
|
||||
}
|
||||
|
||||
fn test_if_int_flag_parses() {
|
||||
mut flag := cli.Flag{
|
||||
flag: .int,
|
||||
name: 'flag',
|
||||
flag: .int
|
||||
name: 'flag'
|
||||
}
|
||||
mut value := 0
|
||||
|
||||
flag.parse(['--flag', '42']) or { panic(err) }
|
||||
value = flag.get_int() or { panic(err) }
|
||||
flag.parse(['-flag', '42'], false) or {
|
||||
panic(err)
|
||||
}
|
||||
value = flag.get_int() or {
|
||||
panic(err)
|
||||
}
|
||||
assert value == 42
|
||||
|
||||
flag.parse(['--flag=42']) or { panic(err) }
|
||||
value = flag.get_int() or { panic(err) }
|
||||
flag.parse(['-flag=42'], false) or {
|
||||
panic(err)
|
||||
}
|
||||
value = flag.get_int() or {
|
||||
panic(err)
|
||||
}
|
||||
assert value == 42
|
||||
}
|
||||
|
||||
fn test_if_float_flag_parses() {
|
||||
mut flag := cli.Flag{
|
||||
flag: .float,
|
||||
name: 'flag',
|
||||
flag: .float
|
||||
name: 'flag'
|
||||
}
|
||||
mut value := f64(0)
|
||||
|
||||
flag.parse(['--flag', '3.14159']) or { panic(err) }
|
||||
value = flag.get_float() or { panic(err) }
|
||||
flag.parse(['-flag', '3.14159'], false) or {
|
||||
panic(err)
|
||||
}
|
||||
value = flag.get_float() or {
|
||||
panic(err)
|
||||
}
|
||||
assert value == 3.14159
|
||||
|
||||
flag.parse(['--flag=3.14159']) or { panic(err) }
|
||||
flag.parse(['-flag=3.14159'], false) or {
|
||||
panic(err)
|
||||
}
|
||||
assert flag.value.f64() == 3.14159
|
||||
value = flag.get_float() or { panic(err) }
|
||||
value = flag.get_float() or {
|
||||
panic(err)
|
||||
}
|
||||
assert value == 3.14159
|
||||
}
|
||||
|
||||
fn test_if_flag_parses_with_abbrev() {
|
||||
mut flag := cli.Flag{
|
||||
flag: .bool
|
||||
name: 'flag'
|
||||
abbrev: 'f'
|
||||
}
|
||||
mut value := false
|
||||
flag.parse(['--flag'], true) or {
|
||||
panic(err)
|
||||
}
|
||||
value = flag.get_bool() or {
|
||||
panic(err)
|
||||
}
|
||||
assert value == true
|
||||
flag.parse(['-flag'], true) or {
|
||||
panic(err)
|
||||
}
|
||||
value = flag.get_bool() or {
|
||||
panic(err)
|
||||
}
|
||||
assert value == true
|
||||
}
|
||||
|
|
115
vlib/cli/help.v
115
vlib/cli/help.v
|
@ -10,38 +10,69 @@ const (
|
|||
)
|
||||
|
||||
fn help_flag(with_abbrev bool) Flag {
|
||||
sabbrev := if with_abbrev { 'h' } else { '' }
|
||||
return Flag{
|
||||
flag: .bool,
|
||||
name: 'help',
|
||||
abbrev: if with_abbrev { 'h' } else { '' },
|
||||
description: 'Prints help information',
|
||||
flag: .bool
|
||||
name: 'help'
|
||||
abbrev: sabbrev
|
||||
description: 'Prints help information'
|
||||
}
|
||||
}
|
||||
|
||||
fn help_cmd() Command {
|
||||
return Command{
|
||||
name: 'help',
|
||||
description: 'Prints help information',
|
||||
execute: help_func,
|
||||
name: 'help'
|
||||
usage: '<command>'
|
||||
description: 'Prints help information'
|
||||
execute: print_help_for_command
|
||||
}
|
||||
}
|
||||
|
||||
fn help_func(help_cmd Command) {
|
||||
cmd := help_cmd.parent
|
||||
full_name := cmd.full_name()
|
||||
fn print_help_for_command(help_cmd Command) ? {
|
||||
if help_cmd.args.len > 0 {
|
||||
mut cmd := help_cmd.parent
|
||||
for arg in help_cmd.args {
|
||||
mut found := false
|
||||
for sub_cmd in cmd.commands {
|
||||
if sub_cmd.name == arg {
|
||||
cmd = &sub_cmd
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
args := help_cmd.args.join(' ')
|
||||
print('invalid command: $args')
|
||||
return
|
||||
}
|
||||
}
|
||||
print(cmd.help_message())
|
||||
} else {
|
||||
if help_cmd.parent != 0 {
|
||||
print(help_cmd.parent.help_message())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn (cmd Command) help_message() string {
|
||||
mut help := ''
|
||||
help += 'Usage: ${full_name}'
|
||||
if cmd.flags.len > 0 { help += ' [FLAGS]'}
|
||||
if cmd.commands.len > 0 { help += ' [COMMANDS]'}
|
||||
help += '\n\n'
|
||||
|
||||
if cmd.description != '' {
|
||||
help += '${cmd.description}\n\n'
|
||||
help += 'Usage: $cmd.full_name()'
|
||||
if cmd.flags.len > 0 {
|
||||
help += ' [flags]'
|
||||
}
|
||||
if cmd.commands.len > 0 {
|
||||
help += ' [commands]'
|
||||
}
|
||||
if cmd.usage.len > 0 {
|
||||
help += ' $cmd.usage'
|
||||
}
|
||||
help += '\n\n'
|
||||
if cmd.description != '' {
|
||||
help += '$cmd.description\n\n'
|
||||
}
|
||||
|
||||
mut abbrev_len := 0
|
||||
mut name_len := min_description_indent_len
|
||||
if cmd.has_abbrev_flags() {
|
||||
for flag in cmd.flags {
|
||||
abbrev_len = max(abbrev_len, flag.abbrev.len + spacing + 1) // + 1 for '-' in front
|
||||
name_len = max(name_len, abbrev_len + flag.name.len + spacing + 2) // + 2 for '--' in front
|
||||
|
@ -49,27 +80,35 @@ fn help_func(help_cmd Command) {
|
|||
for command in cmd.commands {
|
||||
name_len = max(name_len, command.name.len + spacing)
|
||||
}
|
||||
|
||||
} else {
|
||||
for flag in cmd.flags {
|
||||
name_len = max(name_len, abbrev_len + flag.name.len + spacing + 1) // + 1 for '-' in front
|
||||
}
|
||||
for command in cmd.commands {
|
||||
name_len = max(name_len, command.name.len + spacing)
|
||||
}
|
||||
}
|
||||
if cmd.flags.len > 0 {
|
||||
help += 'Flags:\n'
|
||||
for flag in cmd.flags {
|
||||
mut flag_name := ''
|
||||
if flag.abbrev != '' {
|
||||
if flag.abbrev != '' && cmd.has_abbrev_flags() {
|
||||
abbrev_indent := ' '.repeat(abbrev_len - flag.abbrev.len - 1) // - 1 for '-' in front
|
||||
flag_name = '-${flag.abbrev}${abbrev_indent}--${flag.name}'
|
||||
} else {
|
||||
flag_name = '-$flag.abbrev$abbrev_indent--$flag.name'
|
||||
} else if cmd.has_abbrev_flags() {
|
||||
abbrev_indent := ' '.repeat(abbrev_len)
|
||||
flag_name = '${abbrev_indent}--${flag.name}'
|
||||
flag_name = '$abbrev_indent--$flag.name'
|
||||
} else {
|
||||
flag_name = '-$flag.name'
|
||||
}
|
||||
mut required := ''
|
||||
if flag.required {
|
||||
required = ' (required)'
|
||||
}
|
||||
|
||||
base_indent := ' '.repeat(base_indent_len)
|
||||
description_indent := ' '.repeat(name_len - flag_name.len)
|
||||
help += '${base_indent}${flag_name}${description_indent}' +
|
||||
pretty_description(flag.description + required, base_indent_len + name_len) + '\n'
|
||||
help += '$base_indent$flag_name$description_indent' + pretty_description(flag.description +
|
||||
required, base_indent_len + name_len) + '\n'
|
||||
}
|
||||
help += '\n'
|
||||
}
|
||||
|
@ -78,14 +117,12 @@ fn help_func(help_cmd Command) {
|
|||
for command in cmd.commands {
|
||||
base_indent := ' '.repeat(base_indent_len)
|
||||
description_indent := ' '.repeat(name_len - command.name.len)
|
||||
|
||||
help += '${base_indent}${command.name}${description_indent}' +
|
||||
pretty_description(command.description, name_len) + '\n'
|
||||
help += '$base_indent$command.name$description_indent' + pretty_description(command.description, name_len) +
|
||||
'\n'
|
||||
}
|
||||
help += '\n'
|
||||
}
|
||||
|
||||
print(help)
|
||||
return help
|
||||
}
|
||||
|
||||
// pretty_description resizes description text depending on terminal width.
|
||||
|
@ -100,15 +137,20 @@ fn pretty_description(s string, indent_len int) string {
|
|||
chars_per_line := width - indent_len
|
||||
// Give us enough room, better a little bigger than smaller
|
||||
mut acc := strings.new_builder(((s.len / chars_per_line) + 1) * (width + 1))
|
||||
|
||||
for k, line in s.split('\n') {
|
||||
if k != 0 { acc.write('\n${indent}') }
|
||||
if k != 0 {
|
||||
acc.write('\n$indent')
|
||||
}
|
||||
mut i := chars_per_line - 2
|
||||
mut j := 0
|
||||
for ; i < line.len; i += chars_per_line - 2 {
|
||||
for line.str[i] != ` ` { i-- }
|
||||
for line.str[i] != ` ` {
|
||||
i--
|
||||
}
|
||||
// indent was already done the first iteration
|
||||
if j != 0 { acc.write(indent) }
|
||||
if j != 0 {
|
||||
acc.write(indent)
|
||||
}
|
||||
acc.writeln(line[j..i].trim_space())
|
||||
j = i
|
||||
}
|
||||
|
@ -122,5 +164,6 @@ fn pretty_description(s string, indent_len int) string {
|
|||
}
|
||||
|
||||
fn max(a, b int) int {
|
||||
return if a > b {a} else {b}
|
||||
res := if a > b { a } else { b }
|
||||
return res
|
||||
}
|
||||
|
|
|
@ -1,24 +1,25 @@
|
|||
module cli
|
||||
|
||||
fn version_flag(with_abbrev bool) Flag {
|
||||
sabbrev := if with_abbrev { 'v' } else { '' }
|
||||
return Flag{
|
||||
flag: .bool,
|
||||
name: 'version',
|
||||
abbrev: if with_abbrev { 'v' } else { '' },
|
||||
description: 'Prints version information',
|
||||
flag: .bool
|
||||
name: 'version'
|
||||
abbrev: sabbrev
|
||||
description: 'Prints version information'
|
||||
}
|
||||
}
|
||||
|
||||
fn version_cmd() Command {
|
||||
return Command{
|
||||
name: 'version'
|
||||
description: 'Prints version information',
|
||||
execute: version_func,
|
||||
description: 'Prints version information'
|
||||
execute: version_func
|
||||
}
|
||||
}
|
||||
|
||||
fn version_func(version_cmd Command) {
|
||||
fn version_func(version_cmd Command) ? {
|
||||
cmd := version_cmd.parent
|
||||
version := '${cmd.name} v${cmd.version}'
|
||||
version := '$cmd.name version $cmd.version'
|
||||
println(version)
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue