« get me outta code hell

Merge branch 'fast-find-track' into preview - hsmusic-wiki - HSMusic - static wiki software cataloguing collaborative creation
about summary refs log tree commit diff
diff options
context:
space:
mode:
author(quasar) nebula <qznebula@protonmail.com>2023-11-05 19:13:09 -0400
committer(quasar) nebula <qznebula@protonmail.com>2023-11-05 19:13:09 -0400
commit3baba32aacebe09314fa7609817c7d3cc9e6ac7b (patch)
tree496cd9a6185205c8f7d251f0db284b14c00a92f6
parent1704fa016ba81318e92ef59951f4a9b3ffc4d091 (diff)
parent7841f97cde6182359bb3f2b0f6117c6379ef18dc (diff)
Merge branch 'fast-find-track' into preview
-rw-r--r--src/data/composite/things/track/withAlwaysReferenceByDirectory.js51
-rw-r--r--src/find.js29
2 files changed, 46 insertions, 34 deletions
diff --git a/src/data/composite/things/track/withAlwaysReferenceByDirectory.js b/src/data/composite/things/track/withAlwaysReferenceByDirectory.js
index d27f7b2..fac8e21 100644
--- a/src/data/composite/things/track/withAlwaysReferenceByDirectory.js
+++ b/src/data/composite/things/track/withAlwaysReferenceByDirectory.js
@@ -2,22 +2,15 @@
 // just to the track's name, which means you don't have to always reference
 // some *other* (much more commonly referenced) track by directory instead
 // of more naturally by name.
-//
-// See the implementation for an important caveat about matching the original
-// track against other tracks, which uses a custom implementation pulling (and
-// duplicating) details from #find instead of using withOriginalRelease and the
-// usual withResolvedReference / find.track() utilities.
-//
 
 import {input, templateCompositeFrom} from '#composite';
+import find from '#find';
 import {isBoolean} from '#validators';
 
 import {exitWithoutDependency, exposeUpdateValueOrContinue}
   from '#composite/control-flow';
 import {withPropertyFromObject} from '#composite/data';
-
-// TODO: Kludge. (The usage of this, not so much the import.)
-import CacheableObject from '../../../things/cacheable-object.js';
+import {withResolvedReference} from '#composite/wiki-data';
 
 export default templateCompositeFrom({
   annotation: `withAlwaysReferenceByDirectory`,
@@ -44,29 +37,23 @@ export default templateCompositeFrom({
       value: input.value(false),
     }),
 
-    // "Slow" / uncached, manual search from trackData (with this track
-    // excluded). Otherwise there end up being pretty bad recursion issues
-    // (track1.alwaysReferencedByDirectory depends on searching through data
-    // including track2, which depends on evaluating track2.alwaysReferenced-
-    // ByDirectory, which depends on searcing through data including track1...)
-    // That said, this is 100% a kludge, since it involves duplicating find
-    // logic on a completely unrelated context.
-    {
-      dependencies: [input.myself(), 'trackData', 'originalReleaseTrack'],
-      compute: (continuation, {
-        [input.myself()]: thisTrack,
-        ['trackData']: trackData,
-        ['originalReleaseTrack']: ref,
-      }) => continuation({
-        ['#originalRelease']:
-          (ref.startsWith('track:')
-            ? trackData.find(track => track.directory === ref.slice('track:'.length))
-            : trackData.find(track =>
-                track !== thisTrack &&
-                !CacheableObject.getUpdateValue(track, 'originalReleaseTrack') &&
-                track.name.toLowerCase() === ref.toLowerCase())),
-      })
-    },
+    // It's necessary to use the custom trackOriginalReleasesOnly find function
+    // here, so as to avoid recursion issues - the find.track() function depends
+    // on accessing each track's alwaysReferenceByDirectory, which means it'll
+    // hit *this track* - and thus this step - and end up recursing infinitely.
+    // By definition, find.trackOriginalReleasesOnly excludes tracks which have
+    // an originalReleaseTrack update value set, which means even though it does
+    // still access each of tracks' `alwaysReferenceByDirectory` property, it
+    // won't access that of *this* track - it will never proceed past the
+    // `exitWithoutDependency` step directly above, so there's no opportunity
+    // for recursion.
+    withResolvedReference({
+      ref: 'originalReleaseTrack',
+      data: 'trackData',
+      find: input.value(find.trackOriginalReleasesOnly),
+    }).outputs({
+      '#resolvedReference': '#originalRelease',
+    }),
 
     exitWithoutDependency({
       dependency: '#originalRelease',
diff --git a/src/find.js b/src/find.js
index 8c9413b..dfcaa9a 100644
--- a/src/find.js
+++ b/src/find.js
@@ -2,6 +2,7 @@ import {inspect} from 'node:util';
 
 import {colors, logWarn} from '#cli';
 import {typeAppearance} from '#sugar';
+import {CacheableObject} from '#things';
 
 function warnOrThrow(mode, message) {
   if (mode === 'error') {
@@ -16,6 +17,8 @@ function warnOrThrow(mode, message) {
 }
 
 export function processAllAvailableMatches(data, {
+  include = thing => true,
+
   getMatchableNames = thing =>
     (Object.hasOwn(thing, 'name')
       ? [thing.name]
@@ -26,6 +29,10 @@ export function processAllAvailableMatches(data, {
   const multipleNameMatches = Object.create(null);
 
   for (const thing of data) {
+    if (!include(thing)) continue;
+
+    byDirectory[thing.directory] = thing;
+
     for (const name of getMatchableNames(thing)) {
       if (typeof name !== 'string') {
         logWarn`Unexpected ${typeAppearance(name)} returned in names for ${inspect(thing)}`;
@@ -33,6 +40,7 @@ export function processAllAvailableMatches(data, {
       }
 
       const normalizedName = name.toLowerCase();
+
       if (normalizedName in byName) {
         const alreadyMatchesByName = byName[normalizedName];
         byName[normalizedName] = null;
@@ -45,8 +53,6 @@ export function processAllAvailableMatches(data, {
         byName[normalizedName] = thing;
       }
     }
-
-    byDirectory[thing.directory] = thing;
   }
 
   return {byName, byDirectory, multipleNameMatches};
@@ -55,6 +61,7 @@ export function processAllAvailableMatches(data, {
 function findHelper({
   referenceTypes,
 
+  include = undefined,
   getMatchableNames = undefined,
 }) {
   const keyRefRegex =
@@ -84,6 +91,7 @@ function findHelper({
     if (!subcache) {
       subcache =
         processAllAvailableMatches(data, {
+          include,
           getMatchableNames,
         });
 
@@ -178,6 +186,22 @@ const find = {
         ? []
         : [track.name]),
   }),
+
+  trackOriginalReleasesOnly: findHelper({
+    referenceTypes: ['track'],
+
+    include: track =>
+      !CacheableObject.getUpdateValue(track, 'originalReleaseTrack'),
+
+    // It's still necessary to check alwaysReferenceByDirectory here, since it
+    // may be set manually (with the `Always Reference By Directory` field), and
+    // these shouldn't be matched by name (as per usual). See the definition for
+    // that property for more information.
+    getMatchableNames: track =>
+      (track.alwaysReferenceByDirectory
+        ? []
+        : [track.name]),
+  }),
 };
 
 export default find;
@@ -200,6 +224,7 @@ export function bindFind(wikiData, opts1) {
       newsEntry: 'newsData',
       staticPage: 'staticPageData',
       track: 'trackData',
+      trackOriginalReleasesOnly: 'trackData',
     }).map(([key, value]) => {
       const findFn = find[key];
       const thingData = wikiData[value];