« get me outta code hell

hsmusic-wiki - HSMusic - static wiki software cataloguing collaborative creation
about summary refs log tree commit diff
path: root/src/find-reverse.js
diff options
context:
space:
mode:
Diffstat (limited to 'src/find-reverse.js')
-rw-r--r--src/find-reverse.js137
1 files changed, 137 insertions, 0 deletions
diff --git a/src/find-reverse.js b/src/find-reverse.js
new file mode 100644
index 00000000..f31d3c45
--- /dev/null
+++ b/src/find-reverse.js
@@ -0,0 +1,137 @@
+// Helpers common to #find and #reverse logic.
+
+import thingConstructors from '#things';
+
+export function getAllSpecs({
+  word,
+  constructorKey,
+
+  hardcodedSpecs,
+  postprocessSpec,
+}) {
+  try {
+    thingConstructors;
+  } catch (error) {
+    throw new Error(`Thing constructors aren't ready yet, can't get all ${word} specs`);
+  }
+
+  const specs = {...hardcodedSpecs};
+
+  for (const thingConstructor of Object.values(thingConstructors)) {
+    const thingSpecs = thingConstructor[constructorKey];
+    if (!thingSpecs) continue;
+
+    for (const [key, spec] of Object.entries(thingSpecs)) {
+      specs[key] =
+        postprocessSpec(spec, {
+          thingConstructor,
+        });
+    }
+  }
+
+  return specs;
+}
+
+export function findSpec(key, {
+  word,
+  constructorKey,
+
+  hardcodedSpecs,
+  postprocessSpec,
+}) {
+  if (Object.hasOwn(hardcodedSpecs, key)) {
+    return hardcodedSpecs[key];
+  }
+
+  try {
+    thingConstructors;
+  } catch (error) {
+    throw new Error(`Thing constructors aren't ready yet, can't check if "${word}.${key}" available`);
+  }
+
+  for (const thingConstructor of Object.values(thingConstructors)) {
+    const thingSpecs = thingConstructor[constructorKey];
+    if (!thingSpecs) continue;
+
+    if (Object.hasOwn(thingSpecs, key)) {
+      return postprocessSpec(thingSpecs[key], {
+        thingConstructor,
+      });
+    }
+  }
+
+  throw new Error(`"${word}.${key}" isn't available`);
+}
+
+export function tokenProxy({
+  findSpec,
+  prepareBehavior,
+
+  handle: customHandle =
+    (_key) => undefined,
+}) {
+  return new Proxy({}, {
+    get: (store, key) => {
+      const custom = customHandle(key);
+      if (custom !== undefined) {
+        return custom;
+      }
+
+      if (!Object.hasOwn(store, key)) {
+        let behavior = (...args) => {
+          // This will error if the spec isn't available...
+          const spec = findSpec(key);
+
+          // ...or, if it is available, replace this function with the
+          // ready-for-use find function made out of that spec.
+          return (behavior = prepareBehavior(spec))(...args);
+        };
+
+        store[key] = (...args) => behavior(...args);
+        store[key][tokenKey] = key;
+      }
+
+      return store[key];
+    },
+  });
+}
+
+export function bind(wikiData, opts1, {
+  getAllSpecs,
+  prepareBehavior,
+}) {
+  const specs = getAllSpecs();
+
+  const bound = {};
+
+  for (const [key, spec] of Object.entries(specs)) {
+    if (!spec.bindTo) continue;
+
+    const behavior = prepareBehavior(spec);
+
+    const data =
+      (spec.bindTo === 'wikiData'
+        ? wikiData
+        : wikiData[spec.bindTo]);
+
+    bound[key] =
+      (opts1
+        ? (ref, opts2) =>
+            (opts2
+              ? behavior(ref, data, {...opts1, ...opts2})
+              : behavior(ref, data, opts1))
+        : (ref, opts2) =>
+            (opts2
+              ? behavior(ref, data, opts2)
+              : behavior(ref, data)));
+
+    bound[key][boundData] = data;
+    bound[key][boundOptions] = opts1 ?? {};
+  }
+
+  return bound;
+}
+
+export const tokenKey = Symbol.for('find.tokenKey');
+export const boundData = Symbol.for('find.boundData');
+export const boundOptions = Symbol.for('find.boundOptions');