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 {
 | 
			
		||||
	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())
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -94,19 +148,18 @@ fn (mut cmd Command) parse_flags() {
 | 
			
		|||
			break
 | 
			
		||||
		}
 | 
			
		||||
		mut found := false
 | 
			
		||||
		for i in 0..cmd.flags.len {
 | 
			
		||||
		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,13 +169,11 @@ 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 {
 | 
			
		||||
	for i in 0 .. cmd.args.len {
 | 
			
		||||
		arg := cmd.args[i]
 | 
			
		||||
		for j in 0..cmd.commands.len {
 | 
			
		||||
		for j in 0 .. cmd.commands.len {
 | 
			
		||||
			mut command := cmd.commands[j]
 | 
			
		||||
			if command.name == arg {
 | 
			
		||||
				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 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)
 | 
			
		||||
			}
 | 
			
		||||
			assert flag == 'value'
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	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
 | 
			
		||||
 | 
			
		||||
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,11 +161,11 @@ 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
 | 
			
		||||
	}
 | 
			
		||||
	for i in 0..array0.len {
 | 
			
		||||
	for i in 0 .. array0.len {
 | 
			
		||||
		if array0[i] != array1[i] {
 | 
			
		||||
			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
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										117
									
								
								vlib/cli/help.v
								
								
								
								
							
							
						
						
									
										117
									
								
								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()
 | 
			
		||||
 | 
			
		||||
	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'
 | 
			
		||||
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: $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