mirror of
				https://github.com/vlang/v.git
				synced 2023-08-10 21:13:21 +03:00 
			
		
		
		
	
		
			
				
	
	
		
			287 lines
		
	
	
		
			6.5 KiB
		
	
	
	
		
			V
		
	
	
	
	
	
			
		
		
	
	
			287 lines
		
	
	
		
			6.5 KiB
		
	
	
	
		
			V
		
	
	
	
	
	
| module cli
 | |
| 
 | |
| 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     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 = 0
 | |
| 	commands        []Command
 | |
| 	flags           []Flag
 | |
| 	required_args   int
 | |
| 	args            []string
 | |
| }
 | |
| 
 | |
| 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 ...}'
 | |
| 	}
 | |
| 	res << '	commands: $cmd.commands'
 | |
| 	res << '	flags: $cmd.flags'
 | |
| 	res << '	required_args: $cmd.required_args'
 | |
| 	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 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) {
 | |
| 	mut subcmd := command
 | |
| 	if cmd.commands.contains(subcmd.name) {
 | |
| 		println('Command with the name `$subcmd.name` already exists')
 | |
| 		exit(1)
 | |
| 	}
 | |
| 	subcmd.parent = cmd
 | |
| 	cmd.commands << subcmd
 | |
| }
 | |
| 
 | |
| 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) {
 | |
| 	if cmd.flags.contains(flag.name) {
 | |
| 		println('Flag with the name `$flag.name` already exists')
 | |
| 		exit(1)
 | |
| 	}
 | |
| 	cmd.flags << flag
 | |
| }
 | |
| 
 | |
| pub fn (mut cmd Command) parse(args []string) {
 | |
| 	if !cmd.disable_flags {
 | |
| 		cmd.add_default_flags()
 | |
| 	}
 | |
| 	cmd.add_default_commands()
 | |
| 	if cmd.sort_flags {
 | |
| 		cmd.flags.sort(a.name < b.name)
 | |
| 	}
 | |
| 	if cmd.sort_commands {
 | |
| 		cmd.commands.sort(a.name < b.name)
 | |
| 	}
 | |
| 	cmd.args = args[1..]
 | |
| 	if !cmd.disable_flags {
 | |
| 		cmd.parse_flags()
 | |
| 	}
 | |
| 	cmd.parse_commands()
 | |
| }
 | |
| 
 | |
| fn (mut cmd Command) add_default_flags() {
 | |
| 	if !cmd.disable_help && !cmd.flags.contains('help') {
 | |
| 		use_help_abbrev := !cmd.flags.contains('h') && cmd.flags.have_abbrev()
 | |
| 		cmd.add_flag(help_flag(use_help_abbrev))
 | |
| 	}
 | |
| 	if !cmd.disable_version && cmd.version != '' && !cmd.flags.contains('version') {
 | |
| 		use_version_abbrev := !cmd.flags.contains('v') && cmd.flags.have_abbrev()
 | |
| 		cmd.add_flag(version_flag(use_version_abbrev))
 | |
| 	}
 | |
| }
 | |
| 
 | |
| fn (mut cmd Command) add_default_commands() {
 | |
| 	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') {
 | |
| 		cmd.add_command(version_cmd())
 | |
| 	}
 | |
| }
 | |
| 
 | |
| fn (mut cmd Command) parse_flags() {
 | |
| 	for {
 | |
| 		if cmd.args.len < 1 || !cmd.args[0].starts_with('-') {
 | |
| 			break
 | |
| 		}
 | |
| 		mut found := false
 | |
| 		for i in 0 .. cmd.flags.len {
 | |
| 			unsafe {
 | |
| 			mut flag := &cmd.flags[i]
 | |
| 			if flag.matches(cmd.args, cmd.flags.have_abbrev()) {
 | |
| 				found = true
 | |
| 				flag.found = true
 | |
| 				cmd.args = flag.parse(cmd.args, cmd.flags.have_abbrev()) or {
 | |
| 					println('Failed to parse flag `${cmd.args[0]}`: $err')
 | |
| 					exit(1)
 | |
| 				}
 | |
| 				break
 | |
| 			}
 | |
| 		  }
 | |
| 		}
 | |
| 		if !found {
 | |
| 			println('Command `$cmd.name` has no flag `${cmd.args[0]}`')
 | |
| 			exit(1)
 | |
| 		}
 | |
| 	}
 | |
| }
 | |
| 
 | |
| 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 {
 | |
| 			mut command := cmd.commands[j]
 | |
| 			if command.name == arg {
 | |
| 				for flag in global_flags {
 | |
| 					command.add_flag(flag)
 | |
| 				}
 | |
| 				command.parse(cmd.args[i..])
 | |
| 				return
 | |
| 			}
 | |
| 		}
 | |
| 	}
 | |
| 	if cmd.is_root() && int(cmd.execute) == 0 {
 | |
| 		if !cmd.disable_help {
 | |
| 			cmd.execute_help()
 | |
| 			return
 | |
| 		}
 | |
| 	}
 | |
| 	// if no further command was found, execute current command
 | |
| 	if cmd.required_args > 0 {
 | |
| 		if cmd.required_args > cmd.args.len {
 | |
| 			eprintln('Command `$cmd.name` needs at least $cmd.required_args arguments')
 | |
| 			exit(1)
 | |
| 		}
 | |
| 	}
 | |
| 	cmd.check_required_flags()
 | |
| 	if int(cmd.pre_execute) > 0 {
 | |
| 		cmd.pre_execute(*cmd) or {
 | |
| 			eprintln('cli preexecution error: $err')
 | |
| 			exit(1)
 | |
| 		}
 | |
| 	}
 | |
| 	if int(cmd.execute) > 0 {
 | |
| 		cmd.execute(*cmd) or {
 | |
| 			eprintln('cli execution error: $err')
 | |
| 			exit(1)
 | |
| 		}
 | |
| 	}
 | |
| 	if int(cmd.post_execute) > 0 {
 | |
| 		cmd.post_execute(*cmd) or {
 | |
| 			eprintln('cli postexecution error: $err')
 | |
| 			exit(1)
 | |
| 		}
 | |
| 	}
 | |
| }
 | |
| 
 | |
| 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 {
 | |
| 			cmd.execute_help()
 | |
| 			exit(0)
 | |
| 		}
 | |
| 	}
 | |
| }
 | |
| 
 | |
| fn (cmd Command) check_version_flag() {
 | |
| 	if !cmd.disable_version && cmd.version != '' && cmd.flags.contains('version') {
 | |
| 		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.execute(version_cmd)
 | |
| 			exit(0)
 | |
| 		}
 | |
| 	}
 | |
| }
 | |
| 
 | |
| 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`')
 | |
| 			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 in $cmds')
 | |
| }
 | |
| 
 | |
| fn (cmds []Command) contains(name string) bool {
 | |
| 	for cmd in cmds {
 | |
| 		if cmd.name == name {
 | |
| 			return true
 | |
| 		}
 | |
| 	}
 | |
| 	return false
 | |
| }
 | 
