From cacb5226bf9652124dbeee323514943788e66267 Mon Sep 17 00:00:00 2001 From: JeremyStarTM Date: Mon, 15 Apr 2024 21:52:27 +0200 Subject: [PATCH] Add stringify_variables method to misc.gd --- dist/submodules/besseretests | 2 +- src/classes/config.gd | 20 ++++++ src/misc.gd | 121 +++++++++++++++++++++++++++++++++++ tests/unit/misc.gd | 28 ++++++++ 4 files changed, 170 insertions(+), 1 deletion(-) diff --git a/dist/submodules/besseretests b/dist/submodules/besseretests index cdd4924..4cd4f12 160000 --- a/dist/submodules/besseretests +++ b/dist/submodules/besseretests @@ -1 +1 @@ -Subproject commit cdd49246684d446c0115bc5cfbca289eaa5fe354 +Subproject commit 4cd4f12f95f61a27d493c783e367008452745e8c diff --git a/src/classes/config.gd b/src/classes/config.gd index 1edfe7f..ea358bb 100644 --- a/src/classes/config.gd +++ b/src/classes/config.gd @@ -45,6 +45,18 @@ class_name CoreConfiguration @export var logui_background_color: Color ## What font size the graphical log should have. @export var logui_font_size: int + +@export_category("Miscellaneous") +## Shows or hides the type when calling [code]stringify_variables[/code]. +@export var misc_stringify_show_type: bool +## Determines how [code]stringify_variables[/code] should display [class Color] variables.[br] +## Will display colors from [code]0[/code] to [code]255[/code] if [code]true[/code] or from [code]-1.0[/code] to [code]1.0[/code] if [code]false[/code]. +@export var misc_stringify_color_range8: bool +## Determines if [class Array]s should be processed by [code]stringify_variables[/code]. +@export var misc_stringify_array: bool +## Determines if [class Dictionary]s should be processed by [code]stringify_variables[/code]. +@export var misc_stringify_dictionary: bool + @export_category("Easy Request Maker") ## Determines how unsecure requests should be handled. @export var erm_unsecure_requests: CoreTypes.BlockadeLevel @@ -63,9 +75,17 @@ func _init() -> void: logger_format = "%color%[%time%] [%level% %origin%] %message%" logger_newlines_override = true logger_newlines_sizelimit = 40 + # Log UI logui_enabled = true logui_background_color = Color.BLACK # To disable the background, use Color.TRANSPARENT logui_font_size = 14 + + # Misc + misc_stringify_show_type = true + misc_stringify_color_range8 = true + misc_stringify_array = true + misc_stringify_dictionary = true + # Easy Request Maker erm_unsecure_requests = CoreTypes.BlockadeLevel.BLOCK diff --git a/src/misc.gd b/src/misc.gd index dc9e120..b14988b 100644 --- a/src/misc.gd +++ b/src/misc.gd @@ -21,6 +21,19 @@ ## and generally make your life as a developer easier. extends CoreBaseModule +# Configuration +var config_stringify_show_type: bool +var config_stringify_color_range8: bool +var config_stringify_array: bool +var config_stringify_dictionary: bool + +# +++ module +++ +func _pull_config() -> void: + config_stringify_show_type = core.config.misc_stringify_show_type + config_stringify_color_range8 = core.config.misc_stringify_color_range8 + config_stringify_array = core.config.misc_stringify_array + config_stringify_dictionary = core.config.misc_stringify_dictionary + # +++ data type conversion +++ ## Converts a number of bytes into mebibytes.[br] ## [br] @@ -125,6 +138,114 @@ func stringarray_to_array(array: Array[String]) -> Array: func get_center(parent_size: Vector2, child_size: Vector2) -> Vector2: return Vector2(parent_size.x/2-child_size.x/2, parent_size.y/2-child_size.y/2) +## Makes variables as look correct inside strings.[br] +## Short examples:[br] +## [code]true[/code] -> [code]'true'[/code][br] +## [code]Vector2(69.064, PI)[/code] -> [code]'x=69.064 y=3.14159265358979'[/code][br] +## [code]"This is a test string"[/code] -> [code]'"This is a test string"'[/code][br] +## Full example:[br] +## [codeblock] +## Code: +## logger.diag(stringify_variables("Triggered %trigger% (pos=%position%, successful=%success%)", { "trigger": "shoot", "position": Vector2(5156.149, 581.69), "success": true })) +## +## Output: +## [16:44:35] [DIAG Test.gd] Triggered '"shoot"' (pos='x=5156.149 y=581.69', successful='true') +## [/codeblock] +func stringify_variables(template: String, variables: Dictionary, no_quotes: bool = false) -> String: + # To decrease allocations + var value + var type: String = "" + var replacement: String = "" + for placeholder in variables: + # Check key type + if typeof(placeholder) != TYPE_STRING: + logger.error("Invalid placeholder type '\"" + type_string(typeof(placeholder)) + "\", skipping") + continue + + # Check for correct type + value = variables[placeholder] + + match(typeof(value)): + # Primitives + Variant.Type.TYPE_NIL: replacement = "null" + Variant.Type.TYPE_BOOL: replacement = str(value) + Variant.Type.TYPE_INT: replacement = str(value) + Variant.Type.TYPE_FLOAT: replacement = str(value) + Variant.Type.TYPE_STRING: replacement = "\"" + value + "\"" + Variant.Type.TYPE_STRING_NAME: replacement = "\"" + value + "\"" + # Non-primitives + Variant.Type.TYPE_OBJECT: replacement = str(value) + Variant.Type.TYPE_COLOR: + if config_stringify_color_range8: replacement = "r=" + _sa(value.r8) + " g=" + _sa(value.g8) + " b=" + _sa(value.b8) + " a=" + _sa(value.a8) + else: replacement = "r=" + _sa(value.r) + " g=" + _sa(value.g) + " b=" + _sa(value.b) + " a=" + _sa(value.a) + Variant.Type.TYPE_RID: replacement = "id=" + _sa(value.get_id()) + " valid=" + _sa(value.is_valid()) + Variant.Type.TYPE_ARRAY: + if config_stringify_array: + if value.size() == 0: + replacement = "[]" + else: + replacement = "[ " + for item in value: + if replacement == "[ ": replacement += _sa(item) + else: replacement += ", " + _sa(item) + replacement += " ]" + else: replacement = str(value) + Variant.Type.TYPE_DICTIONARY: + if config_stringify_dictionary: + if value.size() == 0: replacement = "{}" + else: + replacement = "{ " + for key in value: + if replacement == "{ ": replacement += _sa(key) + ": " + _sa(value[key]) + else: replacement += ", " + _sa(key) + replacement += " }" + else: replacement = str(value) + # TODO: Packed Arrays + # Nodes & scripting + Variant.Type.TYPE_NODE_PATH: replacement = str(value) + Variant.Type.TYPE_CALLABLE: replacement = "valid=" + _sa(value.is_valid()) + " standard=" + _sa(value.is_standard()) + " object=" + _sa(value.get_object() ) + " method=" + value.get_method() + " args=" + _sa(value.get_bound_arguments()) + Variant.Type.TYPE_SIGNAL: replacement = "name=" + _sa(value.get_name()) + " object=" + _sa(value.get_object()) + # 2D + Variant.Type.TYPE_VECTOR2: replacement = "x=" + _sa(value.x) + " y=" + _sa(value.y) + Variant.Type.TYPE_VECTOR2I: replacement = "x=" + _sa(value.x) + " y=" + _sa(value.y) + Variant.Type.TYPE_RECT2: replacement = "size=" + _sa(value.size) + " pos=" + _sa(value.position) + " end=" + _sa(value.end) + Variant.Type.TYPE_RECT2I: replacement = "size=" + _sa(value.size) + " pos=" + _sa(value.position) + " end=" + _sa(value.end) + Variant.Type.TYPE_TRANSFORM2D: replacement = "x=" + _sa(value.x) + " y=" + _sa(value.y) + " origin=" + _sa(value.origin) + # 3D + Variant.Type.TYPE_VECTOR3: replacement = "x=" + _sa(value.x) + " y=" + _sa(value.y) + " z=" + _sa(value.z) + Variant.Type.TYPE_VECTOR3I: replacement = "x=" + _sa(value.x) + " y=" + _sa(value.y) + " z=" + _sa(value.z) + Variant.Type.TYPE_PLANE: replacement = "x=" + _sa(value.x) + " y=" + _sa(value.y) + " z=" + _sa(value.z) + " d=" + _sa(value.d) + " normal=" + _sa(value.normal) + Variant.Type.TYPE_QUATERNION: replacement = "x=" + _sa(value.x) + " y=" + _sa(value.y) + " z=" + _sa(value.z) + " w=" + _sa(value.w) + Variant.Type.TYPE_AABB: replacement = "size=" + _sa(value.size) + " pos=" + _sa(value.position) + " end=" + _sa(value.end) + Variant.Type.TYPE_TRANSFORM3D: replacement = "basis=" + _sa(value.basis) + " origin=" + _sa(value.origin) + Variant.Type.TYPE_BASIS: replacement = "x=" + _sa(value.x) + " y=" + _sa(value.y) + " z=" + _sa(value.z) + Variant.Type.TYPE_PROJECTION: replacement = "x=" + _sa(value.x) + " y=" + _sa(value.y) + " z=" + _sa(value.z) + " w=" + _sa(value.w) + # 4D + Variant.Type.TYPE_VECTOR4: replacement = "x=" + _sa(value.x) + " y=" + _sa(value.y) + " z=" + _sa(value.z) + " w=" + _sa(value.w) + Variant.Type.TYPE_VECTOR4I: replacement = "x=" + _sa(value.x) + " y=" + _sa(value.y) + " z=" + _sa(value.z) + " w=" + _sa(value.w) + # etc + Variant.Type.TYPE_MAX: replacement = str(value) + _: + replacement = str(value) + type = "(unknown) " + + # Replace + if config_stringify_show_type and type == "": + match(typeof(value)): + Variant.Type.TYPE_NIL: type = "" + Variant.Type.TYPE_MAX: type = "(invalid) " + Variant.Type.TYPE_ARRAY: + if value.get_typed_builtin() != TYPE_NIL: + type = "(" + type_string(typeof(value)).to_lower() + "[" + type_string(typeof(value.get_typed_builtin())).to_lower() + "]) " + _: type = "(" + type_string(typeof(value)).to_lower() + ") " # something is causing almost everything to be displayed as a boolean, which is not true. i don't know what's the problem here however. + var quote: String = "'" if !no_quotes else "" + template = template.replace("%" + placeholder + "%", quote + type + replacement + quote) + return template + +# Makes calls shorter +func _sa(value) -> String: + return stringify_variables("%var%", { "var": value }, true) + ## Moved to [method Core.quit_safely]. ## @deprecated func quit_safely(exitcode: int = 0) -> void: await core.quit_safely(exitcode) diff --git a/tests/unit/misc.gd b/tests/unit/misc.gd index 0a93382..73a682f 100644 --- a/tests/unit/misc.gd +++ b/tests/unit/misc.gd @@ -156,3 +156,31 @@ func test_get_center() -> void: return rok() + +# stringify_variables +func test_stringify_variables() -> void: + rskip("Unfinished") + return + # Init CORE + await load_framework() + + # Variables + var test_in_string: String = "[b]null[/b]=%null%\n[b]bool[/b]=%bool%\n[b]int[/b]=%int%\n[b]float[/b]=%float%\n[b]string[/b]=%string%\n[b]stringname[/b]=%stringname%\n[b]color[/b]=%color%\n[b]rid[/b]=%rid%\n[b]array[/b]=%array%\n[b]dict[/b]=%dictionary%\n[b]nodepath[/b]=%nodepath%\n[b]call[/b]=%callable%\n[b]signal[/b]=%signal%\n[b]vec2[/b]=%vector2%\n[b]vec2i[/b]=%vector2i%\n[b]rect2[/b]=%rect2%\n[b]rect2i[/b]=%rect2i%\n[b]trans2d[/b]=%transform2d%\n[b]vec3[/b]=%vector3%\n[b]vec3i[/b]=%vector3i%\n[b]plane[/b]=%plane%\n[b]quarternion[/b]=%quaternion%\n[b]aabb[/b]=%aabb%\n[b]trans3d[/b]=%transform3d%\n[b]basis[/b]=%basis%\n[b]projection[/b]=%projection%\n[b]vec4[/b]=%vector4%\n[b]vec4i[/b]=%vector4i%" + var test_in_args: Dictionary = { "null": null, "bool": true, "int": 505, "float": 505.69, "string": "some string", "stringname": "some string name", "color": Color("#d60532"), "rid": RenderingServer.get_white_texture(), "array": [ "item1", "item2", true, 4 ], "dictionary": { "key0": true, 1: "key2" }, "nodepath": NodePath("/root/CORE"), "callable": func() -> void: pass, "signal": core.logger.log_event, "vector2": Vector2(PI, TAU), "vector2i": Vector2i(505, 69), "rect2": Rect2(1.51, 2.56, 3.89, 4.11), "rect2i": Rect2i(1, 2, 3, 4), "transform2d": Transform2D(1.5, Vector2(2.1, 2.2), 3.6, Vector2(4.1, 4.2)), "vector3": Vector3(59.666, NAN, INF), "vector3i": Vector3i(505, 69, 1713208182), "plane": Plane(Vector3(1.5, 2.5, 3.5), 55.7777), "quaternion": Quaternion(1.55, 2.7812, 3.671, 4.8871), "aabb": AABB(Vector3(1.1, 1.2, 1.3), Vector3(2.1, 2.2, 2.3)), "transform3d": Transform3D(Basis(Vector3(1.11, 1.12, 1.13), Vector3(1.21, 1.22, 1.23), Vector3(1.31, 1.32, 1.33)), Vector3(2.1, 2.2, 2.3)), "basis": Basis(Vector3(1.1358, 1.268718, 1.35818), Vector3(2.1481, 2.258819, 2.3718), Vector3(3.1658581, 3.2581587, 3.357158)), "projection": Projection(Transform3D(Basis(Vector3(1.11, 1.12, 1.13), Vector3(1.21, 1.22, 1.23), Vector3(1.31, 1.32, 1.33)), Vector3(2.1, 2.2, 2.3))), "vector4": Vector4(1.55158, 2.517571, 3.58157, 4.51857185), "vector4i": Vector4i(1, 2, 3, 4) } + var test_out: String = "unfinished" + var test_result: String = core.misc.stringify_variables(test_in_string, test_in_args) + + if test_result != test_out: + lerror("Got invalid test result:") + lerror("in (string) -> " + test_in_string.replace("\n", "\\n")) + await get_tree().create_timer(1).timeout # output overflow workaround + lerror("in (args) -> " + str(test_in_args)) + await get_tree().create_timer(1).timeout # output overflow workaround + lerror("expected -> " + test_out.replace("\n", "\\n")) + await get_tree().create_timer(1).timeout # output overflow workaround + lerror("got -> " + test_result.replace("\n", "\\n")) + await get_tree().create_timer(1).timeout # output overflow workaround + rerror("Test did not return the right value (check logs)") + return + + rok()