diff options
Diffstat (limited to 'addons/gdUnit4/src/cmd')
| -rw-r--r-- | addons/gdUnit4/src/cmd/CmdArgumentParser.gd | 62 | ||||
| -rw-r--r-- | addons/gdUnit4/src/cmd/CmdArgumentParser.gd.uid | 1 | ||||
| -rw-r--r-- | addons/gdUnit4/src/cmd/CmdCommand.gd | 27 | ||||
| -rw-r--r-- | addons/gdUnit4/src/cmd/CmdCommand.gd.uid | 1 | ||||
| -rw-r--r-- | addons/gdUnit4/src/cmd/CmdCommandHandler.gd | 136 | ||||
| -rw-r--r-- | addons/gdUnit4/src/cmd/CmdCommandHandler.gd.uid | 1 | ||||
| -rw-r--r-- | addons/gdUnit4/src/cmd/CmdOption.gd | 61 | ||||
| -rw-r--r-- | addons/gdUnit4/src/cmd/CmdOption.gd.uid | 1 | ||||
| -rw-r--r-- | addons/gdUnit4/src/cmd/CmdOptions.gd | 31 | ||||
| -rw-r--r-- | addons/gdUnit4/src/cmd/CmdOptions.gd.uid | 1 |
10 files changed, 322 insertions, 0 deletions
diff --git a/addons/gdUnit4/src/cmd/CmdArgumentParser.gd b/addons/gdUnit4/src/cmd/CmdArgumentParser.gd new file mode 100644 index 0000000..aa02319 --- /dev/null +++ b/addons/gdUnit4/src/cmd/CmdArgumentParser.gd @@ -0,0 +1,62 @@ +class_name CmdArgumentParser +extends RefCounted + +var _options :CmdOptions +var _tool_name :String +var _parsed_commands :Dictionary = Dictionary() + + +func _init(p_options :CmdOptions, p_tool_name :String) -> void: + _options = p_options + _tool_name = p_tool_name + + +func parse(args :Array, ignore_unknown_cmd := false) -> GdUnitResult: + _parsed_commands.clear() + + # parse until first program argument + while not args.is_empty(): + var arg :String = args.pop_front() + if arg.find(_tool_name) != -1: + break + + if args.is_empty(): + return GdUnitResult.empty() + + # now parse all arguments + while not args.is_empty(): + var cmd :String = args.pop_front() + var option := _options.get_option(cmd) + + if option: + if _parse_cmd_arguments(option, args) == -1: + return GdUnitResult.error("The '%s' command requires an argument!" % option.short_command()) + elif not ignore_unknown_cmd: + return GdUnitResult.error("Unknown '%s' command!" % cmd) + return GdUnitResult.success(_parsed_commands.values()) + + +func options() -> CmdOptions: + return _options + + +func _parse_cmd_arguments(option: CmdOption, args: Array) -> int: + var command_name := option.short_command() + var command: CmdCommand = _parsed_commands.get(command_name, CmdCommand.new(command_name)) + + if option.has_argument(): + if not option.is_argument_optional() and args.is_empty(): + return -1 + if _is_next_value_argument(args): + var value: String = args.pop_front() + command.add_argument(value) + elif not option.is_argument_optional(): + return -1 + _parsed_commands[command_name] = command + return 0 + + +func _is_next_value_argument(args: PackedStringArray) -> bool: + if args.is_empty(): + return false + return _options.get_option(args[0]) == null diff --git a/addons/gdUnit4/src/cmd/CmdArgumentParser.gd.uid b/addons/gdUnit4/src/cmd/CmdArgumentParser.gd.uid new file mode 100644 index 0000000..978c595 --- /dev/null +++ b/addons/gdUnit4/src/cmd/CmdArgumentParser.gd.uid @@ -0,0 +1 @@ +uid://v2okve7k32hr diff --git a/addons/gdUnit4/src/cmd/CmdCommand.gd b/addons/gdUnit4/src/cmd/CmdCommand.gd new file mode 100644 index 0000000..92e8c1f --- /dev/null +++ b/addons/gdUnit4/src/cmd/CmdCommand.gd @@ -0,0 +1,27 @@ +class_name CmdCommand +extends RefCounted + +var _name: String +var _arguments: PackedStringArray + + +func _init(p_name :String, p_arguments := []) -> void: + _name = p_name + _arguments = PackedStringArray(p_arguments) + + +func name() -> String: + return _name + + +func arguments() -> PackedStringArray: + return _arguments + + +func add_argument(arg :String) -> void: + @warning_ignore("return_value_discarded") + _arguments.append(arg) + + +func _to_string() -> String: + return "%s:%s" % [_name, ", ".join(_arguments)] diff --git a/addons/gdUnit4/src/cmd/CmdCommand.gd.uid b/addons/gdUnit4/src/cmd/CmdCommand.gd.uid new file mode 100644 index 0000000..28437db --- /dev/null +++ b/addons/gdUnit4/src/cmd/CmdCommand.gd.uid @@ -0,0 +1 @@ +uid://detl386jrd80u diff --git a/addons/gdUnit4/src/cmd/CmdCommandHandler.gd b/addons/gdUnit4/src/cmd/CmdCommandHandler.gd new file mode 100644 index 0000000..2a7ed55 --- /dev/null +++ b/addons/gdUnit4/src/cmd/CmdCommandHandler.gd @@ -0,0 +1,136 @@ +class_name CmdCommandHandler +extends RefCounted + +const CB_SINGLE_ARG = 0 +const CB_MULTI_ARGS = 1 +const NO_CB := Callable() + +var _cmd_options :CmdOptions +# holds the command callbacks by key:<cmd_name>:String and value: [<cb single arg>, <cb multible args>]:Array +# Dictionary[String, Array[Callback] +var _command_cbs :Dictionary + + + +func _init(cmd_options: CmdOptions) -> void: + _cmd_options = cmd_options + + +# register a callback function for given command +# cmd_name short name of the command +# fr_arg a funcref to a function with a single argument +func register_cb(cmd_name: String, cb: Callable) -> CmdCommandHandler: + var registered_cb: Array = _command_cbs.get(cmd_name, [NO_CB, NO_CB]) + if registered_cb[CB_SINGLE_ARG]: + push_error("A function for command '%s' is already registered!" % cmd_name) + return self + + if not _validate_cb_signature(cb, TYPE_STRING): + push_error( + ("The callback '%s:%s' for command '%s' has invalid function signature. " + +"The callback signature must be 'func name(value: PackedStringArray)'") + % [cb.get_object().get_class(), cb.get_method(), cmd_name]) + return null + + registered_cb[CB_SINGLE_ARG] = cb + _command_cbs[cmd_name] = registered_cb + return self + + +# register a callback function for given command +# cb a funcref to a function with a variable number of arguments but expects all parameters to be passed via a single Array. +func register_cbv(cmd_name: String, cb: Callable) -> CmdCommandHandler: + var registered_cb: Array = _command_cbs.get(cmd_name, [NO_CB, NO_CB]) + if registered_cb[CB_MULTI_ARGS]: + push_error("A function for command '%s' is already registered!" % cmd_name) + return self + + if not _validate_cb_signature(cb, TYPE_PACKED_STRING_ARRAY): + push_error( + ("The callback '%s:%s' for command '%s' has invalid function signature. " + +"The callback signature must be 'func name(value: PackedStringArray)'") + % [cb.get_object().get_class(), cb.get_method(), cmd_name]) + return null + + registered_cb[CB_MULTI_ARGS] = cb + _command_cbs[cmd_name] = registered_cb + return self + + +func _validate() -> GdUnitResult: + var errors := PackedStringArray() + # Dictionary[StringName, String] + var registered_cbs := Dictionary() + + for cmd_name in _command_cbs.keys() as Array[String]: + var cb: Callable = (_command_cbs[cmd_name][CB_SINGLE_ARG] + if _command_cbs[cmd_name][CB_SINGLE_ARG] + else _command_cbs[cmd_name][CB_MULTI_ARGS]) + if cb != NO_CB and not cb.is_valid(): + @warning_ignore("return_value_discarded") + errors.append("Invalid function reference for command '%s', Check the function reference!" % cmd_name) + if _cmd_options.get_option(cmd_name) == null: + @warning_ignore("return_value_discarded") + errors.append("The command '%s' is unknown, verify your CmdOptions!" % cmd_name) + # verify for multiple registered command callbacks + if cb != NO_CB: + var cb_method := cb.get_method() + if registered_cbs.has(cb_method): + var already_registered_cmd :String = registered_cbs[cb_method] + @warning_ignore("return_value_discarded") + errors.append("The function reference '%s' already registerd for command '%s'!" % [cb_method, already_registered_cmd]) + else: + registered_cbs[cb_method] = cmd_name + if errors.is_empty(): + return GdUnitResult.success(true) + return GdUnitResult.error("\n".join(errors)) + + +func execute(commands: Array[CmdCommand]) -> GdUnitResult: + var result := _validate() + if result.is_error(): + return result + for cmd in commands: + var cmd_name := cmd.name() + if _command_cbs.has(cmd_name): + var cb_s: Callable = _command_cbs.get(cmd_name)[CB_SINGLE_ARG] + var arguments := cmd.arguments() + var cmd_option := _cmd_options.get_option(cmd_name) + + if arguments.is_empty(): + cb_s.call() + elif arguments.size() > 1: + var cb_m: Callable = _command_cbs.get(cmd_name)[CB_MULTI_ARGS] + cb_m.call(arguments) + else: + if cmd_option.type() == TYPE_BOOL: + cb_s.call(true if arguments[0] == "true" else false) + else: + cb_s.call(arguments[0]) + + return GdUnitResult.success(true) + + +func _validate_cb_signature(cb: Callable, arg_type: int) -> bool: + for m in cb.get_object().get_method_list(): + if m["name"] == cb.get_method(): + @warning_ignore("unsafe_cast") + return _validate_func_arguments(m["args"] as Array, arg_type) + return true + + +func _validate_func_arguments(arguments: Array, arg_type: int) -> bool: + # validate we have a single argument + if arguments.size() > 1: + return false + # a cb with no arguments is also valid + if arguments.size() == 0: + return true + # validate argument type + var arg: Dictionary = arguments[0] + @warning_ignore("unsafe_cast") + if arg["usage"] as int == PROPERTY_USAGE_NIL_IS_VARIANT: + return true + if arg["type"] != arg_type: + return false + return true diff --git a/addons/gdUnit4/src/cmd/CmdCommandHandler.gd.uid b/addons/gdUnit4/src/cmd/CmdCommandHandler.gd.uid new file mode 100644 index 0000000..b0fa79a --- /dev/null +++ b/addons/gdUnit4/src/cmd/CmdCommandHandler.gd.uid @@ -0,0 +1 @@ +uid://2q800f4gbor2 diff --git a/addons/gdUnit4/src/cmd/CmdOption.gd b/addons/gdUnit4/src/cmd/CmdOption.gd new file mode 100644 index 0000000..a4982de --- /dev/null +++ b/addons/gdUnit4/src/cmd/CmdOption.gd @@ -0,0 +1,61 @@ +class_name CmdOption +extends RefCounted + + +var _commands :PackedStringArray +var _help :String +var _description :String +var _type :int +var _arg_optional :bool = false + + +# constructs a command option by given arguments +# commands : a string with comma separated list of available commands begining with the short form +# help: a help text show howto use +# description: a full description of the command +# type: the argument type +# arg_optional: defines of the argument optional +func _init(p_commands :String, p_help :String, p_description :String, p_type :int = TYPE_NIL, p_arg_optional :bool = false) -> void: + _commands = p_commands.replace(" ", "").replace("\t", "").split(",") + _help = p_help + _description = p_description + _type = p_type + _arg_optional = p_arg_optional + + +func commands() -> PackedStringArray: + return _commands + + +func short_command() -> String: + return _commands[0] + + +func help() -> String: + return _help + + +func description() -> String: + return _description + + +func type() -> int: + return _type + + +func is_argument_optional() -> bool: + return _arg_optional + + +func has_argument() -> bool: + return _type != TYPE_NIL + + +func describe() -> String: + if help().is_empty(): + return " %-32s %s \n" % [commands(), description()] + return " %-32s %s \n %-32s %s\n" % [commands(), description(), "", help()] + + +func _to_string() -> String: + return describe() diff --git a/addons/gdUnit4/src/cmd/CmdOption.gd.uid b/addons/gdUnit4/src/cmd/CmdOption.gd.uid new file mode 100644 index 0000000..e4717b6 --- /dev/null +++ b/addons/gdUnit4/src/cmd/CmdOption.gd.uid @@ -0,0 +1 @@ +uid://cjlyjf7drcjtf diff --git a/addons/gdUnit4/src/cmd/CmdOptions.gd b/addons/gdUnit4/src/cmd/CmdOptions.gd new file mode 100644 index 0000000..c610529 --- /dev/null +++ b/addons/gdUnit4/src/cmd/CmdOptions.gd @@ -0,0 +1,31 @@ +class_name CmdOptions +extends RefCounted + + +var _default_options :Array[CmdOption] +var _advanced_options :Array[CmdOption] + + +func _init(p_options :Array[CmdOption] = [], p_advanced_options :Array[CmdOption] = []) -> void: + # default help options + _default_options = p_options + _advanced_options = p_advanced_options + + +func default_options() -> Array[CmdOption]: + return _default_options + + +func advanced_options() -> Array[CmdOption]: + return _advanced_options + + +func options() -> Array[CmdOption]: + return default_options() + advanced_options() + + +func get_option(cmd :String) -> CmdOption: + for option in options(): + if Array(option.commands()).has(cmd): + return option + return null diff --git a/addons/gdUnit4/src/cmd/CmdOptions.gd.uid b/addons/gdUnit4/src/cmd/CmdOptions.gd.uid new file mode 100644 index 0000000..6e0c2c7 --- /dev/null +++ b/addons/gdUnit4/src/cmd/CmdOptions.gd.uid @@ -0,0 +1 @@ +uid://bcrrg66r74ryx |
