From c2a959f05a2969117074cdef14f3e35a3ff32e20 Mon Sep 17 00:00:00 2001
From: Kir_Antipov <kp.antipov@gmail.com>
Date: Fri, 17 Feb 2023 14:55:51 +0000
Subject: [PATCH] Made a few path parsers for Action parameters

---
 .../actions/action-parameter-path-parser.ts   | 121 ++++++++++++++++++
 1 file changed, 121 insertions(+)
 create mode 100644 src/utils/actions/action-parameter-path-parser.ts

diff --git a/src/utils/actions/action-parameter-path-parser.ts b/src/utils/actions/action-parameter-path-parser.ts
new file mode 100644
index 0000000..53fa4cc
--- /dev/null
+++ b/src/utils/actions/action-parameter-path-parser.ts
@@ -0,0 +1,121 @@
+import { $i } from "@/utils/collections";
+import { capitalize } from "@/utils/string-utils";
+import { ActionMetadata } from "./action-metadata";
+import { ActionParameter } from "./action-parameter";
+
+/**
+ * Converts a parameter name to an array of property names that identify its location.
+ */
+export interface ActionParameterPathParser {
+    /**
+     * Converts a parameter name to an array of property names that identify its location.
+     *
+     * @param name - The name of the parameter.
+     * @param parameter - The input or output parameter for which to generate the path, if any.
+     * @param metadata - The action metadata object containing the parameter, if any.
+     *
+     * @returns An array of property names that identify the location of the parameter.
+     */
+    (name: string, parameter?: ActionParameter, metadata?: ActionMetadata): string[];
+}
+
+/**
+ * Returns the parameter name as a single-element array, representing the identity path of the parameter.
+ *
+ * @param name - The name of the parameter.
+ *
+ * @returns An array containing a single element, which is the name of the parameter.
+ */
+export const IDENTITY_ACTION_PARAMETER_PATH_PARSER: ActionParameterPathParser = name => [name];
+
+/**
+ * Splits the parameter name by non-letter and non-number characters, converts each word to lowercase,
+ * and returns an array of property names that identify the location of the parameter.
+ *
+ * @param name - The name of the parameter.
+ *
+ * @returns An array of property names that identify the location of the parameter.
+ */
+export const SPLIT_BY_WORDS_ACTION_PARAMETER_PATH_PARSER: ActionParameterPathParser = name =>
+    (name || "").split(/[^\p{L}\p{N}]/u).map(x => x.toLowerCase());
+
+/**
+ * Splits the parameter name by non-letter and non-number characters, converts each word to lowercase,
+ * groups the parameter based on the input/output group specified in the metadata object, and
+ * returns an array of property names that identify the location of the parameter.
+ *
+ * @param name - The name of the parameter.
+ * @param parameter - The input or output parameter for which to generate the path, if any.
+ * @param metadata - The action metadata object containing the parameter, if any.
+ *
+ * @returns An array of property names that identify the location of the parameter.
+ *
+ * @remarks
+ *
+ * For example, given the following set of parameter names:
+ * ```
+ * [
+ *   "bar-baz",
+ *   "foo-qux",
+ *   "foo-qux-waldo",
+ * ]
+ * ```
+ * And groups:
+ * ```
+ * [
+ *   "foo",
+ * ]
+ * ```
+ *
+ * The output would be:
+ * ```
+ * [
+ *   ["barBaz"],
+ *   ["foo", "qux"],
+ *   ["foo", "quxWaldo"],
+ * ]
+ * ```
+ */
+export const SPLIT_BY_WORDS_AND_GROUP_ACTION_PARAMETER_PATH_PARSER: ActionParameterPathParser = (name, parameter, metadata) => {
+    const path = SPLIT_BY_WORDS_ACTION_PARAMETER_PATH_PARSER(name, parameter, metadata);
+    if (!parameter || !metadata) {
+        return path;
+    }
+
+    const groups = metadata.inputs?.[name] === parameter ? metadata.groups?.input : metadata.outputs?.[name] === parameter ? metadata.groups?.output : undefined;
+    const groupNames = groups ? Object.keys(groups) : [];
+    const parameterGroup = $i(groupNames)
+        .map(x => SPLIT_BY_WORDS_ACTION_PARAMETER_PATH_PARSER(x, parameter, metadata))
+        .filter(x => $i(path).startsWith(x))
+        .max((a, b) => a.length - b.length);
+
+    const maxPathLength = (parameterGroup?.length || 0) + 1;
+    const flattenedPath = flattenPath(path, maxPathLength);
+    return flattenedPath;
+};
+
+/**
+ * Flattens the path array by merging consecutive elements that represent a single property name.
+ *
+ * @param path - An array of property names to be flattened.
+ * @param maxPathLength - The maximum length of the flattened path.
+ *
+ * @returns The flattened path array.
+ *
+ * @remarks
+ *
+ * This method changes the array in place.
+ */
+function flattenPath(path: string[], maxPathLength?: number): string[] {
+    // `maxPathLength` cannot be less then `1`, because we cannot fold a path any further than that.
+    // Also, we can handle `NaN`, `undefined`, and `null` this way.
+    if (!(maxPathLength >= 1)) {
+        maxPathLength = 1;
+    }
+
+    while (path.length > maxPathLength) {
+        path[path.length - 2] += capitalize(path[path.length - 1]);
+        path.splice(path.length - 1);
+    }
+    return path;
+}