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