From 4ab8d82727fec17be63f3204eab6096f145d3a9c Mon Sep 17 00:00:00 2001 From: Jesse Bannon Date: Tue, 19 Dec 2023 00:50:19 -0800 Subject: [PATCH] [BACKEND] Recreate regex plugin using functions --- .../entries/script/function_scripts.py | 31 +++++++++++++++++++ .../script/functions/array_functions.py | 22 +++++++++++++ .../script/functions/error_functions.py | 12 +++++++ .../script/functions/regex_functions.py | 8 +++++ tests/e2e/plugins/test_regex.py | 15 +++++++++ 5 files changed, 88 insertions(+) diff --git a/src/ytdl_sub/entries/script/function_scripts.py b/src/ytdl_sub/entries/script/function_scripts.py index 85622187..8d0702c1 100644 --- a/src/ytdl_sub/entries/script/function_scripts.py +++ b/src/ytdl_sub/entries/script/function_scripts.py @@ -6,6 +6,8 @@ from ytdl_sub.entries.script.variable_definitions import VariableDefinitions v: VariableDefinitions = VARIABLES CUSTOM_FUNCTION_SCRIPTS: Dict[str, str] = { + ############################################################################################# + # SIBLING GETTER "%extract_field_from_metadata_array_getter": """{ %map_get( %map(%array_at($0, 0)), %array_at($0, 1) ) }""", @@ -34,4 +36,33 @@ CUSTOM_FUNCTION_SCRIPTS: Dict[str, str] = { [] ) }}""", + ############################################################################################# + # REGEX PLUGIN + # $0 - input variable + # $1 - regex array + # $2 - defaults + "%regex_capture": """{ + %assert_then( + %array_reduce( + %array_apply_fixed( + %array_apply( + $1, + %regex_capture_groups + ), + %array_size($2), + %eq + ), + %and + ), + %array_first( + %array_apply_fixed( + %array($1), + %string($0), + %regex_search + ), + %array_extend( ['dummy'], $2 ) + ), + 'Number of regex capture groups must be the same for every input regex' + ) + }""", } diff --git a/src/ytdl_sub/script/functions/array_functions.py b/src/ytdl_sub/script/functions/array_functions.py index ffff0da7..1e6a6ca8 100644 --- a/src/ytdl_sub/script/functions/array_functions.py +++ b/src/ytdl_sub/script/functions/array_functions.py @@ -57,6 +57,18 @@ class ArrayFunctions: """ return array.value[idx.value] + @staticmethod + def array_first(array: Array, fallback: AnyArgument) -> AnyArgument: + """ + Returns the first element whose boolean conversion is True. Returns fallback + if all elements evaluate to False. + """ + for val in array.value: + if bool(val.value): + return val + + return fallback + @staticmethod def array_contains(array: Array, value: AnyArgument) -> Boolean: """ @@ -130,6 +142,16 @@ class ArrayFunctions: """ return Array([Array([val]) for val in array.value]) + @staticmethod + def array_apply_fixed( + array: Array, fixed_argument: AnyArgument, lambda2_function: LambdaTwo + ) -> Array: + """ + Apply a lambda function on every element in the Array, with ``fixed_argument`` + passed as a second argument to every invocation. + """ + return Array([Array([val, fixed_argument]) for val in array.value]) + @staticmethod def array_enumerate(array: Array, lambda_function: LambdaTwo) -> Array: """ diff --git a/src/ytdl_sub/script/functions/error_functions.py b/src/ytdl_sub/script/functions/error_functions.py index 7eb1420f..a5132bda 100644 --- a/src/ytdl_sub/script/functions/error_functions.py +++ b/src/ytdl_sub/script/functions/error_functions.py @@ -21,3 +21,15 @@ class ErrorFunctions: if not bool(value.value): raise UserThrownRuntimeError(assert_message) return value + + @staticmethod + def assert_then( + value: AnyArgument, ret: ReturnableArgument, assert_message: String + ) -> ReturnableArgument: + """ + Explicitly throw an error with the provided assert message if ``value`` evaluates to False. + If it evaluates to True, it will return ``ret``. + """ + if not bool(value.value): + raise UserThrownRuntimeError(assert_message) + return ret diff --git a/src/ytdl_sub/script/functions/regex_functions.py b/src/ytdl_sub/script/functions/regex_functions.py index 2abca1ec..e38b3139 100644 --- a/src/ytdl_sub/script/functions/regex_functions.py +++ b/src/ytdl_sub/script/functions/regex_functions.py @@ -3,6 +3,7 @@ from typing import AnyStr from typing import Match from ytdl_sub.script.types.array import Array +from ytdl_sub.script.types.resolvable import Integer from ytdl_sub.script.types.resolvable import String @@ -40,3 +41,10 @@ class RegexFunctions: group as a subsequent element in the Array. """ return _re_output_to_array(re.fullmatch(regex.value, string.value)) + + @staticmethod + def regex_capture_groups(regex: String) -> Integer: + """ + Returns number of capture groups in regex + """ + return Integer(re.compile(regex.value).groups) diff --git a/tests/e2e/plugins/test_regex.py b/tests/e2e/plugins/test_regex.py index 58abb325..97a5273f 100644 --- a/tests/e2e/plugins/test_regex.py +++ b/tests/e2e/plugins/test_regex.py @@ -76,6 +76,21 @@ def regex_subscription_dict(regex_subscription_dict_base, output_directory): } }, "overrides": { + "title_capture_list": f"""{{ + %regex_capture( + title, + [ + "should not cap (.+) - (.+)", + ".*\\[(.+) - (Feb.+)]" + ], + [ + "ack", + "ack" + ] + ) + }}""", + "title_capture_list_1": "{%array_at(title_capture_list, 1)}", + "title_capture_list_2": "{%array_at(title_capture_list, 2)}", "contains_regex_default": "contains {title_type}", "contains_regex_sanitized_default": "contains {title_type_sanitized}", },